History

fix(management): improve error handling and normalize YAML comment indentation

Enhance error management for file operations and clean up temporary files. Add `NormalizeCommentIndentation` function to ensure YAML comments maintain consistent formatting.

Luis Pater committed 1 year agov6.3.32

Commit dc804e91 parent ab76cb32 files · +58 −20Browse files

Unified · Split

2 files
  1. internal/api/handlers/management/config_basic.go +19 −16
  2. internal/config/config.go +39 −4
internal/api/handlers/management/config_basic.go+19−16
@@ -28,7 +28,7 @@ func (h *Handler) GetConfigYAML(c *gin.Context) { · 27 unmodified lines
2828 return
2929 }
3030 var node yaml.Node
31− if err := yaml.Unmarshal(data, &node); err != nil {
31+ if err = yaml.Unmarshal(data, &node); err != nil {
3232 c.JSON(http.StatusInternalServerError, gin.H{"error": "parse_failed", "message": err.Error()})
3333 return
3434 }
@@ -41,17 +41,18 @@ func (h *Handler) GetConfigYAML(c *gin.Context) { · 6 unmodified lines
4141 }
4242
4343 func WriteConfig(path string, data []byte) error {
44+ data = config.NormalizeCommentIndentation(data)
4445 f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
4546 if err != nil {
4647 return err
4748 }
48− if _, err := f.Write(data); err != nil {
49− f.Close()
50− return err
49+ if _, errWrite := f.Write(data); errWrite != nil {
50+ _ = f.Close()
51+ return errWrite
5152 }
52− if err := f.Sync(); err != nil {
53− f.Close()
54− return err
53+ if errSync := f.Sync(); errSync != nil {
54+ _ = f.Close()
55+ return errSync
5556 }
5657 return f.Close()
5758 }
@@ -63,7 +64,7 @@ func (h *Handler) PutConfigYAML(c *gin.Context) { · 5 unmodified lines
6364 return
6465 }
6566 var cfg config.Config
66− if err := yaml.Unmarshal(body, &cfg); err != nil {
67+ if err = yaml.Unmarshal(body, &cfg); err != nil {
6768 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_yaml", "message": err.Error()})
6869 return
6970 }
@@ -75,18 +76,20 @@ func (h *Handler) PutConfigYAML(c *gin.Context) { · 5 unmodified lines
7576 return
7677 }
7778 tempFile := tmpFile.Name()
78− if _, err := tmpFile.Write(body); err != nil {
79− tmpFile.Close()
80− os.Remove(tempFile)
81− c.JSON(http.StatusInternalServerError, gin.H{"error": "write_failed", "message": err.Error()})
79+ if _, errWrite := tmpFile.Write(body); errWrite != nil {
80+ _ = tmpFile.Close()
81+ _ = os.Remove(tempFile)
82+ c.JSON(http.StatusInternalServerError, gin.H{"error": "write_failed", "message": errWrite.Error()})
8283 return
8384 }
84− if err := tmpFile.Close(); err != nil {
85− os.Remove(tempFile)
86− c.JSON(http.StatusInternalServerError, gin.H{"error": "write_failed", "message": err.Error()})
85+ if errClose := tmpFile.Close(); errClose != nil {
86+ _ = os.Remove(tempFile)
87+ c.JSON(http.StatusInternalServerError, gin.H{"error": "write_failed", "message": errClose.Error()})
8788 return
8889 }
89− defer os.Remove(tempFile)
90+ defer func() {
91+ _ = os.Remove(tempFile)
92+ }()
9093 _, err = config.LoadConfigOptional(tempFile, false)
9194 if err != nil {
9295 c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "invalid_config", "message": err.Error()})
internal/config/config.go+39−4
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 package config
66
77 import (
8+ "bytes"
89 "errors"
910 "fmt"
1011 "os"
@@ -462,13 +463,19 @@ func SaveConfigPreserveComments(configFile string, cfg *Config) error { · 451 unmodified lines
462463 return err
463464 }
464465 defer func() { _ = f.Close() }()
465− enc := yaml.NewEncoder(f)
466+ var buf bytes.Buffer
467+ enc := yaml.NewEncoder(&buf)
466468 enc.SetIndent(2)
467469 if err = enc.Encode(&original); err != nil {
468470 _ = enc.Close()
469471 return err
470472 }
471− return enc.Close()
473+ if err = enc.Close(); err != nil {
474+ return err
475+ }
476+ data = NormalizeCommentIndentation(buf.Bytes())
477+ _, err = f.Write(data)
478+ return err
472479 }
473480
474481 func sanitizeConfigForPersist(cfg *Config) *Config {
@@ -518,13 +525,40 @@ func SaveConfigPreserveCommentsUpdateNestedScalar(configFile string, path []stri · 43 unmodified lines
518525 return err
519526 }
520527 defer func() { _ = f.Close() }()
521− enc := yaml.NewEncoder(f)
528+ var buf bytes.Buffer
529+ enc := yaml.NewEncoder(&buf)
522530 enc.SetIndent(2)
523531 if err = enc.Encode(&root); err != nil {
524532 _ = enc.Close()
525533 return err
526534 }
527− return enc.Close()
535+ if err = enc.Close(); err != nil {
536+ return err
537+ }
538+ data = NormalizeCommentIndentation(buf.Bytes())
539+ _, err = f.Write(data)
540+ return err
541+}
542+
543+// NormalizeCommentIndentation removes indentation from standalone YAML comment lines to keep them left aligned.
544+func NormalizeCommentIndentation(data []byte) []byte {
545+ lines := bytes.Split(data, []byte("\n"))
546+ changed := false
547+ for i, line := range lines {
548+ trimmed := bytes.TrimLeft(line, " \t")
549+ if len(trimmed) == 0 || trimmed[0] != '#' {
550+ continue
551+ }
552+ if len(trimmed) == len(line) {
553+ continue
554+ }
555+ lines[i] = append([]byte(nil), trimmed...)
556+ changed = true
557+ }
558+ if !changed {
559+ return data
560+ }
561+ return bytes.Join(lines, []byte("\n"))
528562 }
529563
530564 // getOrCreateMapValue finds the value node for a given key in a mapping node.
@@ -766,6 +800,7 @@ func matchSequenceElement(original []*yaml.Node, used []bool, target *yaml.Node) · 235 unmodified lines
766800 }
767801 }
768802 }
803+ default:
769804 }
770805 // Fallback to structural equality to preserve nodes lacking explicit identifiers.
771806 for i := range original {