feat: initialize managed portal
This commit is contained in:
98
internal/managed/docker_runtime.go
Normal file
98
internal/managed/docker_runtime.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package managed
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DockerRuntime interface {
|
||||
GetContainerStatus(containerName string) (string, error)
|
||||
RestartContainer(containerName string) error
|
||||
}
|
||||
|
||||
type CommandRunner func(ctx context.Context, name string, args ...string) ([]byte, error)
|
||||
|
||||
type DockerController struct {
|
||||
runner CommandRunner
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func NewDockerController() *DockerController {
|
||||
return &DockerController{
|
||||
runner: func(ctx context.Context, name string, args ...string) ([]byte, error) {
|
||||
return exec.CommandContext(ctx, name, args...).CombinedOutput()
|
||||
},
|
||||
timeout: 5 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDockerControllerWithRunner(runner CommandRunner) *DockerController {
|
||||
return &DockerController{
|
||||
runner: runner,
|
||||
timeout: 5 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func NormalizeContainerStatus(raw string) string {
|
||||
switch strings.TrimSpace(strings.ToLower(raw)) {
|
||||
case "running":
|
||||
return "running"
|
||||
case "created", "exited", "paused":
|
||||
return "stopped"
|
||||
case "dead", "removing", "restarting":
|
||||
return "failed"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DockerController) GetContainerStatus(containerName string) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
||||
defer cancel()
|
||||
|
||||
output, err := c.runner(ctx, "docker", "inspect", "--format", "{{.State.Status}}", containerName)
|
||||
status := NormalizeContainerStatus(string(output))
|
||||
if status != "unknown" {
|
||||
return status, nil
|
||||
}
|
||||
if err != nil {
|
||||
return status, fmt.Errorf("docker inspect %s: %w", containerName, err)
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (c *DockerController) RestartContainer(containerName string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
|
||||
defer cancel()
|
||||
|
||||
output, err := c.runner(ctx, "docker", "restart", containerName)
|
||||
if err != nil {
|
||||
trimmed := strings.TrimSpace(string(output))
|
||||
if trimmed != "" {
|
||||
return fmt.Errorf("docker restart %s: %w: %s", containerName, err, trimmed)
|
||||
}
|
||||
return fmt.Errorf("docker restart %s: %w", containerName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsDockerUnavailable(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var execErr *exec.Error
|
||||
if errors.As(err, &execErr) {
|
||||
return execErr.Err == exec.ErrNotFound
|
||||
}
|
||||
|
||||
message := err.Error()
|
||||
return errors.Is(err, exec.ErrNotFound) ||
|
||||
strings.Contains(message, `exec: "docker": executable file not found`) ||
|
||||
strings.Contains(message, "failed to connect to the docker API") ||
|
||||
strings.Contains(message, "docker.sock")
|
||||
}
|
||||
Reference in New Issue
Block a user