Skip to content

Commit

Permalink
feat: debug command
Browse files Browse the repository at this point in the history
  • Loading branch information
steveiliop56 committed Sep 6, 2024
1 parent 01f28ef commit eb94b5b
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 40 deletions.
187 changes: 187 additions & 0 deletions cmd/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package cmd

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path"
"runtime"
"strings"
"syscall"

"github.com/aquasecurity/table"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/steveiliop56/runtipi-cli-go/internal/env"
"github.com/steveiliop56/runtipi-cli-go/internal/system"
"github.com/steveiliop56/runtipi-cli-go/internal/utils"
)

func init() {
debugCmd.Flags().BoolVar(&showLogs, "logs", false, "Show last 15 lines of all container logs.")
rootCmd.AddCommand(debugCmd)
}

func GetEnvSafe(key string) (string) {
val, err := env.GetEnvValue(key)
if err != nil {
return "Error"
}
if val == "" {
return "Not set"
}
return val
}

func GetEnvSafeRedact(key string) (string) {
val, err := env.GetEnvValue(key)
if err != nil {
return "Error"
}
if val == "" {
return "Not set"
}
return "<redacted>"
}

var debugCmd = &cobra.Command{
Use: "debug",
Short: "Debug runtipi",
Long: "Use this command to debug your runtipi instance (useful for issues)",
Run: func(cmd *cobra.Command, args []string) {
// Print warning
fmt.Println("\n⚠️ Make sure you have started tipi before running this command")

// Containers
containers := []string{"runtipi", "runtipi-db", "runtipi-redis", "runtipi-reverse-proxy"}

// Colors
green := color.New(color.FgGreen).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
blue := color.New(color.FgBlue).SprintFunc()

// Root folder
rootFolder, rootFolderErr := os.Getwd()

if rootFolderErr != nil {
utils.PrintError("Failed to get root folder")
fmt.Printf("Error: %s\n", rootFolderErr)
os.Exit(1)
}

// System Info
fmt.Println("\n--- " + blue("System Info") + " ---")
operatingSystem := runtime.GOOS
kernel, kernelErr := exec.Command("uname", "-r").Output()
if kernelErr != nil {
utils.PrintError("Failed to run uname command")
fmt.Printf("Error: %s\n", kernelErr)
os.Exit(1)
}
sysSchema := syscall.Sysinfo_t{}
sysErr := syscall.Sysinfo(&sysSchema)
if sysErr != nil {
utils.PrintError("Failed to get total memory")
fmt.Printf("Error: %s\n", sysErr)
os.Exit(1)
}
totalMemory := uint64(sysSchema.Totalram) * uint64(sysSchema.Unit)
totalMemoryGB := fmt.Sprintf("%.2f", float32(totalMemory)/(1<<30))
arch := system.GetArch()
sysInfoTable := table.New(os.Stdout)
sysInfoTable.AddRow("OS", operatingSystem)
sysInfoTable.AddRow("OS Version", string(kernel[:]))
sysInfoTable.AddRow("Memory (GB)", totalMemoryGB)
sysInfoTable.AddRow("Architecture", arch)
sysInfoTable.Render()

// User config
fmt.Println("\n--- " + blue("User Config") + " ---")
userConfigTable := table.New(os.Stdout)
if _, err := os.Stat(path.Join(rootFolder, "user-config", "tipi-compose.yml")); errors.Is(err, os.ErrNotExist) {
userConfigTable.AddRow("Custom tipi docker compose", "false")
} else {
userConfigTable.AddRow("Custom tipi docker compose", "true")
}
userConfigTable.Render()

// Settings
fmt.Println("\n--- " + blue("Settings") + " ---")
settings, settingsErr := os.ReadFile(path.Join(rootFolder, "state", "settings.json"))
if settingsErr != nil {
utils.PrintError("Failed to read settings file")
fmt.Printf("Error: %s\n", settingsErr)
os.Exit(1)
}
var prettyJson bytes.Buffer
prettifyErr := json.Indent(&prettyJson, settings, "", "\t")
if prettifyErr != nil {
utils.PrintError("Failed to prettify json")
fmt.Printf("Error: %s\n", prettifyErr)
os.Exit(1)
}
fmt.Println(prettyJson.String())

// Env
fmt.Println("\n--- " + blue("Environment") + " ---")
envTable := table.New(os.Stdout)
envTable.AddRow("POSTGRES_PASSWORD", GetEnvSafeRedact("POSTGRES_PASSWORD"))
envTable.AddRow("REDIS_PASSWORD", GetEnvSafeRedact("REDIS_PASSWORD"))
envTable.AddRow("APPS_REPO_ID", GetEnvSafe("APPS_REPO_ID"))
envTable.AddRow("APPS_REPO_URL", GetEnvSafe("APPS_REPO_URL"))
envTable.AddRow("TIPI_VERSION", GetEnvSafe("TIPI_VERSION"))
envTable.AddRow("INTERNAL_IP", GetEnvSafe("INTERNAL_IP"))
envTable.AddRow("ARCHITECTURE", GetEnvSafe("ARCHITECTURE"))
envTable.AddRow("JWT_SECRET", GetEnvSafeRedact("JWT_SECRET"))
envTable.AddRow("ROOT_FOLDER_HOST", GetEnvSafe("ROOT_FOLDER_HOST"))
envTable.AddRow("RUNTIPI_APP_DATA_PATH", GetEnvSafe("RUNTIPI_APP_DATA_PATH"))
envTable.AddRow("NGINX_PORT", GetEnvSafe("NGINX_PORT"))
envTable.AddRow("NGINX_PORT_SSL", GetEnvSafe("NGINX_PORT_SSL"))
envTable.AddRow("DOMAIN", GetEnvSafeRedact("DOMAIN"))
envTable.AddRow("POSTGRES_HOST", GetEnvSafe("POSTGRES_HOST"))
envTable.AddRow("POSTGRES_DBNAME", GetEnvSafe("POSTGRES_DBNAME"))
envTable.AddRow("POSTGRES_USERNAME", GetEnvSafe("POSTGRES_USERNAME"))
envTable.AddRow("POSTGRES_PORT", GetEnvSafe("POSTGRES_PORT"))
envTable.AddRow("REDIS_HOST", GetEnvSafe("REDIS_HOST"))
envTable.AddRow("DEMO_MODE", GetEnvSafe("DEMO_MODE"))
envTable.AddRow("LOCAL_DOMAIN", GetEnvSafe("LOCAL_DOMAIN"))
envTable.Render()

// Containers
fmt.Println("\n--- " + blue("Container Status") + " ---")
containerTable := table.New(os.Stdout)
for _, container := range containers {
status, err := exec.Command("docker", "ps", "-a", "--filter", "name=" + container, "--format", "{{.Status}}").Output()
if err != nil {
containerTable.AddRow(container, red("down"))
} else {
if strings.Contains(strings.ToLower(string(status)), "up") {
containerTable.AddRow(container, green("up"))
} else {
containerTable.AddRow(container, red("down"))
}
}
}
containerTable.Render()
fmt.Println("\n^ If a container is not 'Up', you can run the command `docker logs <container_name>` to see the logs of that container.")

// Logs
if showLogs {
fmt.Println("\n--- " + blue("Container Logs") + " ---")
for _, container := range containers {
logs, err := exec.Command("docker", "logs", "-n", "15", container).Output()
if err != nil {
utils.PrintError("Failed to get container logs")
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
fmt.Println("\n" + green(container))
fmt.Printf("\n%s", logs)
}
fmt.Println("^ Make sure to remove any personal information from the logs.")
}
},
}
4 changes: 2 additions & 2 deletions cmd/reset-password.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var resetPasswordCmd = &cobra.Command{
spinner.Fail("Failed to get root folder")
spinner.Stop()
fmt.Printf("Error: %s\n", osErr)
return;
os.Exit(1)
}

time := time.Now().Unix()
Expand All @@ -40,7 +40,7 @@ var resetPasswordCmd = &cobra.Command{
spinner.Fail("Failed to create password change request")
spinner.Stop()
fmt.Printf("Error: %s\n", writeErr)
return;
os.Exit(1)
}

internalIp, _ := env.GetEnvValue("INTERNAL_IP")
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

var noPermissions bool
var envFile string
var showLogs bool

var rootCmd = &cobra.Command{
Use: "runtipi-cli-go",
Expand Down
26 changes: 10 additions & 16 deletions cmd/tipifetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"os/exec"
"path"

"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/steveiliop56/runtipi-cli-go/internal/constants"
"github.com/steveiliop56/runtipi-cli-go/internal/utils"
)

func init() {
Expand All @@ -25,32 +25,26 @@ var tipiFetchCmd = &cobra.Command{

// Write temp ascii file
if err := os.WriteFile(asciiPath, []byte(constants.Neofetch), 0644); err != nil {
color.Set(color.FgRed)
fmt.Print("✗ ")
color.Unset()
fmt.Printf("Failed write neofetch ascii art, error: %s\n", err)
return
utils.PrintError("Failed to write neofetch ascii")
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

// Run the neofetch command
out, err := exec.Command("neofetch", "--ascii", "--ascii_colors", "1", "11", "8", "9", "--source", asciiPath).Output()

// Check for errors
if err != nil {
color.Set(color.FgRed)
fmt.Print("✗ ")
color.Unset()
fmt.Printf("Failed to run neofetch command, error: %s\n", err)
return
utils.PrintError("Failed to run neofetch command")
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

// Delete temp file
if err := os.Remove(asciiPath); err != nil {
color.Set(color.FgRed)
fmt.Print("✗ ")
color.Unset()
fmt.Printf("Failed to remove temp neofetch ascii art, error: %s\n", err)
return
utils.PrintError("Failed to remove neofetch ascii")
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

// Print output
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ require (
)

require (
github.com/aquasecurity/table v1.8.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/Delta456/box-cli-maker v1.3.2 h1:p3LJ67tYrkWOqd3Xju1rYWxx7cRcfHL42poOxdmOLi8=
github.com/Delta456/box-cli-maker v1.3.2/go.mod h1:MsanLNTlPCfUdYyhfPY4Aond1NbPGnRxJiAWFcDBIsA=
github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0=
github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw=
github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650=
github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
Expand Down Expand Up @@ -40,8 +42,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
Expand Down
18 changes: 9 additions & 9 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ func Start(envFile string, noPermissions bool) {
if dockerErr.Error() == "docker-error" {
spinner.Fail("Docker is not installed or user has not the right permissions. See https://docs.docker.com/engine/install/ for more information")
spinner.Stop()
return
os.Exit(1)
} else if dockerErr.Error() == "compose-error" {
spinner.Fail("Docker compose plugin is not installed. See https://docs.docker.com/compose/install/linux/ for more information")
spinner.Stop()
return
os.Exit(1)
}
}
spinner.Succeed("User permissions are ok")
Expand All @@ -38,7 +38,7 @@ func Start(envFile string, noPermissions bool) {
spinner.Fail("Failed to copy system files")
spinner.Stop()
fmt.Printf("Error: %s\n", fileCopyErr)
return
os.Exit(1)
}
spinner.Succeed("Copied system files")

Expand All @@ -49,7 +49,7 @@ func Start(envFile string, noPermissions bool) {
spinner.Fail("Failed to generate env file")
spinner.Stop()
fmt.Printf("Error: %s\n", envErr)
return
os.Exit(1)
}
spinner.Succeed("Env file generated")

Expand All @@ -61,7 +61,7 @@ func Start(envFile string, noPermissions bool) {
spinner.Fail("Failed to chmod files")
spinner.Stop()
fmt.Printf("Error: %s\n", filePermErr)
return
os.Exit(1)
}
spinner.Succeed("File permissions ok")
}
Expand All @@ -75,7 +75,7 @@ func Start(envFile string, noPermissions bool) {
spinner.Fail("Failed to get root folder")
spinner.Stop()
fmt.Printf("Error: %s\n", rootFolderErr)
return
os.Exit(1)
}

_, pullError := exec.Command("docker", "compose", "--env-file", path.Join(rootFolder, ".env"), "pull").Output()
Expand All @@ -84,7 +84,7 @@ func Start(envFile string, noPermissions bool) {
spinner.Fail("Failed to pull images")
spinner.Stop()
fmt.Printf("Error: %s\n", pullError)
return
os.Exit(1)
}
spinner.Succeed("Images pulled")

Expand Down Expand Up @@ -116,7 +116,7 @@ func Start(envFile string, noPermissions bool) {
spinner.Fail("Failed to start containers")
spinner.Stop()
fmt.Printf("Error: %s\n", upErr)
return
os.Exit(1)
}
spinner.Succeed("Containers started")

Expand All @@ -143,7 +143,7 @@ func Stop() {
spinner.Fail("Error in stopping containers")
spinner.Stop()
fmt.Printf("Error: %s\n", err)
return
os.Exit(1)
}

containersToRm := []string{"runtipi-reverse-proxy", "runtipi-db", "runtipi-redis", "runtipi", "tipi-db", "tipi-redis", "tipi-reverse-proxy", "tipi-docker-proxy", "tipi-dashboard", "tipi-worker"}
Expand Down
Loading

0 comments on commit eb94b5b

Please sign in to comment.