257 líneas
6.4 KiB
Go
257 líneas
6.4 KiB
Go
package docker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/manalejandro/buque/internal/models"
|
|
)
|
|
|
|
// ComposeManager manages Docker Compose operations
|
|
type ComposeManager struct {
|
|
composeCommand string
|
|
}
|
|
|
|
// NewComposeManager creates a new Docker Compose manager
|
|
func NewComposeManager() (*ComposeManager, error) {
|
|
// Try to find docker compose command
|
|
cmd := "docker"
|
|
if err := exec.Command(cmd, "compose", "version").Run(); err == nil {
|
|
return &ComposeManager{composeCommand: cmd}, nil
|
|
}
|
|
|
|
// Fallback to docker-compose
|
|
cmd = "docker-compose"
|
|
if err := exec.Command(cmd, "version").Run(); err == nil {
|
|
return &ComposeManager{composeCommand: cmd}, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("docker compose not found. Please install Docker and Docker Compose")
|
|
}
|
|
|
|
// Up starts services in an environment
|
|
func (cm *ComposeManager) Up(ctx context.Context, env models.Environment, detach bool) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "up")
|
|
|
|
if detach {
|
|
args = append(args, "-d")
|
|
}
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// Down stops and removes services in an environment
|
|
func (cm *ComposeManager) Down(ctx context.Context, env models.Environment, removeVolumes bool) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "down")
|
|
|
|
if removeVolumes {
|
|
args = append(args, "-v")
|
|
}
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// Restart restarts services in an environment
|
|
func (cm *ComposeManager) Restart(ctx context.Context, env models.Environment) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "restart")
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// Pull pulls images for an environment
|
|
func (cm *ComposeManager) Pull(ctx context.Context, env models.Environment) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "pull")
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// PS lists services in an environment
|
|
func (cm *ComposeManager) PS(ctx context.Context, env models.Environment) (string, error) {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "ps", "--format", "json")
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Dir = env.Path
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to list services: %w\n%s", err, string(output))
|
|
}
|
|
|
|
return string(output), nil
|
|
}
|
|
|
|
// Logs retrieves logs from an environment
|
|
func (cm *ComposeManager) Logs(ctx context.Context, env models.Environment, follow bool, tail string) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "logs")
|
|
|
|
if follow {
|
|
args = append(args, "-f")
|
|
}
|
|
|
|
if tail != "" {
|
|
args = append(args, "--tail", tail)
|
|
}
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// Update updates images and recreates services
|
|
func (cm *ComposeManager) Update(ctx context.Context, env models.Environment) error {
|
|
// Pull latest images
|
|
if err := cm.Pull(ctx, env); err != nil {
|
|
return fmt.Errorf("failed to pull images: %w", err)
|
|
}
|
|
|
|
// Recreate services with new images
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "up", "-d", "--force-recreate")
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// Build builds images for an environment
|
|
func (cm *ComposeManager) Build(ctx context.Context, env models.Environment) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "build")
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// ValidateComposeFile checks if the compose file is valid
|
|
func (cm *ComposeManager) ValidateComposeFile(env models.Environment) error {
|
|
composeFilePath := filepath.Join(env.Path, env.ComposeFile)
|
|
|
|
if _, err := os.Stat(composeFilePath); os.IsNotExist(err) {
|
|
return fmt.Errorf("compose file not found: %s", composeFilePath)
|
|
}
|
|
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "config", "--quiet")
|
|
|
|
cmd := cm.createCommand(context.Background(), args...)
|
|
cmd.Dir = env.Path
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("invalid compose file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetConfig returns the resolved compose configuration
|
|
func (cm *ComposeManager) GetConfig(ctx context.Context, env models.Environment) (string, error) {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "config")
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Dir = env.Path
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get config: %w\n%s", err, string(output))
|
|
}
|
|
|
|
return string(output), nil
|
|
}
|
|
|
|
// buildComposeArgs builds the base arguments for docker compose commands
|
|
func (cm *ComposeManager) buildComposeArgs(env models.Environment) []string {
|
|
args := []string{}
|
|
|
|
if cm.composeCommand == "docker" {
|
|
args = append(args, "compose")
|
|
}
|
|
|
|
if env.ComposeFile != "" && env.ComposeFile != "docker-compose.yml" {
|
|
args = append(args, "-f", env.ComposeFile)
|
|
}
|
|
|
|
return args
|
|
}
|
|
|
|
// createCommand creates an exec.Cmd with the given arguments
|
|
func (cm *ComposeManager) createCommand(ctx context.Context, args ...string) *exec.Cmd {
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
if cm.composeCommand == "docker-compose" {
|
|
return exec.CommandContext(ctx, cm.composeCommand, args...)
|
|
}
|
|
|
|
return exec.CommandContext(ctx, cm.composeCommand, args...)
|
|
}
|
|
|
|
// ExecInService executes a command in a running service
|
|
func (cm *ComposeManager) ExecInService(ctx context.Context, env models.Environment, service string, command []string) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "exec", service)
|
|
args = append(args, command...)
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
// CopyLogs copies logs from a service to a writer
|
|
func (cm *ComposeManager) CopyLogs(ctx context.Context, env models.Environment, service string, writer io.Writer) error {
|
|
args := cm.buildComposeArgs(env)
|
|
args = append(args, "logs", service)
|
|
|
|
cmd := cm.createCommand(ctx, args...)
|
|
cmd.Stdout = writer
|
|
cmd.Stderr = writer
|
|
cmd.Dir = env.Path
|
|
|
|
return cmd.Run()
|
|
}
|