package runtime import ( "encoding/json" "fmt" "os/exec" "strings" "github.com/warkanum/unitdore/sysuser" ) type Podman struct{} func (p *Podman) Name() string { return "podman" } type podmanContainer struct { ID string `json:"Id"` Names []string `json:"Names"` Image string `json:"Image"` Command []string `json:"Command"` State string `json:"State"` Labels map[string]string `json:"Labels"` } // podmanCmd builds a podman command for the given user scope. // Returns nil if podman is not installed. func podmanCmd(user string, args ...string) (*exec.Cmd, error) { bin, err := exec.LookPath("podman") if err != nil { return nil, nil } return sysuser.Command(user, bin, args...) } func (p *Podman) ListRunning(user string) ([]Container, error) { cmd, err := podmanCmd(user, "ps", "--format", "json") if err != nil { return nil, err } if cmd == nil { return nil, nil // podman not installed — not an error } out, err := cmd.Output() if err != nil { // Podman installed but not usable — treat as no containers return nil, nil } containers, err := parsePodmanPS(out) if err != nil { // Podman installed but output is not valid JSON (e.g. OCI runtime misconfigured) return nil, nil } for i := range containers { containers[i].User = user } return containers, nil } // parsePodmanPS parses `podman ps --format json` output (a JSON array). func parsePodmanPS(out []byte) ([]Container, error) { var raw []podmanContainer if err := json.Unmarshal(out, &raw); err != nil { return nil, err } var containers []Container for _, c := range raw { name := "" if len(c.Names) > 0 { name = c.Names[0] } // Fall back to short container ID if no name assigned if name == "" { if len(c.ID) >= 12 { name = c.ID[:12] } else if c.ID != "" { name = c.ID } else { continue } } containers = append(containers, Container{ Name: name, Image: c.Image, Command: strings.Join(c.Command, " "), Runtime: "podman", Labels: c.Labels, }) } return containers, nil } func (p *Podman) Exists(user, name string) (bool, error) { cmd, err := podmanCmd(user, "ps", "--format", "{{.Names}}") if err != nil { return false, err } if cmd == nil { return false, nil } out, err := cmd.Output() if err != nil { return false, fmt.Errorf("podman ps: %w", err) } for _, line := range strings.Split(string(out), "\n") { if strings.TrimSpace(line) == name { return true, nil } } return false, nil } // PodExists reports whether a pod with the given name exists (any state). func (p *Podman) PodExists(user, pod string) (bool, error) { cmd, err := podmanCmd(user, "pod", "exists", pod) if err != nil { return false, err } if cmd == nil { return false, nil } if err := cmd.Run(); err != nil { if _, ok := err.(*exec.ExitError); ok { return false, nil // exit 1 = pod does not exist } return false, fmt.Errorf("podman pod exists: %w", err) } return true, nil } // PodName returns the pod name podman-compose uses for a project. func PodName(project string) string { return "pod_" + project }