diff --git a/api/grpc/mpi/v1/command_grpc.pb.go b/api/grpc/mpi/v1/command_grpc.pb.go index dbf61a337..ba20831d9 100644 --- a/api/grpc/mpi/v1/command_grpc.pb.go +++ b/api/grpc/mpi/v1/command_grpc.pb.go @@ -8,7 +8,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 +// - protoc-gen-go-grpc v1.6.0 // - protoc (unknown) // source: mpi/v1/command.proto @@ -144,16 +144,16 @@ type CommandServiceServer interface { type UnimplementedCommandServiceServer struct{} func (UnimplementedCommandServiceServer) CreateConnection(context.Context, *CreateConnectionRequest) (*CreateConnectionResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateConnection not implemented") + return nil, status.Error(codes.Unimplemented, "method CreateConnection not implemented") } func (UnimplementedCommandServiceServer) UpdateDataPlaneStatus(context.Context, *UpdateDataPlaneStatusRequest) (*UpdateDataPlaneStatusResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateDataPlaneStatus not implemented") + return nil, status.Error(codes.Unimplemented, "method UpdateDataPlaneStatus not implemented") } func (UnimplementedCommandServiceServer) UpdateDataPlaneHealth(context.Context, *UpdateDataPlaneHealthRequest) (*UpdateDataPlaneHealthResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateDataPlaneHealth not implemented") + return nil, status.Error(codes.Unimplemented, "method UpdateDataPlaneHealth not implemented") } func (UnimplementedCommandServiceServer) Subscribe(grpc.BidiStreamingServer[DataPlaneResponse, ManagementPlaneRequest]) error { - return status.Errorf(codes.Unimplemented, "method Subscribe not implemented") + return status.Error(codes.Unimplemented, "method Subscribe not implemented") } func (UnimplementedCommandServiceServer) testEmbeddedByValue() {} @@ -165,7 +165,7 @@ type UnsafeCommandServiceServer interface { } func RegisterCommandServiceServer(s grpc.ServiceRegistrar, srv CommandServiceServer) { - // If the following call pancis, it indicates UnimplementedCommandServiceServer was + // If the following call panics, it indicates UnimplementedCommandServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/api/grpc/mpi/v1/files_grpc.pb.go b/api/grpc/mpi/v1/files_grpc.pb.go index 69efda491..80ed54f75 100644 --- a/api/grpc/mpi/v1/files_grpc.pb.go +++ b/api/grpc/mpi/v1/files_grpc.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 +// - protoc-gen-go-grpc v1.6.0 // - protoc (unknown) // source: mpi/v1/files.proto @@ -174,22 +174,22 @@ type FileServiceServer interface { type UnimplementedFileServiceServer struct{} func (UnimplementedFileServiceServer) GetOverview(context.Context, *GetOverviewRequest) (*GetOverviewResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetOverview not implemented") + return nil, status.Error(codes.Unimplemented, "method GetOverview not implemented") } func (UnimplementedFileServiceServer) UpdateOverview(context.Context, *UpdateOverviewRequest) (*UpdateOverviewResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateOverview not implemented") + return nil, status.Error(codes.Unimplemented, "method UpdateOverview not implemented") } func (UnimplementedFileServiceServer) GetFile(context.Context, *GetFileRequest) (*GetFileResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetFile not implemented") + return nil, status.Error(codes.Unimplemented, "method GetFile not implemented") } func (UnimplementedFileServiceServer) UpdateFile(context.Context, *UpdateFileRequest) (*UpdateFileResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateFile not implemented") + return nil, status.Error(codes.Unimplemented, "method UpdateFile not implemented") } func (UnimplementedFileServiceServer) GetFileStream(*GetFileRequest, grpc.ServerStreamingServer[FileDataChunk]) error { - return status.Errorf(codes.Unimplemented, "method GetFileStream not implemented") + return status.Error(codes.Unimplemented, "method GetFileStream not implemented") } func (UnimplementedFileServiceServer) UpdateFileStream(grpc.ClientStreamingServer[FileDataChunk, UpdateFileResponse]) error { - return status.Errorf(codes.Unimplemented, "method UpdateFileStream not implemented") + return status.Error(codes.Unimplemented, "method UpdateFileStream not implemented") } func (UnimplementedFileServiceServer) testEmbeddedByValue() {} @@ -201,7 +201,7 @@ type UnsafeFileServiceServer interface { } func RegisterFileServiceServer(s grpc.ServiceRegistrar, srv FileServiceServer) { - // If the following call pancis, it indicates UnimplementedFileServiceServer was + // If the following call panics, it indicates UnimplementedFileServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. diff --git a/internal/config/config.go b/internal/config/config.go index 242bf6671..ac05c5ad6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -469,6 +469,12 @@ func registerFlags() { "The port Agent will start the syslog server on for logs collection", ) + fs.Int( + MaxAccessLogFilesKey, + DefMaxAccessLogFiles, + "The maximum number of access log files to monitor", + ) + registerCommonFlags(fs) registerCommandFlags(fs) registerAuxiliaryCommandFlags(fs) @@ -1091,6 +1097,7 @@ func resolveDataPlaneConfig() *DataPlaneConfig { RandomizationFactor: viperInstance.GetFloat64(NginxReloadBackoffRandomizationFactorKey), Multiplier: viperInstance.GetFloat64(NginxReloadBackoffMultiplierKey), }, + MaxAccessLogFiles: viperInstance.GetInt(MaxAccessLogFilesKey), }, } } diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 0f1e08075..1cb9bf943 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -15,6 +15,7 @@ const ( DefNginxReloadMonitoringPeriod = 10 * time.Second DefTreatErrorsAsWarnings = false DefNginxApiTlsCa = "" + DefMaxAccessLogFiles = 10 // Nginx Reload Backoff defaults DefNginxReloadBackoffInitialInterval = 500 * time.Millisecond diff --git a/internal/config/flags.go b/internal/config/flags.go index d0f664540..a46656b06 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -136,6 +136,7 @@ var ( NginxReloadBackoffMultiplierKey = pre(NginxReloadBackoffKey) + "multiplier" NginxExcludeLogsKey = pre(DataPlaneConfigRootKey, "nginx") + "exclude_logs" NginxApiTlsCa = pre(DataPlaneConfigRootKey, "nginx") + "api_tls_ca" + MaxAccessLogFilesKey = pre(DataPlaneConfigRootKey, "nginx") + "max_access_log_files" SyslogServerPort = pre("syslog_server") + "port" diff --git a/internal/config/types.go b/internal/config/types.go index 72eda1369..8873537bc 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -71,6 +71,7 @@ type ( ExcludeLogs []string `yaml:"exclude_logs" mapstructure:"exclude_logs"` ReloadMonitoringPeriod time.Duration `yaml:"reload_monitoring_period" mapstructure:"reload_monitoring_period"` TreatWarningsAsErrors bool `yaml:"treat_warnings_as_errors" mapstructure:"treat_warnings_as_errors"` + MaxAccessLogFiles int `yaml:"max_access_log_files" mapstructure:"max_access_log_files"` } Client struct { diff --git a/internal/datasource/config/nginx_config_parser.go b/internal/datasource/config/nginx_config_parser.go index 057821119..3cd41acc9 100644 --- a/internal/datasource/config/nginx_config_parser.go +++ b/internal/datasource/config/nginx_config_parser.go @@ -361,6 +361,13 @@ func (ncp *NginxConfigParser) addAccessLog(accessLog *model.AccessLog, } } + if len(accessLogs) >= ncp.agentConfig.DataPlaneConfig.Nginx.MaxAccessLogFiles { + slog.Warn("Maximum access log files have been reached, unable to monitor access log", + "access_log", accessLog.Name) + + return accessLogs + } + slog.Debug("Found valid access log", "access_log", accessLog.Name) return append(accessLogs, accessLog) diff --git a/internal/datasource/config/nginx_config_parser_test.go b/internal/datasource/config/nginx_config_parser_test.go index 37e915ab3..9b49137da 100644 --- a/internal/datasource/config/nginx_config_parser_test.go +++ b/internal/datasource/config/nginx_config_parser_test.go @@ -755,6 +755,7 @@ func TestNginxConfigParser_checkLog(t *testing.T) { accessLog *model.AccessLog currentAccessLogs []*model.AccessLog expectedAccessLogs []*model.AccessLog + maxAccessLogFiles int }{ { name: "Test 1: valid access log", @@ -790,7 +791,8 @@ func TestNginxConfigParser_checkLog(t *testing.T) { Readable: true, }, }, - expectedLog: "Found valid access log", + expectedLog: "Found valid access log", + maxAccessLogFiles: 3, }, { name: "Test 2: Duplicate access log, with same format", @@ -819,7 +821,8 @@ func TestNginxConfigParser_checkLog(t *testing.T) { Readable: true, }, }, - expectedLog: "Found duplicate access log, skipping", + expectedLog: "Found duplicate access log, skipping", + maxAccessLogFiles: 3, }, { @@ -844,12 +847,59 @@ func TestNginxConfigParser_checkLog(t *testing.T) { expectedLog: "Found multiple log_format directives for the same access log. " + "Multiple log formats are not supported in the same access log, metrics from this access log " + "will not be collected", + maxAccessLogFiles: 3, + }, + + { + name: "Test 4: valid access log, maximum access logs reached", + accessLog: &model.AccessLog{ + Name: "/var/log/nginx/access3.log", + Format: "$remote_addr - $remote_user [$time_local] \"$request\" \"$http_user_agent\" " + + "\"$http_x_forwarded_for\"$status $body_bytes_sent \"$http_referer\"", + Permissions: "", + Readable: true, + }, + currentAccessLogs: []*model.AccessLog{ + { + Name: "/var/log/nginx/access.log", + Format: "$remote_addr - $remote_user [$time_local] \"$request\" \"$http_user_agent\" " + + "\"$http_x_forwarded_for\"$status $body_bytes_sent \"$http_referer\"", + Permissions: "", + Readable: true, + }, + { + Name: "/var/log/nginx/access2.log", + Format: "$remote_addr - $remote_user [$time_local] \"$request\" \"$http_user_agent\" " + + "\"$http_x_forwarded_for\"$status $body_bytes_sent \"$http_referer\"", + Permissions: "", + Readable: true, + }, + }, + expectedAccessLogs: []*model.AccessLog{ + { + Name: "/var/log/nginx/access.log", + Format: "$remote_addr - $remote_user [$time_local] \"$request\" \"$http_user_agent\" " + + "\"$http_x_forwarded_for\"$status $body_bytes_sent \"$http_referer\"", + Permissions: "", + Readable: true, + }, + { + Name: "/var/log/nginx/access2.log", + Format: "$remote_addr - $remote_user [$time_local] \"$request\" \"$http_user_agent\" " + + "\"$http_x_forwarded_for\"$status $body_bytes_sent \"$http_referer\"", + Permissions: "", + Readable: true, + }, + }, + expectedLog: "Maximum access log files have been reached", + maxAccessLogFiles: 2, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ncp := NewNginxConfigParser(types.AgentConfig()) + ncp.agentConfig.DataPlaneConfig.Nginx.MaxAccessLogFiles = test.maxAccessLogFiles logs := ncp.addAccessLog(test.accessLog, test.currentAccessLogs) assert.Equal(t, test.expectedAccessLogs, logs) diff --git a/test/types/config.go b/test/types/config.go index 97f2165df..41c103538 100644 --- a/test/types/config.go +++ b/test/types/config.go @@ -169,6 +169,7 @@ func AgentConfig() *config.Config { TreatWarningsAsErrors: true, ReloadMonitoringPeriod: reloadMonitoringPeriod, ExcludeLogs: []string{}, + MaxAccessLogFiles: config.DefMaxAccessLogFiles, }, }, Watchers: &config.Watchers{