@@ -3,7 +3,6 @@ package agent
33import (
44 "bufio"
55 "context"
6- "crypto/rand"
76 "fmt"
87 "os"
98 "os/exec"
@@ -25,106 +24,27 @@ type Worker struct {
2524 docker DockerConfig
2625 vcsOpts vcs.Options
2726 pollInterval time.Duration
28- uuid string
29- slotCleanup func ()
3027 log * logrus.Entry
3128
3229 metricsMu sync.Mutex
3330 lastCounters * RawCounters
3431}
3532
3633func NewWorker (client * Client , docker DockerConfig , vcsOpts vcs.Options ) * Worker {
37- uuid , cleanup := acquireAgentSlot ()
38- client .SetUUID (uuid )
3934 return & Worker {
4035 client : client ,
4136 docker : docker ,
4237 vcsOpts : vcsOpts ,
4338 pollInterval : 1 * time .Second ,
44- uuid : uuid ,
45- slotCleanup : cleanup ,
4639 log : logrus .WithField ("component" , "agent" ),
4740 }
4841}
4942
50- // agentSlotDir returns the directory for agent UUID slot files.
51- func agentSlotDir () string {
52- dir , err := os .UserConfigDir ()
53- if err != nil {
54- dir = os .TempDir ()
55- }
56- return filepath .Join (dir , "actionforge" )
57- }
58-
59- // acquireAgentSlot finds and locks the lowest available agent slot.
60- // Each slot has a persistent UUID file and a lock file. When the process
61- // exits, the lock is released so the next process can reuse that slot
62- // (and its UUID/metrics history).
63- // Returns the UUID and a cleanup function that releases the lock.
64- func acquireAgentSlot () (string , func ()) {
65- dir := agentSlotDir ()
66- _ = os .MkdirAll (dir , 0700 )
67-
68- const maxSlots = 256
69- for i := 0 ; i < maxSlots ; i ++ {
70- lockPath := filepath .Join (dir , fmt .Sprintf ("agent-%d.lock" , i ))
71- uuidPath := filepath .Join (dir , fmt .Sprintf ("agent-%d.uuid" , i ))
72-
73- lockFile , err := os .OpenFile (lockPath , os .O_CREATE | os .O_RDWR , 0600 )
74- if err != nil {
75- continue
76- }
77-
78- if err := lockFileExclusive (lockFile ); err != nil {
79- if cerr := lockFile .Close (); cerr != nil {
80- logrus .WithError (cerr ).Warn ("failed to close lock file" )
81- }
82- continue
83- }
84-
85- // Slot acquired — read or generate UUID
86- uuid := ""
87- if data , err := os .ReadFile (uuidPath ); err == nil {
88- if id := strings .TrimSpace (string (data )); len (id ) == 36 {
89- uuid = id
90- }
91- }
92- if uuid == "" {
93- var buf [16 ]byte
94- _ , _ = rand .Read (buf [:])
95- buf [6 ] = (buf [6 ] & 0x0f ) | 0x40 // version 4
96- buf [8 ] = (buf [8 ] & 0x3f ) | 0x80 // variant 1
97- uuid = fmt .Sprintf ("%08x-%04x-%04x-%04x-%012x" ,
98- buf [0 :4 ], buf [4 :6 ], buf [6 :8 ], buf [8 :10 ], buf [10 :16 ])
99- _ = os .WriteFile (uuidPath , []byte (uuid + "\n " ), 0600 )
100- }
101-
102- cleanup := func () {
103- unlockFile (lockFile )
104- if cerr := lockFile .Close (); cerr != nil {
105- logrus .WithError (cerr ).Warn ("failed to close lock file" )
106- }
107- }
108- return uuid , cleanup
109- }
110-
111- // Fallback: all slots taken, generate ephemeral UUID with no lock
112- var buf [16 ]byte
113- _ , _ = rand .Read (buf [:])
114- buf [6 ] = (buf [6 ] & 0x0f ) | 0x40
115- buf [8 ] = (buf [8 ] & 0x3f ) | 0x80
116- return fmt .Sprintf ("%08x-%04x-%04x-%04x-%012x" ,
117- buf [0 :4 ], buf [4 :6 ], buf [6 :8 ], buf [8 :10 ], buf [10 :16 ]), func () {}
118- }
119-
12043// maxConsecutiveErrors is the number of consecutive connection errors before
12144// Run returns ErrConnectionLost so the caller can decide to restart.
12245const maxConsecutiveErrors = 10
12346
12447func (w * Worker ) Run (ctx context.Context ) error {
125- if w .slotCleanup != nil {
126- defer w .slotCleanup ()
127- }
12848 w .log .Info ("starting" )
12949
13050 // Take initial snapshot for delta computation
@@ -632,13 +552,13 @@ func (w *Worker) buildHeartbeatRequest() HeartbeatRequest {
632552 snap , err := Snapshot ()
633553 if err != nil {
634554 w .log .WithError (err ).Warn ("metrics snapshot error" )
635- return HeartbeatRequest {UUID : w . uuid }
555+ return HeartbeatRequest {}
636556 }
637557
638558 w .metricsMu .Lock ()
639559 defer w .metricsMu .Unlock ()
640560
641- req := HeartbeatRequest {UUID : w . uuid }
561+ req := HeartbeatRequest {}
642562 if w .lastCounters != nil {
643563 // CPU percent
644564 if snap .CPUInstant {
0 commit comments