Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
3.0 KiB
Go
113 lines
3.0 KiB
Go
package bundle
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// WriteExtractedFiles writes payload files from a verified bundle under root (directories created as needed).
|
|
func WriteExtractedFiles(root string, v *VerifiedContents) error {
|
|
return WriteExtractedFilesWithLimits(root, v, Limits{})
|
|
}
|
|
|
|
// WriteExtractedFilesWithLimits confines all filesystem operations to an open
|
|
// directory handle. Replacing existing files uses unlink + exclusive creation,
|
|
// so existing hard links and symlinks cannot redirect writes outside the root.
|
|
func WriteExtractedFilesWithLimits(root string, v *VerifiedContents, limits Limits) error {
|
|
l, err := limits.Normalized()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if v == nil {
|
|
return fmt.Errorf("bundle: nil verified contents")
|
|
}
|
|
if len(v.Manifest.Files) > l.MaxEntries-2 {
|
|
return ErrLimit
|
|
}
|
|
seen := map[string]bool{}
|
|
var total int64
|
|
for _, fe := range v.Manifest.Files {
|
|
if err := validatePath(fe.Path, l.MaxPathBytes); err != nil {
|
|
return err
|
|
}
|
|
if seen[strings.ToLower(fe.Path)] {
|
|
return fmt.Errorf("bundle: duplicate path %q", fe.Path)
|
|
}
|
|
seen[strings.ToLower(fe.Path)] = true
|
|
data, ok := v.Files[fe.Path]
|
|
if !ok {
|
|
return fmt.Errorf("bundle: missing file %q", fe.Path)
|
|
}
|
|
if int64(len(data)) > l.MaxFileBytes || int64(len(data)) > l.MaxExpandedBytes-total {
|
|
return ErrLimit
|
|
}
|
|
total += int64(len(data))
|
|
}
|
|
root = filepath.Clean(root)
|
|
if err := os.MkdirAll(root, 0o755); err != nil {
|
|
return err
|
|
}
|
|
confined, err := os.OpenRoot(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = confined.Close() }()
|
|
for _, fe := range v.Manifest.Files {
|
|
data, ok := v.Files[fe.Path]
|
|
if !ok {
|
|
return fmt.Errorf("bundle: missing file %q", fe.Path)
|
|
}
|
|
parts := strings.Split(fe.Path, "/")
|
|
for i := 1; i < len(parts); i++ {
|
|
dir := filepath.FromSlash(strings.Join(parts[:i], "/"))
|
|
if err := confined.Mkdir(dir, 0o755); err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
info, err := confined.Lstat(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
|
return fmt.Errorf("bundle: unsafe directory %q", dir)
|
|
}
|
|
}
|
|
dest := filepath.FromSlash(fe.Path)
|
|
info, err := confined.Lstat(dest)
|
|
if err == nil {
|
|
if !info.Mode().IsRegular() {
|
|
return fmt.Errorf("bundle: unsafe destination %q", dest)
|
|
}
|
|
if err = confined.Remove(dest); err != nil {
|
|
return err
|
|
}
|
|
} else if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
f, err := confined.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, writeErr := f.Write(data)
|
|
closeErr := f.Close()
|
|
if writeErr != nil {
|
|
return writeErr
|
|
}
|
|
if closeErr != nil {
|
|
return closeErr
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FindMainBirdConf returns the first path ending with bird.conf from extracted manifest order.
|
|
func (v *VerifiedContents) FindMainBirdConf() string {
|
|
for _, fe := range v.Manifest.Files {
|
|
if strings.HasSuffix(fe.Path, "bird.conf") {
|
|
return fe.Path
|
|
}
|
|
}
|
|
return ""
|
|
}
|