Make run identities collision-resistant and outputs exclusive

This commit is contained in:
2026-07-18 14:21:26 +00:00
parent a39eea7ed6
commit 2111e01142
9 changed files with 367 additions and 75 deletions

View File

@@ -9,47 +9,49 @@ import (
"time"
)
const maxCreateAttempts = 16
var utcNow = func() time.Time { return time.Now().UTC() }
type Bundle struct {
path, summaryRoot, traceRoot string
createdAt time.Time
}
func Allocate(parent string) (*Bundle, error) {
func Allocate(parent, runID string, startedAt time.Time) (*Bundle, error) {
parent = strings.TrimSpace(parent)
if parent == "" {
return nil, fmt.Errorf("debug parent must not be empty")
}
if err := validateRunID(runID); err != nil {
return nil, err
}
if err := os.MkdirAll(parent, 0o700); err != nil {
return nil, fmt.Errorf("create debug parent %q: %w", parent, err)
}
var last string
for attempt := 0; attempt < maxCreateAttempts; attempt++ {
createdAt := utcNow()
runID := fmt.Sprintf("run-%d", createdAt.UnixNano())
path := filepath.Join(parent, runID)
last = path
if err := os.Mkdir(path, 0o700); err != nil {
if os.IsExist(err) {
continue
}
return nil, fmt.Errorf("create debug bundle %q: %w", path, err)
path := filepath.Join(parent, runID)
if err := os.Mkdir(path, 0o700); err != nil {
if os.IsExist(err) {
return nil, fmt.Errorf("debug bundle %q already exists", path)
}
summary, trace := filepath.Join(path, "summary"), filepath.Join(path, "trace")
if err := os.Mkdir(summary, 0o700); err != nil {
_ = os.Remove(path)
return nil, fmt.Errorf("create debug summary %q: %w", summary, err)
}
if err := os.Mkdir(trace, 0o700); err != nil {
_ = os.RemoveAll(path)
return nil, fmt.Errorf("create debug trace %q: %w", trace, err)
}
return &Bundle{path: path, summaryRoot: summary, traceRoot: trace, createdAt: createdAt}, nil
return nil, fmt.Errorf("create debug bundle %q: %w", path, err)
}
return nil, fmt.Errorf("create debug bundle %q: exhausted unique run ID attempts", last)
summary, trace := filepath.Join(path, "summary"), filepath.Join(path, "trace")
if err := os.Mkdir(summary, 0o700); err != nil {
_ = os.Remove(path)
return nil, fmt.Errorf("create debug summary %q: %w", summary, err)
}
if err := os.Mkdir(trace, 0o700); err != nil {
_ = os.RemoveAll(path)
return nil, fmt.Errorf("create debug trace %q: %w", trace, err)
}
return &Bundle{path: path, summaryRoot: summary, traceRoot: trace, createdAt: startedAt}, nil
}
func validateRunID(runID string) error {
if runID == "" {
return fmt.Errorf("debug run ID must not be empty")
}
if runID != strings.TrimSpace(runID) || strings.ContainsAny(runID, `/\\`) || filepath.IsAbs(runID) || filepath.Clean(runID) != runID || runID == "." || runID == ".." {
return fmt.Errorf("debug run ID %q must be one safe path component", runID)
}
return nil
}
func (b *Bundle) Path() string {
if b == nil {