diff options
Diffstat (limited to 'internal/admin/yaml_util.go')
| -rw-r--r-- | internal/admin/yaml_util.go | 36 |
1 files changed, 23 insertions, 13 deletions
diff --git a/internal/admin/yaml_util.go b/internal/admin/yaml_util.go index 44712a3..8cd3252 100644 --- a/internal/admin/yaml_util.go +++ b/internal/admin/yaml_util.go @@ -194,11 +194,14 @@ func moveEntityFile(dataDir, subdir, id, targetDir string) (string, string, []by if _, err := os.Stat(newPath); err == nil { return "", "", nil, fmt.Errorf("entity already exists in target directory") } - if err := os.WriteFile(newPath, data, 0644); err != nil { - return "", "", nil, fmt.Errorf("write: %w", err) + // Move in one journaled transaction: a failed write or source removal + // must not leave the entity in two directories. + txn := persistence.NewTransaction(filepath.Join(dataDir, ".journal")) + if err := txn.Move(oldPath, newPath); err != nil { + return "", "", nil, fmt.Errorf("move: %w", err) } - if err := os.Remove(oldPath); err != nil { - return "", "", nil, fmt.Errorf("remove old: %w", err) + if err := txn.Commit(); err != nil { + return "", "", nil, fmt.Errorf("move: %w", err) } return oldPath, newPath, data, nil } @@ -253,11 +256,14 @@ func renameEntityFile(dataDir, subdir, oldID, newID string) (string, string, []b if err != nil { return "", "", nil, fmt.Errorf("read: %w", err) } - if err := os.WriteFile(newPath, data, 0644); err != nil { - return "", "", nil, fmt.Errorf("write: %w", err) + // Rename in one journaled transaction: a failed write or source removal + // must not leave the entity in two directories. + txn := persistence.NewTransaction(filepath.Join(dataDir, ".journal")) + if err := txn.Move(oldPath, newPath); err != nil { + return "", "", nil, fmt.Errorf("rename: %w", err) } - if err := os.Remove(oldPath); err != nil { - return "", "", nil, fmt.Errorf("remove old: %w", err) + if err := txn.Commit(); err != nil { + return "", "", nil, fmt.Errorf("rename: %w", err) } return oldPath, newPath, data, nil } @@ -308,6 +314,7 @@ func deleteEntityDir(dataDir, subdir, dir string) ([]ChangeDesc, error) { } var changes []ChangeDesc + txn := persistence.NewTransaction(filepath.Join(dataDir, ".journal")) for _, e := range entries { if e.IsDir() || filepath.Ext(e.Name()) != ".yaml" { continue @@ -318,11 +325,8 @@ func deleteEntityDir(dataDir, subdir, dir string) ([]ChangeDesc, error) { if err != nil { return nil, fmt.Errorf("read %s: %w", e.Name(), err) } - if err := os.WriteFile(newPath, data, 0644); err != nil { - return nil, fmt.Errorf("write %s: %w", e.Name(), err) - } - if err := os.Remove(oldPath); err != nil { - return nil, fmt.Errorf("remove %s: %w", e.Name(), err) + if err := txn.Move(oldPath, newPath); err != nil { + return nil, fmt.Errorf("plan move %s: %w", e.Name(), err) } changes = append(changes, ChangeDesc{ Description: fmt.Sprintf("move %s %s to root", subdir, e.Name()), @@ -333,6 +337,12 @@ func deleteEntityDir(dataDir, subdir, dir string) ([]ChangeDesc, error) { }) } + // Move every file in one journaled transaction so a failure cannot leave + // the directory half-emptied (some files moved, some not). + if err := txn.Commit(); err != nil { + return nil, fmt.Errorf("move files: %w", err) + } + if err := os.Remove(srcDir); err != nil { return nil, fmt.Errorf("remove directory: %w", err) } |
