5d3b56fe8e
Separa aplicaciones ejecutables (docker_tui, pipeline_launcher) de la librería fn_operations. La carpeta apps/ contiene módulos Go independientes, fn_operations/ queda como librería pura de models/store/operations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
193 lines
4.6 KiB
Go
193 lines
4.6 KiB
Go
package views
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/lucasdataproyects/devfactory/shell"
|
|
)
|
|
|
|
const dockerTimeout = 15 * time.Second
|
|
|
|
// --- Containers ---
|
|
|
|
func ListContainers() ([]DockerContainer, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "ps", "-a", "--format", "{{json .}}")
|
|
stdout, err := result.Both()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseJSONLines[DockerContainer](stdout.Stdout)
|
|
}
|
|
|
|
func StartContainer(id string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "start", id).Both()
|
|
return err
|
|
}
|
|
|
|
func StopContainer(id string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "stop", id).Both()
|
|
return err
|
|
}
|
|
|
|
func RestartContainer(id string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "restart", id).Both()
|
|
return err
|
|
}
|
|
|
|
func ContainerLogs(id string, lines int) (string, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "logs", "--tail", itoa(lines), id)
|
|
out, err := result.Both()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// docker logs writes to both stdout and stderr
|
|
output := out.Stdout
|
|
if out.Stderr != "" {
|
|
if output != "" {
|
|
output += "\n"
|
|
}
|
|
output += out.Stderr
|
|
}
|
|
return output, nil
|
|
}
|
|
|
|
// --- Images ---
|
|
|
|
func ListImages() ([]DockerImage, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "image", "ls", "--format", "{{json .}}")
|
|
stdout, err := result.Both()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseJSONLines[DockerImage](stdout.Stdout)
|
|
}
|
|
|
|
func PullImage(name string) (string, error) {
|
|
result := shell.RunWithTimeout("docker", 120*time.Second, "pull", name)
|
|
out, err := result.Both()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return out.Stdout, nil
|
|
}
|
|
|
|
func RemoveImage(id string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "rmi", id).Both()
|
|
return err
|
|
}
|
|
|
|
// --- Volumes ---
|
|
|
|
func ListVolumes() ([]DockerVolume, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "volume", "ls", "--format", "{{json .}}")
|
|
stdout, err := result.Both()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseJSONLines[DockerVolume](stdout.Stdout)
|
|
}
|
|
|
|
func CreateVolume(name string) error {
|
|
args := []string{"volume", "create"}
|
|
if name != "" {
|
|
args = append(args, name)
|
|
}
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, args...).Both()
|
|
return err
|
|
}
|
|
|
|
func RemoveVolume(name string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "volume", "rm", name).Both()
|
|
return err
|
|
}
|
|
|
|
// --- Networks ---
|
|
|
|
func ListNetworks() ([]DockerNetwork, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "network", "ls", "--format", "{{json .}}")
|
|
stdout, err := result.Both()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseJSONLines[DockerNetwork](stdout.Stdout)
|
|
}
|
|
|
|
func CreateNetwork(name string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "network", "create", name).Both()
|
|
return err
|
|
}
|
|
|
|
func RemoveNetwork(name string) error {
|
|
_, err := shell.RunWithTimeout("docker", dockerTimeout, "network", "rm", name).Both()
|
|
return err
|
|
}
|
|
|
|
// --- Compose ---
|
|
|
|
func ComposePS() ([]ComposeService, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "compose", "ps", "--format", "json")
|
|
stdout, err := result.Both()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// docker compose ps --format json returns a JSON array
|
|
var services []ComposeService
|
|
if err := json.Unmarshal([]byte(stdout.Stdout), &services); err != nil {
|
|
// Try line-by-line as fallback
|
|
return parseJSONLines[ComposeService](stdout.Stdout)
|
|
}
|
|
return services, nil
|
|
}
|
|
|
|
func ComposeUp() (string, error) {
|
|
result := shell.RunWithTimeout("docker", 120*time.Second, "compose", "up", "-d")
|
|
out, err := result.Both()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return out.Stdout + out.Stderr, nil
|
|
}
|
|
|
|
func ComposeDown() (string, error) {
|
|
result := shell.RunWithTimeout("docker", 60*time.Second, "compose", "down")
|
|
out, err := result.Both()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return out.Stdout + out.Stderr, nil
|
|
}
|
|
|
|
func ComposeLogs(lines int) (string, error) {
|
|
result := shell.RunWithTimeout("docker", dockerTimeout, "compose", "logs", "--tail", itoa(lines))
|
|
out, err := result.Both()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return out.Stdout + out.Stderr, nil
|
|
}
|
|
|
|
// --- Helpers ---
|
|
|
|
func parseJSONLines[T any](s string) ([]T, error) {
|
|
var result []T
|
|
for _, line := range strings.Split(strings.TrimSpace(s), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
var item T
|
|
if err := json.Unmarshal([]byte(line), &item); err != nil {
|
|
continue
|
|
}
|
|
result = append(result, item)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func itoa(n int) string {
|
|
return fmt.Sprintf("%d", n)
|
|
}
|