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/resource/resource_service.go b/internal/resource/resource_service.go index 70dc1fa37..00bff24e5 100644 --- a/internal/resource/resource_service.go +++ b/internal/resource/resource_service.go @@ -95,6 +95,7 @@ type ResourceService struct { agentConfig *config.Config instanceOperators map[string]instanceOperator // key is instance ID info host.InfoInterface + manifestFilePath string resourceMutex sync.Mutex operatorsMutex sync.Mutex } @@ -108,6 +109,7 @@ func NewResourceService(ctx context.Context, agentConfig *config.Config) *Resour instanceOperators: make(map[string]instanceOperator), nginxConfigParser: parser.NewNginxConfigParser(agentConfig), agentConfig: agentConfig, + manifestFilePath: agentConfig.LibDir + "/manifest.json", } resourceService.updateResourceInfo(ctx) @@ -216,6 +218,8 @@ func (r *ResourceService) ApplyConfig(ctx context.Context, instanceID string) (* return nil, fmt.Errorf("failed to parse config %w", parseErr) } + nginxConfigContext = r.updateConfigContextFiles(ctx, nginxConfigContext) + datasource.UpdateNginxInstanceRuntime(instance, nginxConfigContext) slog.DebugContext(ctx, "Updated Instance Runtime after parsing config", "instance", instance.GetInstanceRuntime()) @@ -332,6 +336,51 @@ func (r *ResourceService) UpdateHTTPUpstreamServers(ctx context.Context, instanc return added, updated, deleted, createPlusAPIError(updateError) } +func (r *ResourceService) updateConfigContextFiles(ctx context.Context, + nginxConfigContext *model.NginxConfigContext, +) *model.NginxConfigContext { + manifestFiles, manifestErr := r.manifestFile() + if manifestErr != nil { + slog.ErrorContext(ctx, "Error getting manifest files", "error", manifestErr) + } + + for _, manifestFile := range manifestFiles { + if manifestFile.ManifestFileMeta.Unmanaged { + for _, configFile := range nginxConfigContext.Files { + if configFile.GetFileMeta().GetName() == manifestFile.ManifestFileMeta.Name { + configFile.Unmanaged = true + } + } + } + } + + return nginxConfigContext +} + +func (r *ResourceService) manifestFile() (map[string]*model.ManifestFile, error) { + if _, err := os.Stat(r.manifestFilePath); err != nil { + return nil, err + } + + file, err := os.ReadFile(r.manifestFilePath) + if err != nil { + return nil, fmt.Errorf("failed to read manifest file: %w", err) + } + + var manifestFiles map[string]*model.ManifestFile + + err = json.Unmarshal(file, &manifestFiles) + if err != nil { + if len(file) == 0 { + return nil, fmt.Errorf("manifest file is empty: %w", err) + } + + return nil, fmt.Errorf("failed to parse manifest file: %w", err) + } + + return manifestFiles, nil +} + func convertToUpstreamServer(upstreams []*structpb.Struct) []client.UpstreamServer { var servers []client.UpstreamServer res, err := json.Marshal(upstreams) diff --git a/internal/resource/resource_service_test.go b/internal/resource/resource_service_test.go index 39bdd95c2..e629f39ac 100644 --- a/internal/resource/resource_service_test.go +++ b/internal/resource/resource_service_test.go @@ -7,18 +7,18 @@ package resource import ( "context" + "encoding/json" "errors" "fmt" "os" "path/filepath" "testing" - "github.com/nginx/agent/v3/pkg/host/hostfakes" - "github.com/nginx/agent/v3/internal/datasource/config/configfakes" "github.com/nginx/agent/v3/internal/model" + "github.com/nginx/agent/v3/pkg/host/hostfakes" + "github.com/nginx/agent/v3/test/helpers" "github.com/nginx/nginx-plus-go-client/v3/client" - "google.golang.org/protobuf/types/known/structpb" "github.com/nginx/agent/v3/internal/resource/resourcefakes" @@ -399,12 +399,89 @@ func TestResourceService_ApplyConfig(t *testing.T) { } resourceService.resource.Instances = instances - _, reloadError := resourceService.ApplyConfig(ctx, test.instanceID) + configContext, reloadError := resourceService.ApplyConfig(ctx, test.instanceID) assert.Equal(t, test.expected, reloadError) + t.Log("configContext:", configContext) }) } } +func Test_updateConfigContextFiles(t *testing.T) { + ctx := t.Context() + resourceService := NewResourceService(ctx, types.AgentConfig()) + + manifestFileContents := map[string]*model.ManifestFile{ + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Referenced: true, + Unmanaged: false, + }, + }, + "/etc/nginx/unmanaged.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/unmanaged.conf", + Unmanaged: true, + }, + }, + } + + tempDir := t.TempDir() + manifestDirPath := tempDir + manifestFile := helpers.CreateFileWithErrorCheck(t, manifestDirPath, "manifest.json") + + resourceService.manifestFilePath = manifestFile.Name() + manifestJSON, err := json.MarshalIndent(manifestFileContents, "", " ") + require.NoError(t, err) + + _, err = manifestFile.Write(manifestJSON) + require.NoError(t, err) + + nginxConfigContext := &model.NginxConfigContext{ + Files: []*mpi.File{ + { + FileMeta: &mpi.FileMeta{ + Name: "/etc/nginx/unmanaged.conf", + Hash: "", + FileType: nil, + }, + Unmanaged: false, + }, + { + FileMeta: &mpi.FileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "", + FileType: nil, + }, + Unmanaged: false, + }, + }, + } + + expected := &model.NginxConfigContext{ + Files: []*mpi.File{ + { + FileMeta: &mpi.FileMeta{ + Name: "/etc/nginx/unmanaged.conf", + Hash: "", + FileType: nil, + }, + Unmanaged: true, + }, + { + FileMeta: &mpi.FileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "", + FileType: nil, + }, + Unmanaged: false, + }, + }, + } + configContext := resourceService.updateConfigContextFiles(ctx, nginxConfigContext) + assert.Equal(t, expected, configContext) +} + func Test_convertToUpstreamServer(t *testing.T) { expectedMax := 2 expectedFails := 0 diff --git a/internal/watcher/file/file_watcher_service.go b/internal/watcher/file/file_watcher_service.go index 3779d8c99..96c450031 100644 --- a/internal/watcher/file/file_watcher_service.go +++ b/internal/watcher/file/file_watcher_service.go @@ -57,6 +57,7 @@ func NewFileWatcherService(agentConfig *config.Config) *FileWatcherService { } } +//nolint:revive // cant simplify due to for loop func (fws *FileWatcherService) Watch(ctx context.Context, ch chan<- FileUpdateMessage) { monitoringFrequency := fws.agentConfig.Watchers.FileWatcher.MonitoringFrequency slog.DebugContext(ctx, "Starting file watcher monitoring", "monitoring_frequency", monitoringFrequency) @@ -83,7 +84,11 @@ func (fws *FileWatcherService) Watch(ctx context.Context, ch chan<- FileUpdateMe return case <-instanceWatcherTicker.C: - fws.checkForUpdates(ctx, ch) + if fws.enabled.Load() { + fws.checkForUpdates(ctx, ch) + } else { + slog.DebugContext(ctx, "Skipping check for file updates, file watcher is disabled") + } } if fws.watcher != nil {