Agent configuration (agent-<role>.yaml, strict decoding, role section passed undecoded to the role factory) and the state directory of ADR 0011: identity with interrupted-renewal recovery, verified revisions under revisions/<n>/, state.json with apply results, lease sequence and queued deployment reports, atomic writes, pruning and a lock file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
33 lines
1.0 KiB
Go
33 lines
1.0 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//go:build linux || darwin || freebsd || netbsd || openbsd || dragonfly
|
|
|
|
package core
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
)
|
|
|
|
// lockPath takes an exclusive, non-blocking flock on path. The lock is
|
|
// released by the returned function or when the process exits.
|
|
func lockPath(path string) (func(), error) {
|
|
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600) //nolint:gosec // path built from the state directory
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open lock file: %w", err)
|
|
}
|
|
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil { //nolint:gosec // file descriptors fit into int
|
|
_ = f.Close()
|
|
if errors.Is(err, syscall.EWOULDBLOCK) {
|
|
return nil, fmt.Errorf("state directory is locked by another agent process (%s)", path)
|
|
}
|
|
return nil, fmt.Errorf("lock state directory: %w", err)
|
|
}
|
|
return func() {
|
|
_ = syscall.Flock(int(f.Fd()), syscall.LOCK_UN) //nolint:gosec // file descriptors fit into int
|
|
_ = f.Close()
|
|
}, nil
|
|
}
|