Initial code commit
This commit is contained in:
166
internal/docker/manager.go
Normal file
166
internal/docker/manager.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
ClientDir string
|
||||
}
|
||||
|
||||
func NewManager(clientDir string) *Manager {
|
||||
return &Manager{
|
||||
ClientDir: clientDir,
|
||||
}
|
||||
}
|
||||
|
||||
func getComposeCommand() ([]string, error) {
|
||||
// Try docker compose (v2) first
|
||||
cmd := exec.Command("docker", "compose", "version")
|
||||
if err := cmd.Run(); err == nil {
|
||||
return []string{"docker", "compose"}, nil
|
||||
}
|
||||
|
||||
// Fall back to docker-compose (v1)
|
||||
cmd = exec.Command("docker-compose", "--version")
|
||||
if err := cmd.Run(); err == nil {
|
||||
return []string{"docker-compose"}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("neither 'docker compose' nor 'docker-compose' is available. Please install Docker Compose")
|
||||
}
|
||||
|
||||
func (m *Manager) Build() error {
|
||||
dockerfilePath := filepath.Join(m.ClientDir, "Dockerfile")
|
||||
if _, err := os.Stat(dockerfilePath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("Dockerfile not found at %s", dockerfilePath)
|
||||
}
|
||||
|
||||
// Use docker-compose build to ensure it respects docker-compose.yml configuration
|
||||
// and rebuilds when Dockerfile changes
|
||||
composeCmd, err := getComposeCommand()
|
||||
if err != nil {
|
||||
// Fallback to docker build if compose is not available
|
||||
cmd := exec.Command("docker", "build", "--no-cache", "-t", "sslh-lab-client", m.ClientDir)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to build Docker image: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use docker-compose build which will rebuild if Dockerfile changed
|
||||
args := append(composeCmd, "-f", filepath.Join(m.ClientDir, "docker-compose.yml"), "build", "--no-cache")
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = m.ClientDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to build Docker image: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Run() error {
|
||||
composeFile := filepath.Join(m.ClientDir, "docker-compose.yml")
|
||||
if _, err := os.Stat(composeFile); os.IsNotExist(err) {
|
||||
return fmt.Errorf("docker-compose.yml not found at %s", composeFile)
|
||||
}
|
||||
|
||||
composeCmd, err := getComposeCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := append(composeCmd, "-f", composeFile, "up", "-d")
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = m.ClientDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to run Docker container: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Stop() error {
|
||||
composeFile := filepath.Join(m.ClientDir, "docker-compose.yml")
|
||||
if _, err := os.Stat(composeFile); os.IsNotExist(err) {
|
||||
return fmt.Errorf("docker-compose.yml not found at %s", composeFile)
|
||||
}
|
||||
|
||||
composeCmd, err := getComposeCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := append(composeCmd, "-f", composeFile, "down")
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = m.ClientDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to stop Docker container: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Status() (string, error) {
|
||||
cmd := exec.Command("docker", "ps", "--filter", "name=sslh-lab-client", "--format", "{{.Status}}")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check container status: %w", err)
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func (m *Manager) IsRunning() bool {
|
||||
cmd := exec.Command("docker", "ps", "--filter", "name=sslh-lab-client", "--format", "{{.Names}}")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(output) > 0 && string(output) == "sslh-lab-client\n"
|
||||
}
|
||||
|
||||
func (m *Manager) Connect(shell string) error {
|
||||
if !m.IsRunning() {
|
||||
return fmt.Errorf("container is not running. Start it first with 'sslh-lab client'")
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "exec", "-it", "sslh-lab-client", shell)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Run()
|
||||
|
||||
// Exit code 255 is common when user exits normally (Ctrl+D or 'exit' command)
|
||||
// Exit code 130 is common when user presses Ctrl+C
|
||||
// These are not errors, just normal ways to disconnect
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
exitCode := exitError.ExitCode()
|
||||
if exitCode == 255 || exitCode == 130 || exitCode == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// If err is nil (exit code 0), return nil
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// For other errors, return them
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user