diff options
| author | 2026-06-12 18:41:58 +0000 | |
|---|---|---|
| committer | 2026-06-12 18:41:58 +0000 | |
| commit | 7faa841b581dbbacf563a6ca3167efbfd697d37c (patch) | |
| tree | ab54845bcf708b1099f88a339d18bdf1cdb6f23f /object/store/packed/quarantine.go | |
| parent | object/store/packed: Add missing t.Helper (diff) | |
object/store/packed: Add basic ingestion
Diffstat (limited to 'object/store/packed/quarantine.go')
| -rw-r--r-- | object/store/packed/quarantine.go | 215 |
1 files changed, 161 insertions, 54 deletions
diff --git a/object/store/packed/quarantine.go b/object/store/packed/quarantine.go index fac64402..5e0b85cb 100644 --- a/object/store/packed/quarantine.go +++ b/object/store/packed/quarantine.go @@ -1,57 +1,164 @@ package packed -// import ( -// "os" -// -// "lindenii.org/go/furgit/object/store" -// ) -// -// var ( -// _ store.PackQuarantiner = (*Packed)(nil) -// _ store.PackQuarantine = (*packQuarantine)(nil) -// ) -// -// // packQuarantine is one quarantined packed store -// // rooted privately beneath a destination pack root. -// type packQuarantine struct { -// *Packed -// -// parent *Packed -// tempName string -// tempRoot *os.Root -// } -// -// // BeginPackQuarantine creates one quarantined packed store -// // rooted privately beneath the destination pack root. -// // -// // Labels: Deps-Borrowed, Life-Parent, Close-No. -// func (packed *Packed) BeginPackQuarantine(opts store.PackQuarantineOptions) (store.PackQuarantine, error) -// -// // Discard removes the quarantine -// // and invalidates the receiver. -// func (quarantine *packQuarantine) Discard() error -// -// // Promote publishes all finalized pack artifacts in the quarantine -// // into the parent packed store, -// // and invalidates the receiver. -// func (quarantine *packQuarantine) Promote() error -// -// // promoteAll links every pack artifact in the quarantine -// // into the parent packed store, -// // in pack/rev/idx dependency order. -// func (quarantine *packQuarantine) promoteAll() error -// -// // promoteFile links one quarantined pack artifact -// // into the parent packed store, -// // treating an already-present destination as success. -// func (quarantine *packQuarantine) promoteFile(name string) error -// -// // createPackQuarantineRoot creates a private quarantine directory -// // beneath parent, -// // and returns its name and an os.Root over it. -// func createPackQuarantineRoot(parent *os.Root) (string, *os.Root, error) +import ( + "crypto/rand" + "errors" + "fmt" + "io/fs" + "os" + "slices" + "strings" + + "lindenii.org/go/furgit/object/store" +) + +var ( + _ store.PackQuarantiner = (*Packed)(nil) + _ store.PackQuarantine = (*packQuarantine)(nil) +) + +var errQuarantineNamesExhausted = errors.New("object/store/packed: exhausted quarantine directory names") + +// packQuarantine is one quarantined packed store +// rooted privately beneath a destination pack root. +type packQuarantine struct { + *Packed + + parent *Packed + + tempName string + tempRoot *os.Root +} + +// BeginPackQuarantine creates one quarantined packed store +// rooted privately beneath the destination pack root. // -// // packPromotionPriority orders pack artifacts -// // so that data files are linked -// // before the index that publishes them. -// func packPromotionPriority(name string) int +// Labels: Deps-Borrowed, Life-Parent. +func (packed *Packed) BeginPackQuarantine(_ store.PackQuarantineOptions) (store.PackQuarantine, error) { //nolint:ireturn + tempName, tempRoot, err := createPackQuarantineRoot(packed.root) + if err != nil { + return nil, err + } + + quarantineStore, err := New(tempRoot, packed.objectFormat) + if err != nil { + _ = tempRoot.Close() + _ = packed.root.RemoveAll(tempName) + + return nil, err + } + + return &packQuarantine{ + Packed: quarantineStore, + parent: packed, + tempName: tempName, + tempRoot: tempRoot, + }, nil +} + +// Promote publishes the quarantined pack artifacts into the parent store, +// refreshes the parent so the objects become available, +// and invalidates the receiver. +func (quarantine *packQuarantine) Promote() error { + closeErr := quarantine.Close() + promoteErr := quarantine.promoteAll() + + var refreshErr error + if promoteErr == nil { + refreshErr = quarantine.parent.Refresh() + } + + tempRootErr := quarantine.tempRoot.Close() + removeErr := quarantine.parent.root.RemoveAll(quarantine.tempName) + + return errors.Join(closeErr, promoteErr, refreshErr, tempRootErr, removeErr) +} + +// Discard removes the quarantine and invalidates the receiver. +func (quarantine *packQuarantine) Discard() error { + closeErr := quarantine.Close() + tempRootErr := quarantine.tempRoot.Close() + removeErr := quarantine.parent.root.RemoveAll(quarantine.tempName) + + return errors.Join(closeErr, tempRootErr, removeErr) +} + +// promoteAll links every pack artifact in the quarantine into the parent store, +// in pack/rev/idx dependency order. +func (quarantine *packQuarantine) promoteAll() error { + entries, err := fs.ReadDir(quarantine.tempRoot.FS(), ".") + if err != nil { + return fmt.Errorf("object/store/packed: %w", err) + } + + slices.SortFunc(entries, func(left, right fs.DirEntry) int { + return packPromotionPriority(left.Name()) - packPromotionPriority(right.Name()) + }) + + for _, entry := range entries { + err := quarantine.promoteFile(entry.Name()) + if err != nil { + return err + } + } + + return nil +} + +// promoteFile links one quarantined artifact into the parent store, +// treating an already-present destination as success. +func (quarantine *packQuarantine) promoteFile(name string) error { + src := quarantine.tempName + "/" + name + + err := quarantine.parent.root.Link(src, name) + if err != nil && !errors.Is(err, fs.ErrExist) { + return fmt.Errorf("object/store/packed: promoting %q: %w", name, err) + } + + _ = quarantine.parent.root.Remove(src) + + return nil +} + +// createPackQuarantineRoot creates a private quarantine directory beneath parent +// and returns its name and an os.Root over it. +func createPackQuarantineRoot(parent *os.Root) (string, *os.Root, error) { + for range 32 { + name := "tmp_packq_" + rand.Text() + + err := parent.Mkdir(name, 0o700) + if err != nil { + if errors.Is(err, fs.ErrExist) { + continue + } + + return "", nil, fmt.Errorf("object/store/packed: %w", err) + } + + root, err := parent.OpenRoot(name) + if err != nil { + _ = parent.RemoveAll(name) + + return "", nil, fmt.Errorf("object/store/packed: %w", err) + } + + return name, root, nil + } + + return "", nil, errQuarantineNamesExhausted +} + +// packPromotionPriority orders pack artifacts +// so that data files are linked before the index that publishes them. +func packPromotionPriority(name string) int { + switch { + case strings.HasPrefix(name, "pack-") && strings.HasSuffix(name, ".pack"): + return 1 + case strings.HasPrefix(name, "pack-") && strings.HasSuffix(name, ".rev"): + return 2 + case strings.HasPrefix(name, "pack-") && strings.HasSuffix(name, ".idx"): + return 3 + default: + return 0 + } +} |
