Files
seaweedfs/weed/admin/plugin/manager.go
T

402 lines
9.3 KiB
Go

package plugin
import (
"context"
"fmt"
"net"
"sync"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
"google.golang.org/grpc"
)
// Manager is the main plugin system manager
type Manager struct {
registry *Registry
dispatcher *Dispatcher
configMgr *ConfigManager
grpcServer *GrpcServer
// gRPC server
grpcListener net.Listener
grpc *grpc.Server
// Configuration
listenAddr string
dataDir string
healthCheckInterval time.Duration
// State
mu sync.RWMutex
running bool
stopChan chan struct{}
}
// NewManager creates a new plugin manager
func NewManager(listenAddr, dataDir string) (*Manager, error) {
// Create configuration manager
configMgr, err := NewConfigManager(dataDir)
if err != nil {
return nil, fmt.Errorf("failed to create config manager: %w", err)
}
// Create registry
registry := NewRegistry()
// Create dispatcher
dispatcher := NewDispatcher(registry, configMgr)
// Create gRPC server implementation
grpcServer := NewGrpcServer(registry, dispatcher, configMgr)
return &Manager{
registry: registry,
dispatcher: dispatcher,
configMgr: configMgr,
grpcServer: grpcServer,
listenAddr: listenAddr,
dataDir: dataDir,
healthCheckInterval: 30 * time.Second,
stopChan: make(chan struct{}),
}, nil
}
// Start starts the plugin manager and gRPC server
func (m *Manager) Start() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.running {
return fmt.Errorf("plugin manager already running")
}
// Start registry health checks
m.registry.Start()
// Start dispatcher
m.dispatcher.Start()
// Create gRPC server
m.grpc = grpc.NewServer()
// Register services
plugin_pb.RegisterPluginServiceServer(m.grpc, m.grpcServer)
plugin_pb.RegisterAdminQueryServiceServer(m.grpc, m.grpcServer)
plugin_pb.RegisterAdminCommandServiceServer(m.grpc, m.grpcServer)
// Start listening
listener, err := net.Listen("tcp", m.listenAddr)
if err != nil {
return fmt.Errorf("failed to listen on %s: %w", m.listenAddr, err)
}
m.grpcListener = listener
// Start gRPC server in background
go func() {
if err := m.grpc.Serve(listener); err != nil && err != grpc.ErrServerStopped {
fmt.Printf("gRPC server error: %v\n", err)
}
}()
m.running = true
fmt.Printf("Plugin manager started on %s\n", m.listenAddr)
return nil
}
// Stop stops the plugin manager and gRPC server
func (m *Manager) Stop() error {
m.mu.Lock()
defer m.mu.Unlock()
if !m.running {
return fmt.Errorf("plugin manager not running")
}
close(m.stopChan)
// Stop dispatcher
m.dispatcher.Stop()
// Stop registry
m.registry.Stop()
// Stop gRPC server
if m.grpc != nil {
m.grpc.GracefulStop()
}
m.running = false
return nil
}
// IsRunning returns whether the manager is running
func (m *Manager) IsRunning() bool {
m.mu.RLock()
defer m.mu.RUnlock()
return m.running
}
// RegisterJobType registers a job type for detection and execution
func (m *Manager) RegisterJobType(jobType string, detectionInterval time.Duration) error {
m.mu.RLock()
running := m.running
m.mu.RUnlock()
if !running {
return fmt.Errorf("plugin manager not running")
}
// Register job type
if err := m.dispatcher.RegisterJobType(jobType, detectionInterval); err != nil {
return err
}
_, err := m.configMgr.LoadConfig(jobType)
if err != nil {
return err
}
return nil
}
// QueueJob queues a new job for execution
func (m *Manager) QueueJob(jobType string, description string, priority int64, metadata map[string]string) (*Job, error) {
m.mu.RLock()
running := m.running
m.mu.RUnlock()
if !running {
return nil, fmt.Errorf("plugin manager not running")
}
jobReq := &plugin_pb.JobRequest{
JobType: jobType,
Description: description,
Priority: priority,
Metadata: metadata,
RequestSource: "admin",
}
return m.dispatcher.QueueJob(jobType, jobReq, "")
}
// DispatchJob dispatches the next available job
func (m *Manager) DispatchJob(jobType string) (*Job, *ConnectedPlugin, error) {
m.mu.RLock()
running := m.running
m.mu.RUnlock()
if !running {
return nil, nil, fmt.Errorf("plugin manager not running")
}
return m.dispatcher.DispatchJob(jobType)
}
// GetJob retrieves a job by ID
func (m *Manager) GetJob(jobID string) (*Job, error) {
return m.dispatcher.GetJob(jobID)
}
// ListJobs returns jobs of a specific type and status
func (m *Manager) ListJobs(jobType string, status JobState) []*Job {
return m.dispatcher.ListJobs(jobType, status)
}
// ListAllJobs returns all jobs
func (m *Manager) ListAllJobs() []*Job {
return m.dispatcher.ListAllJobs()
}
// ListPlugins returns all connected plugins
func (m *Manager) ListPlugins() []*ConnectedPlugin {
return m.registry.ListPlugins()
}
// GetPlugin retrieves a plugin by ID
func (m *Manager) GetPlugin(pluginID string) (*ConnectedPlugin, error) {
return m.registry.GetPlugin(pluginID)
}
// ListPluginsByCapability returns plugins that support a job type
func (m *Manager) ListPluginsByCapability(jobType string) []*ConnectedPlugin {
return m.registry.ListPluginsByCapability(jobType)
}
// UpdateJobTypeConfig updates configuration for a job type
func (m *Manager) UpdateJobTypeConfig(jobType string, enabled bool, config []*plugin_pb.ConfigFieldValue) (*JobTypeConfig, error) {
return m.configMgr.UpdateConfig(jobType, enabled, config, nil, "admin")
}
// GetJobTypeConfig retrieves configuration for a job type
func (m *Manager) GetJobTypeConfig(jobType string) (*JobTypeConfig, error) {
return m.configMgr.GetConfig(jobType)
}
// CancelJob cancels a job
func (m *Manager) CancelJob(jobID string) error {
return m.dispatcher.CancelJob(jobID)
}
// RetryJob retries a failed job
func (m *Manager) RetryJob(jobID string) error {
return m.dispatcher.RetryJob(jobID)
}
// GetStats returns system statistics
func (m *Manager) GetStats() map[string]interface{} {
m.mu.RLock()
running := m.running
m.mu.RUnlock()
stats := map[string]interface{}{
"running": running,
}
if running {
stats["registry"] = m.registry.GetStats()
stats["dispatcher"] = m.dispatcher.GetStats()
}
return stats
}
// WaitForPluginConnection waits for a plugin to connect with timeout
func (m *Manager) WaitForPluginConnection(timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
plugins := m.registry.ListPlugins()
if len(plugins) > 0 {
return nil
}
time.Sleep(100 * time.Millisecond)
}
return fmt.Errorf("no plugin connected within %v", timeout)
}
// CreateTestJob creates a job for testing
func (m *Manager) CreateTestJob(jobType string, description string) (*Job, error) {
return m.QueueJob(jobType, description, 100, map[string]string{
"test": "true",
})
}
// GetHealthStatus returns the health status of the plugin system
func (m *Manager) GetHealthStatus() map[string]interface{} {
m.mu.RLock()
running := m.running
m.mu.RUnlock()
status := map[string]interface{}{
"running": running,
}
if !running {
return status
}
plugins := m.registry.ListPlugins()
var healthyCount int
for _, p := range plugins {
if p.IsHealthy() {
healthyCount++
}
}
jobs := m.dispatcher.ListAllJobs()
var runningJobs, pendingJobs, completedJobs, failedJobs int
for _, job := range jobs {
switch job.GetState() {
case JobStateRunning:
runningJobs++
case JobStatePending:
pendingJobs++
case JobStateCompleted:
completedJobs++
case JobStateFailed:
failedJobs++
}
}
status["plugins"] = map[string]interface{}{
"total": len(plugins),
"healthy": healthyCount,
}
status["jobs"] = map[string]interface{}{
"running": runningJobs,
"pending": pendingJobs,
"completed": completedJobs,
"failed": failedJobs,
"total": len(jobs),
}
return status
}
// LoadAllConfigs loads all configurations from disk
func (m *Manager) LoadAllConfigs() error {
return m.configMgr.LoadAllConfigs()
}
// GetDataDir returns the data directory
func (m *Manager) GetDataDir() string {
return m.configMgr.GetDataDir()
}
// QueryPluginService queries a plugin service for capabilities
func (m *Manager) QueryPluginService(ctx context.Context, pluginID string, addr string) (*plugin_pb.PluginRegister, error) {
// This would be implemented to connect to a plugin and query its capabilities
// For now, return a placeholder error
return nil, fmt.Errorf("not yet implemented")
}
// ExecuteDetection triggers detection for a job type
func (m *Manager) ExecuteDetection(ctx context.Context, jobType string) ([]*DetectedJobInfo, error) {
_, err := m.registry.GetDetectorForJobType(jobType)
if err != nil {
return nil, fmt.Errorf("no detector available for job type %s: %w", jobType, err)
}
// TODO: Call detector plugin's DetectJobs method
// This would require connecting to the plugin and calling its service
return nil, fmt.Errorf("detection not yet implemented")
}
// DetectedJobInfo holds information about a detected job
type DetectedJobInfo struct {
Key string
JobType string
Priority int64
Metadata map[string]string
Timestamp time.Time
}
// GetListenAddr returns the listen address
func (m *Manager) GetListenAddr() string {
m.mu.RLock()
defer m.mu.RUnlock()
return m.listenAddr
}
// GetContext returns a context for the manager
func (m *Manager) GetContext() context.Context {
return context.Background()
}
// Close closes the manager and releases resources
func (m *Manager) Close() error {
if m.IsRunning() {
return m.Stop()
}
return nil
}