fix(install): add error handling and minor code cleanups

Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
This commit is contained in:
Rodney Osodo
2026-02-17 08:53:40 +03:00
parent bfd5aa30a7
commit ffbea7af59
5 changed files with 67 additions and 41 deletions

View File

@@ -192,8 +192,7 @@ func MarshalYAMLWithIndent(data interface{}, indent int) ([]byte, error) {
encoder := yaml.NewEncoder(buffer) encoder := yaml.NewEncoder(buffer)
encoder.SetIndent(indent) encoder.SetIndent(indent)
err := encoder.Encode(data) if err := encoder.Encode(data); err != nil {
if err != nil {
return nil, err return nil, err
} }
@@ -209,7 +208,7 @@ func replaceInFile(filepath, oldStr, newStr string) error {
} }
// Replace the string // Replace the string
newContent := strings.Replace(string(content), oldStr, newStr, -1) newContent := strings.ReplaceAll(string(content), oldStr, newStr)
// Write the modified content back to the file // Write the modified content back to the file
err = os.WriteFile(filepath, []byte(newContent), 0644) err = os.WriteFile(filepath, []byte(newContent), 0644)

View File

@@ -144,12 +144,13 @@ func installDocker() error {
} }
func startDockerService() error { func startDockerService() error {
if runtime.GOOS == "linux" { switch runtime.GOOS {
case "linux":
cmd := exec.Command("systemctl", "enable", "--now", "docker") cmd := exec.Command("systemctl", "enable", "--now", "docker")
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd.Run() return cmd.Run()
} else if runtime.GOOS == "darwin" { case "darwin":
// On macOS, Docker is usually started via the Docker Desktop application // On macOS, Docker is usually started via the Docker Desktop application
fmt.Println("Please start Docker Desktop manually on macOS.") fmt.Println("Please start Docker Desktop manually on macOS.")
return nil return nil
@@ -302,7 +303,7 @@ func pullContainers(containerType SupportedContainer) error {
return nil return nil
} }
return fmt.Errorf("Unsupported container type: %s", containerType) return fmt.Errorf("unsupported container type: %s", containerType)
} }
// startContainers starts the containers using the appropriate command. // startContainers starts the containers using the appropriate command.
@@ -325,7 +326,7 @@ func startContainers(containerType SupportedContainer) error {
return nil return nil
} }
return fmt.Errorf("Unsupported container type: %s", containerType) return fmt.Errorf("unsupported container type: %s", containerType)
} }
// stopContainers stops the containers using the appropriate command. // stopContainers stops the containers using the appropriate command.
@@ -347,7 +348,7 @@ func stopContainers(containerType SupportedContainer) error {
return nil return nil
} }
return fmt.Errorf("Unsupported container type: %s", containerType) return fmt.Errorf("unsupported container type: %s", containerType)
} }
// restartContainer restarts a specific container using the appropriate command. // restartContainer restarts a specific container using the appropriate command.
@@ -369,5 +370,5 @@ func restartContainer(container string, containerType SupportedContainer) error
return nil return nil
} }
return fmt.Errorf("Unsupported container type: %s", containerType) return fmt.Errorf("unsupported container type: %s", containerType)
} }

View File

@@ -27,9 +27,18 @@ func installCrowdsec(config Config) error {
os.Exit(1) os.Exit(1)
} }
os.MkdirAll("config/crowdsec/db", 0755) if err := os.MkdirAll("config/crowdsec/db", 0755); err != nil {
os.MkdirAll("config/crowdsec/acquis.d", 0755) fmt.Printf("Error creating config files: %v\n", err)
os.MkdirAll("config/traefik/logs", 0755) os.Exit(1)
}
if err := os.MkdirAll("config/crowdsec/acquis.d", 0755); err != nil {
fmt.Printf("Error creating config files: %v\n", err)
os.Exit(1)
}
if err := os.MkdirAll("config/traefik/logs", 0755); err != nil {
fmt.Printf("Error creating config files: %v\n", err)
os.Exit(1)
}
if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil { if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil {
fmt.Printf("Error copying docker service: %v\n", err) fmt.Printf("Error copying docker service: %v\n", err)

View File

@@ -57,11 +57,12 @@ func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool {
for { for {
input := readString(reader, prompt+" (yes/no)", defaultStr) input := readString(reader, prompt+" (yes/no)", defaultStr)
lower := strings.ToLower(input) lower := strings.ToLower(input)
if lower == "yes" { switch lower {
case "yes":
return true return true
} else if lower == "no" { case "no":
return false return false
} else { default:
fmt.Println("Please enter 'yes' or 'no'.") fmt.Println("Please enter 'yes' or 'no'.")
} }
} }
@@ -71,11 +72,12 @@ func readBoolNoDefault(reader *bufio.Reader, prompt string) bool {
for { for {
input := readStringNoDefault(reader, prompt+" (yes/no)") input := readStringNoDefault(reader, prompt+" (yes/no)")
lower := strings.ToLower(input) lower := strings.ToLower(input)
if lower == "yes" { switch lower {
case "yes":
return true return true
} else if lower == "no" { case "no":
return false return false
} else { default:
fmt.Println("Please enter 'yes' or 'no'.") fmt.Println("Please enter 'yes' or 'no'.")
} }
} }

View File

@@ -2,12 +2,12 @@ package main
import ( import (
"bufio" "bufio"
"crypto/rand"
"embed" "embed"
"encoding/base64"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"crypto/rand"
"encoding/base64"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@@ -102,7 +102,10 @@ func main() {
os.Exit(1) os.Exit(1)
} }
moveFile("config/docker-compose.yml", "docker-compose.yml") if err := moveFile("config/docker-compose.yml", "docker-compose.yml"); err != nil {
fmt.Printf("Error moving docker-compose.yml: %v\n", err)
os.Exit(1)
}
fmt.Println("\nConfiguration files created successfully!") fmt.Println("\nConfiguration files created successfully!")
@@ -123,7 +126,11 @@ func main() {
if !isDockerInstalled() && runtime.GOOS == "linux" && config.InstallationContainerType == Docker { if !isDockerInstalled() && runtime.GOOS == "linux" && config.InstallationContainerType == Docker {
if readBool(reader, "Docker is not installed. Would you like to install it?", true) { if readBool(reader, "Docker is not installed. Would you like to install it?", true) {
installDocker() if err := installDocker(); err != nil {
fmt.Printf("Error installing Docker: %v\n", err)
return
}
// try to start docker service but ignore errors // try to start docker service but ignore errors
if err := startDockerService(); err != nil { if err := startDockerService(); err != nil {
fmt.Println("Error starting Docker service:", err) fmt.Println("Error starting Docker service:", err)
@@ -132,7 +139,7 @@ func main() {
} }
// wait 10 seconds for docker to start checking if docker is running every 2 seconds // wait 10 seconds for docker to start checking if docker is running every 2 seconds
fmt.Println("Waiting for Docker to start...") fmt.Println("Waiting for Docker to start...")
for i := 0; i < 5; i++ { for range 5 {
if isDockerRunning() { if isDockerRunning() {
fmt.Println("Docker is running!") fmt.Println("Docker is running!")
break break
@@ -290,7 +297,8 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
os.Exit(1) os.Exit(1)
} }
if chosenContainer == Podman { switch chosenContainer {
case Podman:
if !isPodmanInstalled() { if !isPodmanInstalled() {
fmt.Println("Podman or podman-compose is not installed. Please install both manually. Automated installation will be available in a later release.") fmt.Println("Podman or podman-compose is not installed. Please install both manually. Automated installation will be available in a later release.")
os.Exit(1) os.Exit(1)
@@ -311,7 +319,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
// Linux only. // Linux only.
if err := run("bash", "-c", "echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system"); err != nil { if err := run("bash", "-c", "echo 'net.ipv4.ip_unprivileged_port_start=80' > /etc/sysctl.d/99-podman.conf && sysctl --system"); err != nil {
fmt.Printf("Error configuring unprivileged ports: %v\n", err) fmt.Printf("Error configuring unprivileged ports: %v\n", err)
os.Exit(1) os.Exit(1)
} }
} else { } else {
@@ -321,7 +329,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
fmt.Println("Unprivileged ports have been configured.") fmt.Println("Unprivileged ports have been configured.")
} }
} else if chosenContainer == Docker { case Docker:
// check if docker is not installed and the user is root // check if docker is not installed and the user is root
if !isDockerInstalled() { if !isDockerInstalled() {
if os.Geteuid() != 0 { if os.Geteuid() != 0 {
@@ -336,7 +344,7 @@ func podmanOrDocker(reader *bufio.Reader) SupportedContainer {
fmt.Println("The installer will not be able to run docker commands without running it as root.") fmt.Println("The installer will not be able to run docker commands without running it as root.")
os.Exit(1) os.Exit(1)
} }
} else { default:
// This shouldn't happen unless there's a third container runtime. // This shouldn't happen unless there's a third container runtime.
os.Exit(1) os.Exit(1)
} }
@@ -405,10 +413,18 @@ func collectUserInput(reader *bufio.Reader) Config {
} }
func createConfigFiles(config Config) error { func createConfigFiles(config Config) error {
os.MkdirAll("config", 0755) if err := os.MkdirAll("config", 0755); err != nil {
os.MkdirAll("config/letsencrypt", 0755) return fmt.Errorf("failed to create config directory: %v", err)
os.MkdirAll("config/db", 0755) }
os.MkdirAll("config/logs", 0755) if err := os.MkdirAll("config/letsencrypt", 0755); err != nil {
return fmt.Errorf("failed to create letsencrypt directory: %v", err)
}
if err := os.MkdirAll("config/db", 0755); err != nil {
return fmt.Errorf("failed to create db directory: %v", err)
}
if err := os.MkdirAll("config/logs", 0755); err != nil {
return fmt.Errorf("failed to create logs directory: %v", err)
}
// Walk through all embedded files // Walk through all embedded files
err := fs.WalkDir(configFiles, "config", func(path string, d fs.DirEntry, err error) error { err := fs.WalkDir(configFiles, "config", func(path string, d fs.DirEntry, err error) error {
@@ -562,22 +578,24 @@ func showSetupTokenInstructions(containerType SupportedContainer, dashboardDomai
fmt.Println("To get your setup token, you need to:") fmt.Println("To get your setup token, you need to:")
fmt.Println("") fmt.Println("")
fmt.Println("1. Start the containers") fmt.Println("1. Start the containers")
if containerType == Docker { switch containerType {
case Docker:
fmt.Println(" docker compose up -d") fmt.Println(" docker compose up -d")
} else if containerType == Podman { case Podman:
fmt.Println(" podman-compose up -d") fmt.Println(" podman-compose up -d")
} else {
} }
fmt.Println("") fmt.Println("")
fmt.Println("2. Wait for the Pangolin container to start and generate the token") fmt.Println("2. Wait for the Pangolin container to start and generate the token")
fmt.Println("") fmt.Println("")
fmt.Println("3. Check the container logs for the setup token") fmt.Println("3. Check the container logs for the setup token")
if containerType == Docker { switch containerType {
case Docker:
fmt.Println(" docker logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'") fmt.Println(" docker logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'")
} else if containerType == Podman { case Podman:
fmt.Println(" podman logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'") fmt.Println(" podman logs pangolin | grep -A 2 -B 2 'SETUP TOKEN'")
} else {
} }
fmt.Println("") fmt.Println("")
fmt.Println("4. Look for output like") fmt.Println("4. Look for output like")
fmt.Println(" === SETUP TOKEN GENERATED ===") fmt.Println(" === SETUP TOKEN GENERATED ===")
@@ -639,10 +657,7 @@ func checkPortsAvailable(port int) error {
addr := fmt.Sprintf(":%d", port) addr := fmt.Sprintf(":%d", port)
ln, err := net.Listen("tcp", addr) ln, err := net.Listen("tcp", addr)
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf("ERROR: port %d is occupied or cannot be bound: %w", port, err)
"ERROR: port %d is occupied or cannot be bound: %w\n\n",
port, err,
)
} }
if closeErr := ln.Close(); closeErr != nil { if closeErr := ln.Close(); closeErr != nil {
fmt.Fprintf(os.Stderr, fmt.Fprintf(os.Stderr,