maintenance_handlers.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. package handlers
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/gin-gonic/gin"
  11. "github.com/seaweedfs/seaweedfs/weed/admin/config"
  12. "github.com/seaweedfs/seaweedfs/weed/admin/dash"
  13. "github.com/seaweedfs/seaweedfs/weed/admin/maintenance"
  14. "github.com/seaweedfs/seaweedfs/weed/admin/view/app"
  15. "github.com/seaweedfs/seaweedfs/weed/admin/view/layout"
  16. "github.com/seaweedfs/seaweedfs/weed/glog"
  17. "github.com/seaweedfs/seaweedfs/weed/worker/tasks"
  18. "github.com/seaweedfs/seaweedfs/weed/worker/tasks/balance"
  19. "github.com/seaweedfs/seaweedfs/weed/worker/tasks/erasure_coding"
  20. "github.com/seaweedfs/seaweedfs/weed/worker/tasks/vacuum"
  21. "github.com/seaweedfs/seaweedfs/weed/worker/types"
  22. )
  23. // MaintenanceHandlers handles maintenance-related HTTP requests
  24. type MaintenanceHandlers struct {
  25. adminServer *dash.AdminServer
  26. }
  27. // NewMaintenanceHandlers creates a new instance of MaintenanceHandlers
  28. func NewMaintenanceHandlers(adminServer *dash.AdminServer) *MaintenanceHandlers {
  29. return &MaintenanceHandlers{
  30. adminServer: adminServer,
  31. }
  32. }
  33. // ShowTaskDetail displays the task detail page
  34. func (h *MaintenanceHandlers) ShowTaskDetail(c *gin.Context) {
  35. taskID := c.Param("id")
  36. glog.Infof("DEBUG ShowTaskDetail: Starting for task ID: %s", taskID)
  37. taskDetail, err := h.adminServer.GetMaintenanceTaskDetail(taskID)
  38. if err != nil {
  39. glog.Errorf("DEBUG ShowTaskDetail: error getting task detail for %s: %v", taskID, err)
  40. c.String(http.StatusNotFound, "Task not found: %s (Error: %v)", taskID, err)
  41. return
  42. }
  43. glog.Infof("DEBUG ShowTaskDetail: got task detail for %s, task type: %s, status: %s", taskID, taskDetail.Task.Type, taskDetail.Task.Status)
  44. c.Header("Content-Type", "text/html")
  45. taskDetailComponent := app.TaskDetail(taskDetail)
  46. layoutComponent := layout.Layout(c, taskDetailComponent)
  47. err = layoutComponent.Render(c.Request.Context(), c.Writer)
  48. if err != nil {
  49. glog.Errorf("DEBUG ShowTaskDetail: render error: %v", err)
  50. c.String(http.StatusInternalServerError, "Failed to render template: %v", err)
  51. return
  52. }
  53. glog.Infof("DEBUG ShowTaskDetail: template rendered successfully for task %s", taskID)
  54. }
  55. // ShowMaintenanceQueue displays the maintenance queue page
  56. func (h *MaintenanceHandlers) ShowMaintenanceQueue(c *gin.Context) {
  57. // Add timeout to prevent hanging
  58. ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
  59. defer cancel()
  60. // Use a channel to handle timeout for data retrieval
  61. type result struct {
  62. data *maintenance.MaintenanceQueueData
  63. err error
  64. }
  65. resultChan := make(chan result, 1)
  66. go func() {
  67. data, err := h.getMaintenanceQueueData()
  68. resultChan <- result{data: data, err: err}
  69. }()
  70. select {
  71. case res := <-resultChan:
  72. if res.err != nil {
  73. glog.V(1).Infof("ShowMaintenanceQueue: error getting data: %v", res.err)
  74. c.JSON(http.StatusInternalServerError, gin.H{"error": res.err.Error()})
  75. return
  76. }
  77. glog.V(2).Infof("ShowMaintenanceQueue: got data with %d tasks", len(res.data.Tasks))
  78. // Render HTML template
  79. c.Header("Content-Type", "text/html")
  80. maintenanceComponent := app.MaintenanceQueue(res.data)
  81. layoutComponent := layout.Layout(c, maintenanceComponent)
  82. err := layoutComponent.Render(ctx, c.Writer)
  83. if err != nil {
  84. glog.V(1).Infof("ShowMaintenanceQueue: render error: %v", err)
  85. c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()})
  86. return
  87. }
  88. glog.V(3).Infof("ShowMaintenanceQueue: template rendered successfully")
  89. case <-ctx.Done():
  90. glog.Warningf("ShowMaintenanceQueue: timeout waiting for data")
  91. c.JSON(http.StatusRequestTimeout, gin.H{
  92. "error": "Request timeout - maintenance data retrieval took too long. This may indicate a system issue.",
  93. "suggestion": "Try refreshing the page or contact system administrator if the problem persists.",
  94. })
  95. return
  96. }
  97. }
  98. // ShowMaintenanceWorkers displays the maintenance workers page
  99. func (h *MaintenanceHandlers) ShowMaintenanceWorkers(c *gin.Context) {
  100. workersData, err := h.adminServer.GetMaintenanceWorkersData()
  101. if err != nil {
  102. c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  103. return
  104. }
  105. // Render HTML template
  106. c.Header("Content-Type", "text/html")
  107. workersComponent := app.MaintenanceWorkers(workersData)
  108. layoutComponent := layout.Layout(c, workersComponent)
  109. err = layoutComponent.Render(c.Request.Context(), c.Writer)
  110. if err != nil {
  111. c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()})
  112. return
  113. }
  114. }
  115. // ShowMaintenanceConfig displays the maintenance configuration page
  116. func (h *MaintenanceHandlers) ShowMaintenanceConfig(c *gin.Context) {
  117. config, err := h.getMaintenanceConfig()
  118. if err != nil {
  119. c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  120. return
  121. }
  122. // Get the schema for dynamic form rendering
  123. schema := maintenance.GetMaintenanceConfigSchema()
  124. // Render HTML template using schema-driven approach
  125. c.Header("Content-Type", "text/html")
  126. configComponent := app.MaintenanceConfigSchema(config, schema)
  127. layoutComponent := layout.Layout(c, configComponent)
  128. err = layoutComponent.Render(c.Request.Context(), c.Writer)
  129. if err != nil {
  130. c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()})
  131. return
  132. }
  133. }
  134. // ShowTaskConfig displays the configuration page for a specific task type
  135. func (h *MaintenanceHandlers) ShowTaskConfig(c *gin.Context) {
  136. taskTypeName := c.Param("taskType")
  137. // Get the schema for this task type
  138. schema := tasks.GetTaskConfigSchema(taskTypeName)
  139. if schema == nil {
  140. c.JSON(http.StatusNotFound, gin.H{"error": "Task type not found or no schema available"})
  141. return
  142. }
  143. // Get the UI provider for current configuration
  144. uiRegistry := tasks.GetGlobalUIRegistry()
  145. typesRegistry := tasks.GetGlobalTypesRegistry()
  146. var provider types.TaskUIProvider
  147. for workerTaskType := range typesRegistry.GetAllDetectors() {
  148. if string(workerTaskType) == taskTypeName {
  149. provider = uiRegistry.GetProvider(workerTaskType)
  150. break
  151. }
  152. }
  153. if provider == nil {
  154. c.JSON(http.StatusNotFound, gin.H{"error": "UI provider not found for task type"})
  155. return
  156. }
  157. // Get current configuration
  158. currentConfig := provider.GetCurrentConfig()
  159. // Note: Do NOT apply schema defaults to current config as it overrides saved values
  160. // Only apply defaults when creating new configs, not when displaying existing ones
  161. // Create task configuration data
  162. configData := &maintenance.TaskConfigData{
  163. TaskType: maintenance.MaintenanceTaskType(taskTypeName),
  164. TaskName: schema.DisplayName,
  165. TaskIcon: schema.Icon,
  166. Description: schema.Description,
  167. }
  168. // Render HTML template using schema-based approach
  169. c.Header("Content-Type", "text/html")
  170. taskConfigComponent := app.TaskConfigSchema(configData, schema, currentConfig)
  171. layoutComponent := layout.Layout(c, taskConfigComponent)
  172. err := layoutComponent.Render(c.Request.Context(), c.Writer)
  173. if err != nil {
  174. c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to render template: " + err.Error()})
  175. return
  176. }
  177. }
  178. // UpdateTaskConfig updates task configuration from form
  179. func (h *MaintenanceHandlers) UpdateTaskConfig(c *gin.Context) {
  180. taskTypeName := c.Param("taskType")
  181. taskType := types.TaskType(taskTypeName)
  182. // Parse form data
  183. err := c.Request.ParseForm()
  184. if err != nil {
  185. c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse form data: " + err.Error()})
  186. return
  187. }
  188. // Debug logging - show received form data
  189. glog.V(1).Infof("Received form data for task type %s:", taskTypeName)
  190. for key, values := range c.Request.PostForm {
  191. glog.V(1).Infof(" %s: %v", key, values)
  192. }
  193. // Get the task configuration schema
  194. schema := tasks.GetTaskConfigSchema(taskTypeName)
  195. if schema == nil {
  196. c.JSON(http.StatusNotFound, gin.H{"error": "Schema not found for task type: " + taskTypeName})
  197. return
  198. }
  199. // Create a new config instance based on task type and apply schema defaults
  200. var config TaskConfig
  201. switch taskType {
  202. case types.TaskTypeVacuum:
  203. config = &vacuum.Config{}
  204. case types.TaskTypeBalance:
  205. config = &balance.Config{}
  206. case types.TaskTypeErasureCoding:
  207. config = &erasure_coding.Config{}
  208. default:
  209. c.JSON(http.StatusBadRequest, gin.H{"error": "Unsupported task type: " + taskTypeName})
  210. return
  211. }
  212. // Apply schema defaults first using type-safe method
  213. if err := schema.ApplyDefaultsToConfig(config); err != nil {
  214. c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply defaults: " + err.Error()})
  215. return
  216. }
  217. // First, get the current configuration to preserve existing values
  218. currentUIRegistry := tasks.GetGlobalUIRegistry()
  219. currentTypesRegistry := tasks.GetGlobalTypesRegistry()
  220. var currentProvider types.TaskUIProvider
  221. for workerTaskType := range currentTypesRegistry.GetAllDetectors() {
  222. if string(workerTaskType) == string(taskType) {
  223. currentProvider = currentUIRegistry.GetProvider(workerTaskType)
  224. break
  225. }
  226. }
  227. if currentProvider != nil {
  228. // Copy current config values to the new config
  229. currentConfig := currentProvider.GetCurrentConfig()
  230. if currentConfigProtobuf, ok := currentConfig.(TaskConfig); ok {
  231. // Apply current values using protobuf directly - no map conversion needed!
  232. currentPolicy := currentConfigProtobuf.ToTaskPolicy()
  233. if err := config.FromTaskPolicy(currentPolicy); err != nil {
  234. glog.Warningf("Failed to load current config for %s: %v", taskTypeName, err)
  235. }
  236. }
  237. }
  238. // Parse form data using schema-based approach (this will override with new values)
  239. err = h.parseTaskConfigFromForm(c.Request.PostForm, schema, config)
  240. if err != nil {
  241. c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse configuration: " + err.Error()})
  242. return
  243. }
  244. // Debug logging - show parsed config values
  245. switch taskType {
  246. case types.TaskTypeVacuum:
  247. if vacuumConfig, ok := config.(*vacuum.Config); ok {
  248. glog.V(1).Infof("Parsed vacuum config - GarbageThreshold: %f, MinVolumeAgeSeconds: %d, MinIntervalSeconds: %d",
  249. vacuumConfig.GarbageThreshold, vacuumConfig.MinVolumeAgeSeconds, vacuumConfig.MinIntervalSeconds)
  250. }
  251. case types.TaskTypeErasureCoding:
  252. if ecConfig, ok := config.(*erasure_coding.Config); ok {
  253. glog.V(1).Infof("Parsed EC config - FullnessRatio: %f, QuietForSeconds: %d, MinSizeMB: %d, CollectionFilter: '%s'",
  254. ecConfig.FullnessRatio, ecConfig.QuietForSeconds, ecConfig.MinSizeMB, ecConfig.CollectionFilter)
  255. }
  256. case types.TaskTypeBalance:
  257. if balanceConfig, ok := config.(*balance.Config); ok {
  258. glog.V(1).Infof("Parsed balance config - Enabled: %v, MaxConcurrent: %d, ScanIntervalSeconds: %d, ImbalanceThreshold: %f, MinServerCount: %d",
  259. balanceConfig.Enabled, balanceConfig.MaxConcurrent, balanceConfig.ScanIntervalSeconds, balanceConfig.ImbalanceThreshold, balanceConfig.MinServerCount)
  260. }
  261. }
  262. // Validate the configuration
  263. if validationErrors := schema.ValidateConfig(config); len(validationErrors) > 0 {
  264. errorMessages := make([]string, len(validationErrors))
  265. for i, err := range validationErrors {
  266. errorMessages[i] = err.Error()
  267. }
  268. c.JSON(http.StatusBadRequest, gin.H{"error": "Configuration validation failed", "details": errorMessages})
  269. return
  270. }
  271. // Apply configuration using UIProvider
  272. uiRegistry := tasks.GetGlobalUIRegistry()
  273. typesRegistry := tasks.GetGlobalTypesRegistry()
  274. var provider types.TaskUIProvider
  275. for workerTaskType := range typesRegistry.GetAllDetectors() {
  276. if string(workerTaskType) == string(taskType) {
  277. provider = uiRegistry.GetProvider(workerTaskType)
  278. break
  279. }
  280. }
  281. if provider == nil {
  282. c.JSON(http.StatusNotFound, gin.H{"error": "UI provider not found for task type"})
  283. return
  284. }
  285. // Apply configuration using provider
  286. err = provider.ApplyTaskConfig(config)
  287. if err != nil {
  288. c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply configuration: " + err.Error()})
  289. return
  290. }
  291. // Save task configuration to protobuf file using ConfigPersistence
  292. if h.adminServer != nil && h.adminServer.GetConfigPersistence() != nil {
  293. err = h.saveTaskConfigToProtobuf(taskType, config)
  294. if err != nil {
  295. glog.Warningf("Failed to save task config to protobuf file: %v", err)
  296. // Don't fail the request, just log the warning
  297. }
  298. }
  299. // Trigger a configuration reload in the maintenance manager
  300. if h.adminServer != nil {
  301. if manager := h.adminServer.GetMaintenanceManager(); manager != nil {
  302. err = manager.ReloadTaskConfigurations()
  303. if err != nil {
  304. glog.Warningf("Failed to reload task configurations: %v", err)
  305. } else {
  306. glog.V(1).Infof("Successfully reloaded task configurations after updating %s", taskTypeName)
  307. }
  308. }
  309. }
  310. // Redirect back to task configuration page
  311. c.Redirect(http.StatusSeeOther, "/maintenance/config/"+taskTypeName)
  312. }
  313. // parseTaskConfigFromForm parses form data using schema definitions
  314. func (h *MaintenanceHandlers) parseTaskConfigFromForm(formData map[string][]string, schema *tasks.TaskConfigSchema, config interface{}) error {
  315. configValue := reflect.ValueOf(config)
  316. if configValue.Kind() == reflect.Ptr {
  317. configValue = configValue.Elem()
  318. }
  319. if configValue.Kind() != reflect.Struct {
  320. return fmt.Errorf("config must be a struct or pointer to struct")
  321. }
  322. configType := configValue.Type()
  323. for i := 0; i < configValue.NumField(); i++ {
  324. field := configValue.Field(i)
  325. fieldType := configType.Field(i)
  326. // Handle embedded structs recursively
  327. if fieldType.Anonymous && field.Kind() == reflect.Struct {
  328. err := h.parseTaskConfigFromForm(formData, schema, field.Addr().Interface())
  329. if err != nil {
  330. return fmt.Errorf("error parsing embedded struct %s: %w", fieldType.Name, err)
  331. }
  332. continue
  333. }
  334. // Get JSON tag name
  335. jsonTag := fieldType.Tag.Get("json")
  336. if jsonTag == "" {
  337. continue
  338. }
  339. // Remove options like ",omitempty"
  340. if commaIdx := strings.Index(jsonTag, ","); commaIdx > 0 {
  341. jsonTag = jsonTag[:commaIdx]
  342. }
  343. // Find corresponding schema field
  344. schemaField := schema.GetFieldByName(jsonTag)
  345. if schemaField == nil {
  346. continue
  347. }
  348. // Parse value based on field type
  349. if err := h.parseFieldFromForm(formData, schemaField, field); err != nil {
  350. return fmt.Errorf("error parsing field %s: %w", schemaField.DisplayName, err)
  351. }
  352. }
  353. return nil
  354. }
  355. // parseFieldFromForm parses a single field value from form data
  356. func (h *MaintenanceHandlers) parseFieldFromForm(formData map[string][]string, schemaField *config.Field, fieldValue reflect.Value) error {
  357. if !fieldValue.CanSet() {
  358. return nil
  359. }
  360. switch schemaField.Type {
  361. case config.FieldTypeBool:
  362. // Checkbox fields - present means true, absent means false
  363. _, exists := formData[schemaField.JSONName]
  364. fieldValue.SetBool(exists)
  365. case config.FieldTypeInt:
  366. if values, ok := formData[schemaField.JSONName]; ok && len(values) > 0 {
  367. if intVal, err := strconv.Atoi(values[0]); err != nil {
  368. return fmt.Errorf("invalid integer value: %s", values[0])
  369. } else {
  370. fieldValue.SetInt(int64(intVal))
  371. }
  372. }
  373. case config.FieldTypeFloat:
  374. if values, ok := formData[schemaField.JSONName]; ok && len(values) > 0 {
  375. if floatVal, err := strconv.ParseFloat(values[0], 64); err != nil {
  376. return fmt.Errorf("invalid float value: %s", values[0])
  377. } else {
  378. fieldValue.SetFloat(floatVal)
  379. }
  380. }
  381. case config.FieldTypeString:
  382. if values, ok := formData[schemaField.JSONName]; ok && len(values) > 0 {
  383. fieldValue.SetString(values[0])
  384. }
  385. case config.FieldTypeInterval:
  386. // Parse interval fields with value + unit
  387. valueKey := schemaField.JSONName + "_value"
  388. unitKey := schemaField.JSONName + "_unit"
  389. if valueStrs, ok := formData[valueKey]; ok && len(valueStrs) > 0 {
  390. value, err := strconv.Atoi(valueStrs[0])
  391. if err != nil {
  392. return fmt.Errorf("invalid interval value: %s", valueStrs[0])
  393. }
  394. unit := "minutes" // default
  395. if unitStrs, ok := formData[unitKey]; ok && len(unitStrs) > 0 {
  396. unit = unitStrs[0]
  397. }
  398. // Convert to seconds
  399. seconds := config.IntervalValueUnitToSeconds(value, unit)
  400. fieldValue.SetInt(int64(seconds))
  401. }
  402. default:
  403. return fmt.Errorf("unsupported field type: %s", schemaField.Type)
  404. }
  405. return nil
  406. }
  407. // UpdateMaintenanceConfig updates maintenance configuration from form
  408. func (h *MaintenanceHandlers) UpdateMaintenanceConfig(c *gin.Context) {
  409. var config maintenance.MaintenanceConfig
  410. if err := c.ShouldBind(&config); err != nil {
  411. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  412. return
  413. }
  414. err := h.updateMaintenanceConfig(&config)
  415. if err != nil {
  416. c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  417. return
  418. }
  419. c.Redirect(http.StatusSeeOther, "/maintenance/config")
  420. }
  421. // Helper methods that delegate to AdminServer
  422. func (h *MaintenanceHandlers) getMaintenanceQueueData() (*maintenance.MaintenanceQueueData, error) {
  423. tasks, err := h.getMaintenanceTasks()
  424. if err != nil {
  425. return nil, err
  426. }
  427. workers, err := h.getMaintenanceWorkers()
  428. if err != nil {
  429. return nil, err
  430. }
  431. stats, err := h.getMaintenanceQueueStats()
  432. if err != nil {
  433. return nil, err
  434. }
  435. data := &maintenance.MaintenanceQueueData{
  436. Tasks: tasks,
  437. Workers: workers,
  438. Stats: stats,
  439. LastUpdated: time.Now(),
  440. }
  441. return data, nil
  442. }
  443. func (h *MaintenanceHandlers) getMaintenanceQueueStats() (*maintenance.QueueStats, error) {
  444. // Use the exported method from AdminServer
  445. return h.adminServer.GetMaintenanceQueueStats()
  446. }
  447. func (h *MaintenanceHandlers) getMaintenanceTasks() ([]*maintenance.MaintenanceTask, error) {
  448. // Call the maintenance manager directly to get recent tasks (limit for performance)
  449. if h.adminServer == nil {
  450. return []*maintenance.MaintenanceTask{}, nil
  451. }
  452. manager := h.adminServer.GetMaintenanceManager()
  453. if manager == nil {
  454. return []*maintenance.MaintenanceTask{}, nil
  455. }
  456. // Get recent tasks only (last 100) to prevent slow page loads
  457. // Users can view more tasks via pagination if needed
  458. allTasks := manager.GetTasks("", "", 100)
  459. return allTasks, nil
  460. }
  461. func (h *MaintenanceHandlers) getMaintenanceWorkers() ([]*maintenance.MaintenanceWorker, error) {
  462. // Get workers from the admin server's maintenance manager
  463. if h.adminServer == nil {
  464. return []*maintenance.MaintenanceWorker{}, nil
  465. }
  466. if h.adminServer.GetMaintenanceManager() == nil {
  467. return []*maintenance.MaintenanceWorker{}, nil
  468. }
  469. // Get workers from the maintenance manager
  470. workers := h.adminServer.GetMaintenanceManager().GetWorkers()
  471. return workers, nil
  472. }
  473. func (h *MaintenanceHandlers) getMaintenanceConfig() (*maintenance.MaintenanceConfigData, error) {
  474. // Delegate to AdminServer's real persistence method
  475. return h.adminServer.GetMaintenanceConfigData()
  476. }
  477. func (h *MaintenanceHandlers) updateMaintenanceConfig(config *maintenance.MaintenanceConfig) error {
  478. // Delegate to AdminServer's real persistence method
  479. return h.adminServer.UpdateMaintenanceConfigData(config)
  480. }
  481. // saveTaskConfigToProtobuf saves task configuration to protobuf file
  482. func (h *MaintenanceHandlers) saveTaskConfigToProtobuf(taskType types.TaskType, config TaskConfig) error {
  483. configPersistence := h.adminServer.GetConfigPersistence()
  484. if configPersistence == nil {
  485. return fmt.Errorf("config persistence not available")
  486. }
  487. // Use the new ToTaskPolicy method - much simpler and more maintainable!
  488. taskPolicy := config.ToTaskPolicy()
  489. // Save using task-specific methods
  490. switch taskType {
  491. case types.TaskTypeVacuum:
  492. return configPersistence.SaveVacuumTaskPolicy(taskPolicy)
  493. case types.TaskTypeErasureCoding:
  494. return configPersistence.SaveErasureCodingTaskPolicy(taskPolicy)
  495. case types.TaskTypeBalance:
  496. return configPersistence.SaveBalanceTaskPolicy(taskPolicy)
  497. default:
  498. return fmt.Errorf("unsupported task type for protobuf persistence: %s", taskType)
  499. }
  500. }