| 1 | package instancectl |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "fmt" |
| 7 | "os" |
| 8 | "path/filepath" |
| 9 | |
| 10 | "github.com/carlosframework/platform/internal/safefile" |
| 11 | ) |
| 12 | |
| 13 | // clearLegacyClaimLaunch removes a claim launch snapshot before the legacy |
| 14 | // controller may reuse the main run unit. Artifact-free hosts keep the old |
| 15 | // path: they do not acquire a systemd query or cgroup read. |
| 16 | func (s *Server) clearLegacyClaimLaunch(ctx context.Context, host string) error { |
| 17 | if s.DropInDir == "" || s.ExeRoot == "" || s.DataRoot == "" || s.CgroupRoot == "" { |
| 18 | return nil |
| 19 | } |
| 20 | u := ClaimUnits{ |
| 21 | Systemctl: s.Systemctl, |
| 22 | CgroupRoot: s.CgroupRoot, |
| 23 | ExeRoot: s.ExeRoot, |
| 24 | UnitRoot: s.DropInDir, |
| 25 | DataRoot: s.DataRoot, |
| 26 | } |
| 27 | source, dropin, err := u.launchPaths(host) |
| 28 | if err != nil { |
| 29 | return err |
| 30 | } |
| 31 | found := false |
| 32 | dropinPath := filepath.Join(dropin, "60-claim-launch.conf") |
| 33 | var dropinBody []byte |
| 34 | for _, path := range []string{ |
| 35 | filepath.Join(source, "launch.json"), |
| 36 | filepath.Join(source, "permit.json"), |
| 37 | filepath.Join(source, "exec.sock"), |
| 38 | dropinPath, |
| 39 | } { |
| 40 | if _, err := os.Lstat(path); err == nil { |
| 41 | found = true |
| 42 | if path == dropinPath { |
| 43 | dropinBody, err = safefile.ReadFile(path) |
| 44 | if err != nil { |
| 45 | return fmt.Errorf("read claim launch %s: %w", path, err) |
| 46 | } |
| 47 | } |
| 48 | } else if !errors.Is(err, os.ErrNotExist) { |
| 49 | return fmt.Errorf("inspect claim launch %s: %w", path, err) |
| 50 | } |
| 51 | } |
| 52 | if !found { |
| 53 | return nil |
| 54 | } |
| 55 | // Gone proves both facts required before clearLaunch mutates either |
| 56 | // artifact: systemd says the unit is idle and its cgroup is empty. |
| 57 | if err := u.Gone(ctx, host, ""); err != nil { |
| 58 | // clearLaunch removes the drop-in before daemon-reload. Restore it |
| 59 | // when that reload fails, so a later attempt cannot mistake a cached |
| 60 | // systemd binding for an artifact-free host and start through it. |
| 61 | if dropinBody != nil { |
| 62 | if _, statErr := os.Lstat(dropinPath); errors.Is(statErr, os.ErrNotExist) { |
| 63 | restoreErr := safefile.WriteFile(dropinPath, dropinBody, 0644) |
| 64 | if restoreErr != nil { |
| 65 | err = errors.Join(err, fmt.Errorf("restore claim launch drop-in: %w", restoreErr)) |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | return fmt.Errorf("clear claim launch for %s: %w", host, err) |
| 70 | } |
| 71 | return nil |
| 72 | } |
| 73 | |