Skip to content

Commit 7904677

Browse files
committed
Abstract functions to pkg
1 parent b358707 commit 7904677

3 files changed

Lines changed: 321 additions & 233 deletions

File tree

core/manager/health_check.go

Lines changed: 4 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
package manager
22

33
import (
4-
"bytes"
5-
"encoding/json"
64
"fmt"
7-
"os/exec"
85
"path/filepath"
96
"strings"
107
"time"
118

12-
"github.com/docker/docker/api/types"
139
"github.com/sdslabs/beastv4/core"
1410
"github.com/sdslabs/beastv4/core/cache"
1511
"github.com/sdslabs/beastv4/core/config"
@@ -236,238 +232,13 @@ func ProcessInstanceDeletionQueue() {
236232
func CleanupOrphanedInstanceContainers() {
237233
log.Debug("Checking for orphaned instance containers")
238234

239-
cleanupOrphanedOnServer(core.LOCALHOST)
240-
cleanupOrphanedComposeInstancesOnServer(core.LOCALHOST)
235+
cr.CleanupOrphans()
236+
cr.CleanupOrphanedComposeInstances()
241237

242238
for host, server := range config.Cfg.AvailableServers {
243239
if server.Active && host != core.LOCALHOST {
244-
cleanupOrphanedOnServer(host)
245-
cleanupOrphanedComposeInstancesOnServer(host)
240+
remoteManager.CleanupOrphanedOnServer(host)
241+
remoteManager.CleanupOrphanedComposeInstancesOnServer(host)
246242
}
247243
}
248244
}
249-
250-
func cleanupOrphanedOnServer(serverHost string) {
251-
var containers []types.Container
252-
var err error
253-
254-
if serverHost == core.LOCALHOST {
255-
containers, err = cr.SearchContainerByFilter(map[string]string{
256-
"label": "beast.instance=true",
257-
})
258-
} else {
259-
server := config.Cfg.AvailableServers[serverHost]
260-
containers, err = remoteManager.SearchContainerByFilterRemote(map[string]string{
261-
"label": "beast.instance=true",
262-
}, server)
263-
}
264-
265-
if err != nil {
266-
log.Warnf("Failed to search for instance containers on %s: %v", serverHost, err)
267-
return
268-
}
269-
270-
for _, container := range containers {
271-
instanceID := container.Labels["beast.instance.id"]
272-
if instanceID == "" {
273-
for _, name := range container.Names {
274-
name = strings.TrimPrefix(name, "/")
275-
if strings.HasPrefix(name, "beast_instance_") {
276-
parts := strings.Split(name, "_")
277-
if len(parts) >= 4 {
278-
instanceID = parts[len(parts)-1]
279-
break
280-
}
281-
}
282-
}
283-
}
284-
285-
if instanceID == "" {
286-
continue
287-
}
288-
289-
_, err := cache.GetInstance(instanceID)
290-
if err != nil {
291-
containerName := ""
292-
if len(container.Names) > 0 {
293-
containerName = strings.TrimPrefix(container.Names[0], "/")
294-
}
295-
log.Infof("Removing orphaned instance container: %s (instance %s) on %s", containerName, instanceID, serverHost)
296-
297-
if serverHost == core.LOCALHOST {
298-
if err := cr.StopAndRemoveContainer(container.ID); err != nil {
299-
log.Warnf("Failed to remove orphaned container %s: %v", container.ID[:12], err)
300-
}
301-
} else {
302-
server := config.Cfg.AvailableServers[serverHost]
303-
if err := remoteManager.StopAndRemoveContainerRemote(container.ID, server); err != nil {
304-
log.Warnf("Failed to remove orphaned container %s on %s: %v", container.ID[:12], serverHost, err)
305-
}
306-
}
307-
308-
cache.FreeContainerPortsOnHost(serverHost, container.ID)
309-
}
310-
}
311-
}
312-
313-
// cleanupOrphanedComposeInstancesOnServer finds and removes orphaned docker compose instance projects.
314-
// Docker Compose containers don't have the beast.instance labels, but they have
315-
// com.docker.compose.project labels with project names starting with "beast-instance-".
316-
func cleanupOrphanedComposeInstancesOnServer(serverHost string) {
317-
var projectNames []string
318-
var err error
319-
320-
if serverHost == core.LOCALHOST {
321-
projectNames, err = getOrphanedComposeInstanceProjects()
322-
} else {
323-
server := config.Cfg.AvailableServers[serverHost]
324-
projectNames, err = getOrphanedComposeInstanceProjectsRemote(server)
325-
}
326-
327-
if err != nil {
328-
log.Warnf("Failed to get compose instance projects on %s: %v", serverHost, err)
329-
return
330-
}
331-
332-
for _, projectName := range projectNames {
333-
// Extract instance ID from project name: beast-instance-{encoded_challenge}-{instanceID}
334-
parts := strings.Split(projectName, "-")
335-
if len(parts) < 4 {
336-
continue
337-
}
338-
instanceID := parts[len(parts)-1]
339-
340-
// Check if instance still exists in cache
341-
_, err := cache.GetInstance(instanceID)
342-
if err != nil {
343-
log.Infof("Removing orphaned compose instance project: %s (instance %s) on %s", projectName, instanceID, serverHost)
344-
345-
if serverHost == core.LOCALHOST {
346-
if err := composeDownProject(projectName); err != nil {
347-
log.Warnf("Failed to remove orphaned compose project %s: %v", projectName, err)
348-
}
349-
} else {
350-
server := config.Cfg.AvailableServers[serverHost]
351-
if err := composeDownProjectRemote(projectName, server); err != nil {
352-
log.Warnf("Failed to remove orphaned compose project %s on %s: %v", projectName, serverHost, err)
353-
}
354-
}
355-
}
356-
}
357-
}
358-
359-
// getOrphanedComposeInstanceProjects returns a list of docker compose project names
360-
// that match the instance naming pattern (beast-instance-*)
361-
func getOrphanedComposeInstanceProjects() ([]string, error) {
362-
cmd := exec.Command("docker", "compose", "ls", "--format", "json")
363-
var output bytes.Buffer
364-
cmd.Stdout = &output
365-
cmd.Stderr = &output
366-
367-
if err := cmd.Run(); err != nil {
368-
return nil, fmt.Errorf("docker compose ls failed: %v, output: %s", err, output.String())
369-
}
370-
371-
type ComposeProject struct {
372-
Name string `json:"Name"`
373-
Status string `json:"Status"`
374-
}
375-
376-
var projects []ComposeProject
377-
outputStr := strings.TrimSpace(output.String())
378-
if outputStr == "" {
379-
return nil, nil
380-
}
381-
382-
if err := json.Unmarshal([]byte(outputStr), &projects); err != nil {
383-
// Try parsing line by line (older docker compose versions)
384-
for _, line := range strings.Split(outputStr, "\n") {
385-
if strings.TrimSpace(line) == "" {
386-
continue
387-
}
388-
var project ComposeProject
389-
if err := json.Unmarshal([]byte(line), &project); err != nil {
390-
continue
391-
}
392-
projects = append(projects, project)
393-
}
394-
}
395-
396-
var instanceProjects []string
397-
for _, project := range projects {
398-
if strings.HasPrefix(project.Name, "beast-instance-") {
399-
instanceProjects = append(instanceProjects, project.Name)
400-
}
401-
}
402-
403-
return instanceProjects, nil
404-
}
405-
406-
// getOrphanedComposeInstanceProjectsRemote returns compose instance projects on a remote server
407-
func getOrphanedComposeInstanceProjectsRemote(server config.AvailableServer) ([]string, error) {
408-
output, err := remoteManager.RunCommandOnServer(server, "docker compose ls --format json")
409-
if err != nil {
410-
return nil, fmt.Errorf("docker compose ls failed on remote: %v", err)
411-
}
412-
413-
type ComposeProject struct {
414-
Name string `json:"Name"`
415-
Status string `json:"Status"`
416-
}
417-
418-
var projects []ComposeProject
419-
outputStr := strings.TrimSpace(output)
420-
if outputStr == "" {
421-
return nil, nil
422-
}
423-
424-
if err := json.Unmarshal([]byte(outputStr), &projects); err != nil {
425-
// Try parsing line by line
426-
for _, line := range strings.Split(outputStr, "\n") {
427-
if strings.TrimSpace(line) == "" {
428-
continue
429-
}
430-
var project ComposeProject
431-
if err := json.Unmarshal([]byte(line), &project); err != nil {
432-
continue
433-
}
434-
projects = append(projects, project)
435-
}
436-
}
437-
438-
var instanceProjects []string
439-
for _, project := range projects {
440-
if strings.HasPrefix(project.Name, "beast-instance-") {
441-
instanceProjects = append(instanceProjects, project.Name)
442-
}
443-
}
444-
445-
return instanceProjects, nil
446-
}
447-
448-
// composeDownProject removes a docker compose project by name
449-
func composeDownProject(projectName string) error {
450-
cmd := exec.Command("docker", "compose", "-p", projectName, "down", "--remove-orphans", "-v")
451-
var output bytes.Buffer
452-
cmd.Stdout = &output
453-
cmd.Stderr = &output
454-
455-
if err := cmd.Run(); err != nil {
456-
return fmt.Errorf("docker compose down failed: %v, output: %s", err, output.String())
457-
}
458-
459-
log.Debugf("Successfully removed compose project %s", projectName)
460-
return nil
461-
}
462-
463-
// composeDownProjectRemote removes a docker compose project on a remote server
464-
func composeDownProjectRemote(projectName string, server config.AvailableServer) error {
465-
cmd := fmt.Sprintf("docker compose -p %s down --remove-orphans -v", projectName)
466-
output, err := remoteManager.RunCommandOnServer(server, cmd)
467-
if err != nil {
468-
return fmt.Errorf("docker compose down failed on remote: %v, output: %s", err, output)
469-
}
470-
471-
log.Debugf("Successfully removed compose project %s on %s", projectName, server.Host)
472-
return nil
473-
}

0 commit comments

Comments
 (0)