mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
308 lines
7.5 KiB
Go
308 lines
7.5 KiB
Go
package plugin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
|
|
)
|
|
|
|
// ConfigManager handles configuration persistence and retrieval
|
|
type ConfigManager struct {
|
|
dataDir string
|
|
configs map[string]*JobTypeConfig
|
|
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewConfigManager creates a new configuration manager
|
|
func NewConfigManager(dataDir string) (*ConfigManager, error) {
|
|
cm := &ConfigManager{
|
|
dataDir: filepath.Join(dataDir, "plugins"),
|
|
configs: make(map[string]*JobTypeConfig),
|
|
}
|
|
|
|
// Create plugins directory if it doesn't exist
|
|
if err := os.MkdirAll(cm.dataDir, 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create plugin config directory: %w", err)
|
|
}
|
|
|
|
return cm, nil
|
|
}
|
|
|
|
// LoadConfig loads a configuration from disk
|
|
func (cm *ConfigManager) LoadConfig(jobType string) (*JobTypeConfig, error) {
|
|
cm.mu.Lock()
|
|
defer cm.mu.Unlock()
|
|
|
|
// Check cache first
|
|
if config, exists := cm.configs[jobType]; exists {
|
|
return config, nil
|
|
}
|
|
|
|
configPath := filepath.Join(cm.dataDir, jobType+".json")
|
|
|
|
// If file doesn't exist, create default config
|
|
if _, err := os.Stat(configPath); err != nil {
|
|
if os.IsNotExist(err) {
|
|
config := &JobTypeConfig{
|
|
JobType: jobType,
|
|
Enabled: false,
|
|
AdminConfig: make([]*plugin_pb.ConfigFieldValue, 0),
|
|
WorkerConfig: make([]*plugin_pb.ConfigFieldValue, 0),
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
CreatedBy: "system",
|
|
}
|
|
cm.configs[jobType] = config
|
|
return config, nil
|
|
}
|
|
return nil, fmt.Errorf("failed to access config file: %w", err)
|
|
}
|
|
|
|
// Load from file
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read config file: %w", err)
|
|
}
|
|
|
|
var configData map[string]interface{}
|
|
if err := json.Unmarshal(data, &configData); err != nil {
|
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
|
}
|
|
|
|
config := &JobTypeConfig{
|
|
JobType: jobType,
|
|
AdminConfig: make([]*plugin_pb.ConfigFieldValue, 0),
|
|
WorkerConfig: make([]*plugin_pb.ConfigFieldValue, 0),
|
|
}
|
|
|
|
// Parse fields
|
|
if enabled, ok := configData["enabled"].(bool); ok {
|
|
config.Enabled = enabled
|
|
}
|
|
|
|
if createdBy, ok := configData["created_by"].(string); ok {
|
|
config.CreatedBy = createdBy
|
|
}
|
|
|
|
if createdAt, ok := configData["created_at"].(string); ok {
|
|
if t, err := time.Parse(time.RFC3339, createdAt); err == nil {
|
|
config.CreatedAt = t
|
|
}
|
|
}
|
|
|
|
if updatedAt, ok := configData["updated_at"].(string); ok {
|
|
if t, err := time.Parse(time.RFC3339, updatedAt); err == nil {
|
|
config.UpdatedAt = t
|
|
}
|
|
}
|
|
|
|
cm.configs[jobType] = config
|
|
return config, nil
|
|
}
|
|
|
|
// SaveConfig saves a configuration to disk
|
|
func (cm *ConfigManager) SaveConfig(config *JobTypeConfig) error {
|
|
cm.mu.Lock()
|
|
defer cm.mu.Unlock()
|
|
|
|
config.UpdatedAt = time.Now()
|
|
cm.configs[config.JobType] = config
|
|
|
|
configPath := filepath.Join(cm.dataDir, config.JobType+".json")
|
|
|
|
// Convert to JSON-serializable format
|
|
configData := map[string]interface{}{
|
|
"job_type": config.JobType,
|
|
"enabled": config.Enabled,
|
|
"created_by": config.CreatedBy,
|
|
"created_at": config.CreatedAt.Format(time.RFC3339),
|
|
"updated_at": config.UpdatedAt.Format(time.RFC3339),
|
|
"admin_config": cm.configValuesToJSON(config.AdminConfig),
|
|
"worker_config": cm.configValuesToJSON(config.WorkerConfig),
|
|
}
|
|
|
|
data, err := json.MarshalIndent(configData, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(configPath, data, 0644); err != nil {
|
|
return fmt.Errorf("failed to write config file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetConfig retrieves a configuration
|
|
func (cm *ConfigManager) GetConfig(jobType string) (*JobTypeConfig, error) {
|
|
cm.mu.RLock()
|
|
if config, exists := cm.configs[jobType]; exists {
|
|
cm.mu.RUnlock()
|
|
return config, nil
|
|
}
|
|
cm.mu.RUnlock()
|
|
|
|
// Load from disk if not in cache
|
|
return cm.LoadConfig(jobType)
|
|
}
|
|
|
|
// UpdateConfig updates a configuration
|
|
func (cm *ConfigManager) UpdateConfig(jobType string, enabled bool, adminConfig, workerConfig []*plugin_pb.ConfigFieldValue, updatedBy string) (*JobTypeConfig, error) {
|
|
config, err := cm.GetConfig(jobType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
config.SetConfig(enabled, adminConfig, workerConfig)
|
|
config.CreatedBy = updatedBy
|
|
|
|
if err := cm.SaveConfig(config); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// ListConfigs lists all configurations
|
|
func (cm *ConfigManager) ListConfigs() []*JobTypeConfig {
|
|
cm.mu.RLock()
|
|
defer cm.mu.RUnlock()
|
|
|
|
configs := make([]*JobTypeConfig, 0, len(cm.configs))
|
|
for _, config := range cm.configs {
|
|
configs = append(configs, config)
|
|
}
|
|
return configs
|
|
}
|
|
|
|
// DeleteConfig deletes a configuration
|
|
func (cm *ConfigManager) DeleteConfig(jobType string) error {
|
|
cm.mu.Lock()
|
|
defer cm.mu.Unlock()
|
|
|
|
configPath := filepath.Join(cm.dataDir, jobType+".json")
|
|
|
|
if err := os.Remove(configPath); err != nil && !os.IsNotExist(err) {
|
|
return fmt.Errorf("failed to delete config file: %w", err)
|
|
}
|
|
|
|
delete(cm.configs, jobType)
|
|
return nil
|
|
}
|
|
|
|
// configValuesToJSON converts ConfigFieldValue slice to JSON
|
|
func (cm *ConfigManager) configValuesToJSON(values []*plugin_pb.ConfigFieldValue) []map[string]interface{} {
|
|
result := make([]map[string]interface{}, 0, len(values))
|
|
|
|
for _, val := range values {
|
|
item := map[string]interface{}{
|
|
"field_name": val.FieldName,
|
|
}
|
|
|
|
// Include non-empty values
|
|
if val.StringValue != "" {
|
|
item["string_value"] = val.StringValue
|
|
}
|
|
if val.IntValue != 0 {
|
|
item["int_value"] = val.IntValue
|
|
}
|
|
if val.FloatValue != 0 {
|
|
item["float_value"] = val.FloatValue
|
|
}
|
|
if val.BoolValue {
|
|
item["bool_value"] = val.BoolValue
|
|
}
|
|
if val.DurationValue != nil {
|
|
item["duration_value"] = val.DurationValue.String()
|
|
}
|
|
if len(val.MultiselectValues) > 0 {
|
|
item["multiselect_values"] = val.MultiselectValues
|
|
}
|
|
if val.JsonValue != "" {
|
|
item["json_value"] = val.JsonValue
|
|
}
|
|
|
|
result = append(result, item)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// LoadAllConfigs loads all configurations from disk
|
|
func (cm *ConfigManager) LoadAllConfigs() error {
|
|
cm.mu.Lock()
|
|
defer cm.mu.Unlock()
|
|
|
|
entries, err := os.ReadDir(cm.dataDir)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read config directory: %w", err)
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
// Only load .json files
|
|
if filepath.Ext(entry.Name()) != ".json" {
|
|
continue
|
|
}
|
|
|
|
jobType := entry.Name()[:len(entry.Name())-5] // Remove .json
|
|
|
|
configPath := filepath.Join(cm.dataDir, entry.Name())
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
var configData map[string]interface{}
|
|
if err := json.Unmarshal(data, &configData); err != nil {
|
|
continue
|
|
}
|
|
|
|
config := &JobTypeConfig{
|
|
JobType: jobType,
|
|
AdminConfig: make([]*plugin_pb.ConfigFieldValue, 0),
|
|
WorkerConfig: make([]*plugin_pb.ConfigFieldValue, 0),
|
|
}
|
|
|
|
// Parse fields
|
|
if enabled, ok := configData["enabled"].(bool); ok {
|
|
config.Enabled = enabled
|
|
}
|
|
|
|
if createdBy, ok := configData["created_by"].(string); ok {
|
|
config.CreatedBy = createdBy
|
|
}
|
|
|
|
if createdAt, ok := configData["created_at"].(string); ok {
|
|
if t, err := time.Parse(time.RFC3339, createdAt); err == nil {
|
|
config.CreatedAt = t
|
|
}
|
|
}
|
|
|
|
if updatedAt, ok := configData["updated_at"].(string); ok {
|
|
if t, err := time.Parse(time.RFC3339, updatedAt); err == nil {
|
|
config.UpdatedAt = t
|
|
}
|
|
}
|
|
|
|
cm.configs[jobType] = config
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetDataDir returns the data directory path
|
|
func (cm *ConfigManager) GetDataDir() string {
|
|
return cm.dataDir
|
|
}
|