From 426612af8405f416ebb49c2befd30af97b32df8d Mon Sep 17 00:00:00 2001 From: oliveromahony Date: Tue, 10 Dec 2024 15:15:55 +0000 Subject: [PATCH 1/2] Set the message identifiers as UUIDv7 (#943) * edited the message format for message id, fixed the install test --- .../collector/otel_collector_plugin_test.go | 13 +- internal/command/command_plugin.go | 5 +- internal/command/command_plugin_test.go | 7 +- internal/command/command_service.go | 10 +- internal/command/command_service_test.go | 7 +- internal/datasource/proto/message.go | 31 +++++ internal/datasource/proto/message_test.go | 128 ++++++++++++++++++ internal/file/file_manager_service.go | 9 +- internal/file/file_plugin.go | 6 +- internal/file/file_plugin_test.go | 4 +- internal/logger/logger.go | 4 +- internal/resource/resource_plugin.go | 4 +- .../watcher/file/file_watcher_service_test.go | 8 +- .../instance/nginx_config_parser_test.go | 7 +- internal/watcher/watcher_plugin_test.go | 6 +- pkg/uuid/uuid.go | 15 ++ test/helpers/os_utils.go | 15 ++ test/helpers/os_utils_test.go | 61 +++++++++ test/helpers/slog_utils.go | 48 +++++++ test/integration/install_uninstall_test.go | 8 +- .../grpc/mock_management_command_service.go | 7 +- .../mock/grpc/mock_management_file_service.go | 4 +- test/protos/data_plane_response.go | 10 +- 23 files changed, 353 insertions(+), 64 deletions(-) create mode 100644 internal/datasource/proto/message.go create mode 100644 internal/datasource/proto/message_test.go create mode 100644 test/helpers/os_utils_test.go create mode 100644 test/helpers/slog_utils.go diff --git a/internal/collector/otel_collector_plugin_test.go b/internal/collector/otel_collector_plugin_test.go index d686603f9b..cbe0c808a1 100644 --- a/internal/collector/otel_collector_plugin_test.go +++ b/internal/collector/otel_collector_plugin_test.go @@ -9,7 +9,6 @@ import ( "context" "errors" "fmt" - "strings" "testing" "github.com/nginx/agent/v3/test/protos" @@ -119,23 +118,13 @@ func TestCollector_Init(t *testing.T) { initError := collector.Init(context.Background(), nil) require.NoError(t, initError) - validateLog(t, tt.expectedLog, logBuf) + helpers.ValidateLog(t, tt.expectedLog, logBuf) require.NoError(t, collector.Close(context.TODO())) }) } } -func validateLog(t *testing.T, expectedLog string, logBuf *bytes.Buffer) { - t.Helper() - - if expectedLog != "" { - if !strings.Contains(logBuf.String(), expectedLog) { - t.Errorf("Expected log to contain %q, but got %q", expectedLog, logBuf.String()) - } - } -} - func TestCollector_InitAndClose(t *testing.T) { conf := types.OTelConfig(t) conf.Collector.Log.Path = "" diff --git a/internal/command/command_plugin.go b/internal/command/command_plugin.go index be66f8823d..c0ab746608 100644 --- a/internal/command/command_plugin.go +++ b/internal/command/command_plugin.go @@ -11,11 +11,10 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" - "github.com/google/uuid" - mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" "github.com/nginx/agent/v3/internal/bus" "github.com/nginx/agent/v3/internal/config" + "github.com/nginx/agent/v3/internal/datasource/proto" "github.com/nginx/agent/v3/internal/grpc" "github.com/nginx/agent/v3/internal/logger" pkgConfig "github.com/nginx/agent/v3/pkg/config" @@ -249,7 +248,7 @@ func (cp *CommandPlugin) createDataPlaneResponse(correlationID string, status mp ) *mpi.DataPlaneResponse { return &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, diff --git a/internal/command/command_plugin_test.go b/internal/command/command_plugin_test.go index 0481232f3f..5b46582fde 100644 --- a/internal/command/command_plugin_test.go +++ b/internal/command/command_plugin_test.go @@ -8,7 +8,6 @@ package command import ( "bytes" "context" - "strings" "testing" "time" @@ -18,6 +17,7 @@ import ( "github.com/nginx/agent/v3/internal/bus" "github.com/nginx/agent/v3/internal/command/commandfakes" "github.com/nginx/agent/v3/internal/grpc/grpcfakes" + "github.com/nginx/agent/v3/test/helpers" "github.com/nginx/agent/v3/test/protos" "github.com/nginx/agent/v3/test/stub" "github.com/nginx/agent/v3/test/types" @@ -211,10 +211,7 @@ func TestMonitorSubscribeChannel(t *testing.T) { time.Sleep(100 * time.Millisecond) - // Verify the logger was called - if s := logBuf.String(); !strings.Contains(s, "Received management plane request") { - t.Errorf("Unexpected log %s", s) - } + helpers.ValidateLog(t, "Received management plane request", logBuf) // Clear the log buffer logBuf.Reset() diff --git a/internal/command/command_service.go b/internal/command/command_service.go index 9d24c245d1..5c991e76d2 100644 --- a/internal/command/command_service.go +++ b/internal/command/command_service.go @@ -14,11 +14,13 @@ import ( "time" "github.com/cenkalti/backoff/v4" - "github.com/google/uuid" + mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" "github.com/nginx/agent/v3/internal/config" + "github.com/nginx/agent/v3/internal/datasource/proto" "github.com/nginx/agent/v3/internal/grpc" "github.com/nginx/agent/v3/internal/logger" + "google.golang.org/protobuf/types/known/timestamppb" backoffHelpers "github.com/nginx/agent/v3/internal/backoff" @@ -86,7 +88,7 @@ func (cs *CommandService) UpdateDataPlaneStatus( request := &mpi.UpdateDataPlaneStatusRequest{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, @@ -136,7 +138,7 @@ func (cs *CommandService) UpdateDataPlaneHealth(ctx context.Context, instanceHea request := &mpi.UpdateDataPlaneHealthRequest{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, @@ -214,7 +216,7 @@ func (cs *CommandService) CreateConnection( request := &mpi.CreateConnectionRequest{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, diff --git a/internal/command/command_service_test.go b/internal/command/command_service_test.go index ad8c6f790c..9534414dbd 100644 --- a/internal/command/command_service_test.go +++ b/internal/command/command_service_test.go @@ -10,7 +10,6 @@ import ( "context" "errors" "log/slog" - "strings" "testing" "time" @@ -122,9 +121,9 @@ func TestCommandService_UpdateDataPlaneStatusSubscribeError(t *testing.T) { err := commandService.UpdateDataPlaneStatus(ctx, protos.GetHostResource()) require.Error(t, err) - if s := logBuf.String(); !strings.Contains(s, "Failed to send update data plane status") { - t.Errorf("Unexpected log %s", s) - } + helpers.ValidateLog(t, "Failed to send update data plane status", logBuf) + + logBuf.Reset() } func TestCommandService_CreateConnection(t *testing.T) { diff --git a/internal/datasource/proto/message.go b/internal/datasource/proto/message.go new file mode 100644 index 0000000000..a2d547ca01 --- /dev/null +++ b/internal/datasource/proto/message.go @@ -0,0 +1,31 @@ +// 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 proto + +import ( + "log/slog" + "time" + + "github.com/google/uuid" + agentUuid "github.com/nginx/agent/v3/pkg/uuid" +) + +// UUIDGenerator defines a function type for generating UUIDs. +type UUIDGenerator func() (uuid.UUID, error) + +// DefaultUUIDGenerator is the production implementation for generating UUIDv7. +var defaultUUIDGenerator UUIDGenerator = uuid.NewUUID + +// GenerateMessageID generates a unique message ID, falling back to sha256 and timestamp if UUID generation fails. +func GenerateMessageID() string { + uuidv7, err := defaultUUIDGenerator() + if err != nil { + slog.Debug("Issue generating uuidv7, using sha256 and timestamp instead", "error", err) + return agentUuid.Generate("%s", time.Now().String()) + } + + return uuidv7.String() +} diff --git a/internal/datasource/proto/message_test.go b/internal/datasource/proto/message_test.go new file mode 100644 index 0000000000..10418d44b4 --- /dev/null +++ b/internal/datasource/proto/message_test.go @@ -0,0 +1,128 @@ +// 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 proto + +import ( + "bytes" + "errors" + "regexp" + "testing" + + "github.com/nginx/agent/v3/test/helpers" + "github.com/nginx/agent/v3/test/stub" + "github.com/stretchr/testify/assert" + + "github.com/google/uuid" +) + +func TestUUIDv7Regex(t *testing.T) { + // Define the UUIDv7 regex + uuidv7Regex := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`) + + // Define test cases + tests := []struct { + name string + input string + expectMatch bool + }{ + { + name: "Valid UUIDv7 with variant 8", + input: "01876395-3f9d-7c91-89a3-4f57e53a1a4b", + expectMatch: true, + }, + { + name: "Valid UUIDv7 with variant a", + input: "01876395-3f9d-7c91-9f00-4f57e53a1a4b", + expectMatch: true, + }, + { + name: "Invalid UUIDv7 - wrong version", + input: "01876395-3f9d-6c91-89a3-4f57e53a1a4b", + expectMatch: false, + }, + { + name: "Invalid UUIDv7 - wrong variant", + input: "01876395-3f9d-7c91-7a00-4f57e53a1a4b", + expectMatch: false, + }, + { + name: "Invalid UUIDv7 - extra characters", + input: "01876395-3f9d-7c91-89a3-4f57e53a1a4b123", + expectMatch: false, + }, + { + name: "Invalid UUIDv7 - missing characters", + input: "01876395-3f9d-7c91-89a3-4f57e53a", + expectMatch: false, + }, + } + + // Iterate over test cases + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + match := uuidv7Regex.MatchString(tt.input) + assert.Equal(t, tt.expectMatch, match, "Regex match result did not match expectation") + }) + } +} + +func TestGenerateMessageID(t *testing.T) { + tests := []struct { + name string + mockFunc func() (uuid.UUID, error) + expected string + expectError bool + }{ + { + name: "Valid UUID generation", + mockFunc: func() (uuid.UUID, error) { + return uuid.UUID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, nil + }, + expected: "01020304-0506-0708-090a-0b0c0d0e0f10", + expectError: false, + }, + { + name: "Fallback UUID generation due to error", + mockFunc: func() (uuid.UUID, error) { + return uuid.Nil, errors.New("mock error") + }, + expected: "", // Fallback UUIDs don't follow a fixed prefix but should not be empty + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defaultUUIDGenerator = tt.mockFunc + + if tt.expectError { + logBuf := &bytes.Buffer{} + stub.StubLoggerWith(logBuf) + + got := GenerateMessageID() + assert.NotEmpty(t, got) + + // Inspect logs + helpers.ValidateLog(t, "Issue generating uuidv7, using sha256 and timestamp instead", logBuf) + + logBuf.Reset() + } else { + got := GenerateMessageID() + + assert.Equal(t, tt.expected, got, "Expected UUID string to match") + } + + // reset + defaultUUIDGenerator = uuid.NewUUID + }) + } + defaultUUIDGenerator = func() (uuid.UUID, error) { + return uuid.UUID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, nil + } + + got := GenerateMessageID() + assert.Equal(t, "01020304-0506-0708-090a-0b0c0d0e0f10", got, "Expected correct UUID string") +} diff --git a/internal/file/file_manager_service.go b/internal/file/file_manager_service.go index 149d0ce64f..cc55f9876e 100644 --- a/internal/file/file_manager_service.go +++ b/internal/file/file_manager_service.go @@ -14,10 +14,11 @@ import ( "sync" "sync/atomic" + "github.com/nginx/agent/v3/internal/datasource/proto" "github.com/nginx/agent/v3/internal/model" "github.com/cenkalti/backoff/v4" - "github.com/google/uuid" + mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" "github.com/nginx/agent/v3/internal/config" "github.com/nginx/agent/v3/internal/grpc" @@ -96,7 +97,7 @@ func (fms *FileManagerService) UpdateOverview( request := &mpi.UpdateOverviewRequest{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: requestCorrelationID.Value.String(), Timestamp: timestamppb.Now(), }, @@ -168,7 +169,7 @@ func (fms *FileManagerService) UpdateFile( Contents: contents, }, MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, @@ -325,7 +326,7 @@ func (fms *FileManagerService) fileUpdate(ctx context.Context, file *mpi.File) e getFile := func() (*mpi.GetFileResponse, error) { return fms.fileServiceClient.GetFile(ctx, &mpi.GetFileRequest{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: logger.GetCorrelationID(ctx), Timestamp: timestamppb.Now(), }, diff --git a/internal/file/file_plugin.go b/internal/file/file_plugin.go index aee199ff57..1b93565489 100644 --- a/internal/file/file_plugin.go +++ b/internal/file/file_plugin.go @@ -12,10 +12,10 @@ import ( "github.com/nginx/agent/v3/pkg/files" - "github.com/google/uuid" mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" "github.com/nginx/agent/v3/internal/bus" "github.com/nginx/agent/v3/internal/config" + "github.com/nginx/agent/v3/internal/datasource/proto" "github.com/nginx/agent/v3/internal/grpc" "github.com/nginx/agent/v3/internal/logger" "github.com/nginx/agent/v3/internal/model" @@ -319,7 +319,7 @@ func (fp *FilePlugin) handleConfigUploadRequest(ctx context.Context, msg *bus.Me response := &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, @@ -343,7 +343,7 @@ func (fp *FilePlugin) createDataPlaneResponse(correlationID string, status mpi.C ) *mpi.DataPlaneResponse { return &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, diff --git a/internal/file/file_plugin_test.go b/internal/file/file_plugin_test.go index 2544fa4976..27c29949b1 100644 --- a/internal/file/file_plugin_test.go +++ b/internal/file/file_plugin_test.go @@ -13,8 +13,8 @@ import ( "time" "github.com/nginx/agent/v3/internal/bus/busfakes" + "github.com/nginx/agent/v3/internal/datasource/proto" - "github.com/google/uuid" "google.golang.org/protobuf/types/known/timestamppb" mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" @@ -441,7 +441,7 @@ func TestFilePlugin_Process_ConfigApplyRollbackCompleteTopic(t *testing.T) { expectedResponse := &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: "dfsbhj6-bc92-30c1-a9c9-85591422068e", Timestamp: timestamppb.Now(), }, diff --git a/internal/logger/logger.go b/internal/logger/logger.go index cc0728996b..ec7491f52a 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -13,8 +13,8 @@ import ( "path" "strings" - "github.com/google/uuid" "github.com/nginx/agent/v3/internal/config" + "github.com/nginx/agent/v3/internal/datasource/proto" ) const ( @@ -121,7 +121,7 @@ func (h contextHandler) observe(ctx context.Context) (as []slog.Attr) { } func GenerateCorrelationID() slog.Attr { - return slog.Any(CorrelationIDKey, uuid.NewString()) + return slog.Any(CorrelationIDKey, proto.GenerateMessageID()) } func GetCorrelationID(ctx context.Context) string { diff --git a/internal/resource/resource_plugin.go b/internal/resource/resource_plugin.go index c3dc5ca40e..a2c05ae654 100644 --- a/internal/resource/resource_plugin.go +++ b/internal/resource/resource_plugin.go @@ -9,8 +9,8 @@ import ( "context" "log/slog" - "github.com/google/uuid" "github.com/nginx/agent/v3/internal/config" + "github.com/nginx/agent/v3/internal/datasource/proto" "github.com/nginx/agent/v3/internal/model" "google.golang.org/protobuf/types/known/timestamppb" @@ -186,7 +186,7 @@ func (*Resource) createDataPlaneResponse(correlationID string, status mpi.Comman ) *mpi.DataPlaneResponse { return &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: correlationID, Timestamp: timestamppb.Now(), }, diff --git a/internal/watcher/file/file_watcher_service_test.go b/internal/watcher/file/file_watcher_service_test.go index 8595c8b4a5..fae18a2f2b 100644 --- a/internal/watcher/file/file_watcher_service_test.go +++ b/internal/watcher/file/file_watcher_service_test.go @@ -10,10 +10,10 @@ import ( "context" "os" "path" - "strings" "testing" "time" + "github.com/nginx/agent/v3/test/helpers" "github.com/nginx/agent/v3/test/stub" "github.com/fsnotify/fsnotify" @@ -127,9 +127,9 @@ func TestFileWatcherService_removeWatcher(t *testing.T) { fileWatcherService.directoriesBeingWatched.Store(testDirectory, true) fileWatcherService.removeWatcher(ctx, testDirectory) - if s := logBuf.String(); !strings.Contains(s, "Failed to remove file watcher") { - t.Errorf("Unexpected log %s", s) - } + helpers.ValidateLog(t, "Failed to remove file watcher", logBuf) + + logBuf.Reset() } func TestFileWatcherService_isEventSkippable(t *testing.T) { diff --git a/internal/watcher/instance/nginx_config_parser_test.go b/internal/watcher/instance/nginx_config_parser_test.go index 36f08c4a98..b37b4b3cc4 100644 --- a/internal/watcher/instance/nginx_config_parser_test.go +++ b/internal/watcher/instance/nginx_config_parser_test.go @@ -12,7 +12,6 @@ import ( "net/http" "net/http/httptest" "os" - "strings" "testing" "github.com/google/go-cmp/cmp" @@ -936,9 +935,9 @@ func TestNginxConfigParser_ignoreLog(t *testing.T) { ncp := NewNginxConfigParser(agentConfig) assert.Equal(t, test.expected, ncp.ignoreLog(test.logPath)) - if s := logBuf.String(); !strings.Contains(s, test.expectedLog) { - t.Errorf("Expected to receive log: %s", test.expectedLog) - } + helpers.ValidateLog(t, test.expectedLog, logBuf) + + logBuf.Reset() }) } } diff --git a/internal/watcher/watcher_plugin_test.go b/internal/watcher/watcher_plugin_test.go index c4b74d09a7..427b76af2b 100644 --- a/internal/watcher/watcher_plugin_test.go +++ b/internal/watcher/watcher_plugin_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/nginx/agent/v3/internal/bus/busfakes" + "github.com/nginx/agent/v3/internal/datasource/proto" - "github.com/google/uuid" "google.golang.org/protobuf/types/known/timestamppb" "github.com/nginx/agent/v3/internal/watcher/health" @@ -138,7 +138,7 @@ func TestWatcher_Process_ConfigApplySuccessfulTopic(t *testing.T) { response := &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: "dfsbhj6-bc92-30c1-a9c9-85591422068e", Timestamp: timestamppb.Now(), }, @@ -172,7 +172,7 @@ func TestWatcher_Process_RollbackCompleteTopic(t *testing.T) { response := &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: "dfsbhj6-bc92-30c1-a9c9-85591422068e", Timestamp: timestamppb.Now(), }, diff --git a/pkg/uuid/uuid.go b/pkg/uuid/uuid.go index ee18764493..b10d5bb885 100644 --- a/pkg/uuid/uuid.go +++ b/pkg/uuid/uuid.go @@ -12,6 +12,21 @@ import ( "github.com/google/uuid" ) +// Generate creates a UUID based on a hashed string derived from the input format and arguments. +// This function is used primarilty in generating the NGINX Id +// +// Parameters: +// - format: A format string, similar to fmt.Sprintf. +// - a: Variadic arguments to be substituted into the format string. +// +// Process: +// 1. Creates a SHA-256 hash from the formatted string (using `fmt.Sprintf`). +// 2. Converts the hash to a hexadecimal string. +// 3. Generates an MD5-based UUID using the hashed string. +// +// Returns: +// +// A string representation of the generated UUID. func Generate(format string, a ...interface{}) string { h := sha256.New() s := fmt.Sprintf(format, a...) diff --git a/test/helpers/os_utils.go b/test/helpers/os_utils.go index 5ff3dc9462..5670bb02c8 100644 --- a/test/helpers/os_utils.go +++ b/test/helpers/os_utils.go @@ -7,6 +7,8 @@ package helpers import ( "os" + "regexp" + "strings" "testing" "github.com/stretchr/testify/require" @@ -14,6 +16,7 @@ import ( const ( filePermission = 0o700 + specialChars = "#$%\x00\x01\n" ) func CreateDirWithErrorCheck(t testing.TB, dirName string) { @@ -40,3 +43,15 @@ func RemoveFileWithErrorCheck(t testing.TB, fileName string) { require.NoError(t, err) } + +// RemoveASCIIControlSignals removes all non-printable ASCII control characters from a string. +func RemoveASCIIControlSignals(t testing.TB, input string) string { + t.Helper() + + // Use a regex to match and remove ASCII control characters (0x00 to 0x1F and 0x7F). + // by matching all control characters (ASCII 0–31 and 127). + re := regexp.MustCompile(`[[:cntrl:]]`) + output := strings.Trim(re.ReplaceAllString(input, ""), specialChars) + + return output +} diff --git a/test/helpers/os_utils_test.go b/test/helpers/os_utils_test.go new file mode 100644 index 0000000000..b7d601580f --- /dev/null +++ b/test/helpers/os_utils_test.go @@ -0,0 +1,61 @@ +// 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 helpers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// nolint: stylecheck +func TestRemoveASCIIControlSignals(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "No control characters", + input: "Hello, World!", + expected: "Hello, World!", + }, + { + name: "With control characters", + input: "Hello , World!", expected: "Hello, World!", + }, + { + name: "Only control characters", + input: " ", expected: "", + }, + { + name: "Mixed printable and control characters", + input: "Hello\nWorld\t!", + expected: "HelloWorld!", + }, + { + name: "Empty string", + input: "", + expected: "", + }, + { + name: "Agent version example", + input: " nginx-agent version v3.0.0-4a64a94", expected: "nginx-agent version v3.0.0-4a64a94", + }, + { + name: "Agent version example alpine", + input: "#nginx-agent version v3.0.0-f94d93a", + expected: "nginx-agent version v3.0.0-f94d93a", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := RemoveASCIIControlSignals(t, test.input) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/test/helpers/slog_utils.go b/test/helpers/slog_utils.go new file mode 100644 index 0000000000..650c72ee3b --- /dev/null +++ b/test/helpers/slog_utils.go @@ -0,0 +1,48 @@ +// 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 helpers + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +// ValidateLog checks if the expected log message is present in the provided log buffer. +// If the expected log message is not found, it reports an error to the testing framework. +// +// Parameters: +// - t (*testing.T): The testing object used to report errors. +// - expectedLog (string): The expected log message to validate against the log buffer. +// If empty, no validation is performed. +// - logBuf (*bytes.Buffer): The log buffer that contains the actual log messages. +// +// Behavior: +// - If the expected log message is not an empty string: +// - The function checks whether the log buffer contains the expected log message. +// - If the log message is missing, an error is reported using t.Errorf. +// +// Usage: +// - Use this function within test cases to validate that a specific log message was produced. +// +// Example: +// +// var logBuffer bytes.Buffer +// logBuffer.WriteString("App started successfully") +// ValidateLog(t, "App started successfully", &logBuffer) +func ValidateLog(t *testing.T, expectedLog string, logBuf *bytes.Buffer) { + t.Helper() + + if expectedLog != "" { + require.NotEmpty(t, logBuf) + + if !strings.Contains(logBuf.String(), expectedLog) { + t.Errorf("Expected log to contain %q, but got %q", expectedLog, logBuf.String()) + } + } +} diff --git a/test/integration/install_uninstall_test.go b/test/integration/install_uninstall_test.go index 62eeb50320..95f8444a31 100644 --- a/test/integration/install_uninstall_test.go +++ b/test/integration/install_uninstall_test.go @@ -159,10 +159,10 @@ func verifyAgentVersion(ctx context.Context, tb testing.TB, testContainer testco exitCode, cmdOut, err := testContainer.Exec(ctx, []string{"nginx-agent", "--version"}) require.NoError(tb, err) assert.Equal(tb, 0, exitCode) - stdoutStderr, _ := io.ReadAll(cmdOut) - versionOutput := strings.Trim(string(stdoutStderr), "#$%\x00\x01\n") - require.NoError(tb, err) + stdoutStderr, readAllErr := io.ReadAll(cmdOut) + versionOutput := helpers.RemoveASCIIControlSignals(tb, string(stdoutStderr)) + require.NoError(tb, readAllErr) assert.Equal(tb, expectedVersionOutput, versionOutput) } @@ -176,8 +176,10 @@ func installAgent(ctx context.Context, tb testing.TB, container testcontainers.C exitCode, cmdOut, err := container.Exec(ctx, installCmd) require.NoError(tb, err) + stdoutStderr, err := io.ReadAll(cmdOut) require.NoError(tb, err) + msg := fmt.Sprintf("expected error code of 0 from cmd %q. Got: %v\n %s", installCmd, exitCode, stdoutStderr) assert.Equal(tb, 0, exitCode, msg) diff --git a/test/mock/grpc/mock_management_command_service.go b/test/mock/grpc/mock_management_command_service.go index 9d1192ea40..ce9abd24e7 100644 --- a/test/mock/grpc/mock_management_command_service.go +++ b/test/mock/grpc/mock_management_command_service.go @@ -20,8 +20,9 @@ import ( "sync" "github.com/gin-gonic/gin" - "github.com/google/uuid" + mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" + "github.com/nginx/agent/v3/internal/datasource/proto" "github.com/nginx/agent/v3/pkg/files" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/timestamppb" @@ -362,8 +363,8 @@ func (cs *CommandService) addConfigApplyEndpoint() { request := mpi.ManagementPlaneRequest{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), - CorrelationId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), + CorrelationId: proto.GenerateMessageID(), Timestamp: timestamppb.Now(), }, Request: &mpi.ManagementPlaneRequest_ConfigApplyRequest{ diff --git a/test/mock/grpc/mock_management_file_service.go b/test/mock/grpc/mock_management_file_service.go index 2e312182ff..4a5485a412 100644 --- a/test/mock/grpc/mock_management_file_service.go +++ b/test/mock/grpc/mock_management_file_service.go @@ -13,8 +13,8 @@ import ( "path/filepath" "strconv" - "github.com/google/uuid" "github.com/nginx/agent/v3/api/grpc/mpi/v1" + "github.com/nginx/agent/v3/internal/datasource/proto" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/protojson" @@ -76,7 +76,7 @@ func (mgs *FileService) UpdateOverview( configUploadRequest := &v1.ManagementPlaneRequest{ MessageMeta: &v1.MessageMeta{ - MessageId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), CorrelationId: request.GetMessageMeta().GetCorrelationId(), Timestamp: timestamppb.Now(), }, diff --git a/test/protos/data_plane_response.go b/test/protos/data_plane_response.go index c7a01f5357..5ce05c5c01 100644 --- a/test/protos/data_plane_response.go +++ b/test/protos/data_plane_response.go @@ -6,21 +6,23 @@ package protos import ( - "github.com/google/uuid" mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" + "github.com/nginx/agent/v3/internal/datasource/proto" "google.golang.org/protobuf/types/known/timestamppb" ) +const success = "Success" + func OKDataPlaneResponse() *mpi.DataPlaneResponse { return &mpi.DataPlaneResponse{ MessageMeta: &mpi.MessageMeta{ - MessageId: uuid.NewString(), - CorrelationId: uuid.NewString(), + MessageId: proto.GenerateMessageID(), + CorrelationId: proto.GenerateMessageID(), Timestamp: timestamppb.Now(), }, CommandResponse: &mpi.CommandResponse{ Status: mpi.CommandResponse_COMMAND_STATUS_OK, - Message: "Success", + Message: success, }, InstanceId: ossInstanceID, } From 8d7c4debd1621bb60d46b20726eda50243d8facf Mon Sep 17 00:00:00 2001 From: oliveromahony Date: Thu, 12 Dec 2024 16:04:02 +0000 Subject: [PATCH 2/2] updated golang crypto (#946) --- go.mod | 8 ++++---- go.sum | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 8481ed0fe2..0a2dd1ce25 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e golang.org/x/mod v0.21.0 - golang.org/x/sync v0.9.0 + golang.org/x/sync v0.10.0 google.golang.org/protobuf v1.35.2 ) @@ -307,9 +307,9 @@ require ( github.com/vardius/message-bus v1.1.5 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.32.0 - golang.org/x/crypto v0.29.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.31.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/grpc v1.68.0 ) diff --git a/go.sum b/go.sum index 5a643b5455..adda7ce6c5 100644 --- a/go.sum +++ b/go.sum @@ -804,8 +804,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= @@ -842,8 +842,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -862,19 +862,19 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=