123 lines
3.0 KiB
Go
123 lines
3.0 KiB
Go
package podman
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// CmdClient talks to podman socket via CLI.
|
|
type CmdClient struct {
|
|
SocketPath string
|
|
}
|
|
|
|
func NewCmdClient(sock string) *CmdClient {
|
|
return &CmdClient{SocketPath: sock}
|
|
}
|
|
|
|
func (c *CmdClient) List(ctID string) ([]OCIContainer, error) {
|
|
args := c.baseArgs(ctID, "ps", "--format", "json")
|
|
out, err := exec.Command(args[0], args[1:]...).Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var parsed []struct {
|
|
ID string `json:"Id"`
|
|
Image string `json:"Image"`
|
|
Status string `json:"Status"`
|
|
}
|
|
if err := json.Unmarshal(out, &parsed); err != nil {
|
|
return nil, err
|
|
}
|
|
res := make([]OCIContainer, 0, len(parsed))
|
|
for _, p := range parsed {
|
|
res = append(res, OCIContainer{ID: p.ID, Image: p.Image, Status: p.Status})
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (c *CmdClient) Create(ctID string, spec CreateSpec) (OCIContainer, error) {
|
|
args := c.baseArgs(ctID, "create")
|
|
for k, v := range spec.Env {
|
|
args = append(args, "--env", k+"="+v)
|
|
}
|
|
for _, v := range spec.Volumes {
|
|
args = append(args, "-v", v)
|
|
}
|
|
for _, p := range spec.Ports {
|
|
args = append(args, "-p", formatPort(p))
|
|
}
|
|
if spec.Restart != "" {
|
|
args = append(args, "--restart", spec.Restart)
|
|
}
|
|
args = append(args, spec.Image)
|
|
args = append(args, spec.Cmd...)
|
|
out, err := exec.Command(args[0], args[1:]...).CombinedOutput()
|
|
if err != nil {
|
|
return OCIContainer{}, err
|
|
}
|
|
id := strings.TrimSpace(string(out))
|
|
return OCIContainer{ID: id, Image: spec.Image, Status: "created"}, nil
|
|
}
|
|
|
|
func (c *CmdClient) Start(ctID, cid string) error {
|
|
args := c.baseArgs(ctID, "start", cid)
|
|
return exec.Command(args[0], args[1:]...).Run()
|
|
}
|
|
|
|
func (c *CmdClient) Stop(ctID, cid string) error {
|
|
args := c.baseArgs(ctID, "stop", cid)
|
|
return exec.Command(args[0], args[1:]...).Run()
|
|
}
|
|
|
|
func (c *CmdClient) Delete(ctID, cid string) error {
|
|
args := c.baseArgs(ctID, "rm", "-f", cid)
|
|
return exec.Command(args[0], args[1:]...).Run()
|
|
}
|
|
|
|
// baseArgs chooses how to enter the CT: prefer nsenter into CT init pid; otherwise use host socket.
|
|
func (c *CmdClient) baseArgs(ctID string, args ...string) []string {
|
|
nsPrefix := []string{}
|
|
if ctID != "" {
|
|
pid := containerInitPID(ctID)
|
|
if pid > 0 {
|
|
nsPrefix = []string{"nsenter", "-t", fmt.Sprintf("%d", pid), "-n", "-m", "-u", "-i", "--"}
|
|
}
|
|
}
|
|
full := append([]string{"podman"}, args...)
|
|
if len(nsPrefix) > 0 {
|
|
return append(nsPrefix, full...)
|
|
}
|
|
// fallback to socket on host
|
|
if c.SocketPath != "" {
|
|
full = append([]string{"podman", "--url", "unix://" + c.SocketPath}, args...)
|
|
}
|
|
return full
|
|
}
|
|
|
|
func formatPort(p PortMap) string {
|
|
return fmt.Sprintf("%d:%d", p.HostPort, p.ContainerPort)
|
|
}
|
|
|
|
func fileExists(path string) bool {
|
|
_, err := os.Stat(path)
|
|
return err == nil
|
|
}
|
|
|
|
// containerInitPID returns the PID of the CT's init using lxc-info.
|
|
func containerInitPID(ctID string) int {
|
|
out, err := exec.Command("lxc-info", "-n", ctID, "-pH").Output()
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
pidStr := strings.TrimSpace(string(out))
|
|
pid, err := strconv.Atoi(pidStr)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return pid
|
|
}
|