- README.md: full usage docs - Makefile: build/install/uninstall/test/lint/clean targets - config/config_test.go: 7 tests (load, save, find, add, roundtrip, invalid yaml) - runtime/runtime_test.go: 6 tests (get, available, ListRunning graceful degradation) - systemd/generator_test.go: 7 tests (ServiceName, Generate system/user/custom/docker/unknown, buildExecCommands) - fix: docker/podman ListRunning returns nil (not error) when daemon unavailable - fix: invalid YAML test uses tab-indent which is genuinely unparseable
75 lines
1.6 KiB
Go
75 lines
1.6 KiB
Go
package runtime
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
type Docker struct{}
|
|
|
|
func (d *Docker) Name() string { return "docker" }
|
|
|
|
type dockerContainer struct {
|
|
Names string `json:"Names"`
|
|
Image string `json:"Image"`
|
|
Command string `json:"Command"`
|
|
State string `json:"State"`
|
|
}
|
|
|
|
func (d *Docker) ListRunning() ([]Container, error) {
|
|
bin, err := exec.LookPath("docker")
|
|
if err != nil {
|
|
return nil, nil // docker not installed — not an error
|
|
}
|
|
|
|
out, err := exec.Command(bin, "ps", "--format", "json").Output()
|
|
if err != nil {
|
|
// Docker installed but daemon not running — treat as no containers
|
|
return nil, nil
|
|
}
|
|
|
|
// Docker outputs one JSON object per line (not a JSON array)
|
|
var containers []Container
|
|
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
var c dockerContainer
|
|
if err := json.Unmarshal([]byte(line), &c); err != nil {
|
|
continue
|
|
}
|
|
name := strings.TrimPrefix(c.Names, "/")
|
|
if name == "" {
|
|
continue
|
|
}
|
|
containers = append(containers, Container{
|
|
Name: name,
|
|
Image: c.Image,
|
|
Command: c.Command,
|
|
Runtime: "docker",
|
|
})
|
|
}
|
|
return containers, nil
|
|
}
|
|
|
|
func (d *Docker) Exists(name string) (bool, error) {
|
|
bin, err := exec.LookPath("docker")
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
out, err := exec.Command(bin, "ps", "-a", "--format", "{{.Names}}").Output()
|
|
if err != nil {
|
|
return false, fmt.Errorf("docker ps -a: %w", err)
|
|
}
|
|
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
if strings.TrimSpace(line) == name {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|