Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic app.ini and path checks to doctor cmd #10064

Merged
merged 13 commits into from
Jan 30, 2020
122 changes: 109 additions & 13 deletions cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"

"github.com/urfave/cli"
Expand All @@ -22,18 +25,24 @@ import (
// CmdDoctor represents the available doctor sub-command.
var CmdDoctor = cli.Command{
Name: "doctor",
Usage: "Diagnose the problems",
Description: "A command to diagnose the problems of current gitea instance according the given configuration.",
Usage: "Diagnose and repair problems",
Description: "A command to diagnose and repair problems of current gitea instance according the given configuration.",
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
Action: runDoctor,
}

type check struct {
title string
f func(ctx *cli.Context) ([]string, error)
abort bool
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
}

// checklist represents list for all checks
var checklist = []check{
{
title: "Check paths and basic configuration",
f: runDoctorPathInfo,
abort: true,
},
{
title: "Check if OpenSSH authorized_keys file id correct",
f: runDoctorLocationMoved,
Expand All @@ -42,21 +51,30 @@ var checklist = []check{
}

func runDoctor(ctx *cli.Context) error {
err := initDB()
fmt.Println("Using app.ini at", setting.CustomConf)
if err != nil {
fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
return nil
}

// Silence the console logger
// TODO: Redirect all logs into `doctor.log` ignoring any other log configuration
log.DelNamedLogger("console")
log.DelNamedLogger(log.DEFAULT)

for i, check := range checklist {
if i == 1 {
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
// Only open database after the most basic configuration check
if err := initDB(); err != nil {
fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
return nil
}
}
fmt.Println("[", i+1, "]", check.title)
if messages, err := check.f(ctx); err != nil {
messages, err := check.f(ctx)
for _, message := range messages {
fmt.Println("-", message)
}
if err != nil {
fmt.Println("Error:", err)
} else if len(messages) > 0 {
for _, message := range messages {
fmt.Println("-", message)
if check.abort {
return nil
}
} else {
fmt.Println("OK.")
Expand All @@ -74,6 +92,84 @@ func exePath() (string, error) {
return filepath.Abs(file)
}

func runDoctorPathInfo(ctx *cli.Context) ([]string, error) {

res := make([]string, 0, 10)

if fi, err := os.Stat(setting.CustomConf); err != nil || !fi.Mode().IsRegular() {
res = append(res, fmt.Sprintf("Failed to find configuration file at '%s'.", setting.CustomConf))
res = append(res, fmt.Sprintf("If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run.", setting.CustomConf))
res = append(res, "Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter.")
return res, fmt.Errorf("Can't proceed without a configuration file")
}

setting.NewContext()

fail := false

check := func(name, path string, is_dir, required, is_write bool) {
res = append(res, fmt.Sprintf("%-25s '%s'", name+":", path))
if fi, err := os.Stat(path); err != nil {
if required {
res = append(res, fmt.Sprintf(" ERROR: %v", err))
fail = true
} else {
res = append(res, fmt.Sprintf(" NOTICE: not accessible (%v)", err))
}
return
} else if is_dir && !fi.IsDir() {
res = append(res, " ERROR: not a directory")
fail = true
return
} else if !is_dir && !fi.Mode().IsRegular() {
res = append(res, " ERROR: not a regular file")
fail = true
return
} else if is_write {
if err := runDoctorWritableDir(path); err != nil {
res = append(res, fmt.Sprintf(" ERROR: not writable: %v", err))
fail = true
return
}
}
return
}

// Note print paths inside quotes to make any leading/trailing spaces evident
check("Configuration File Path", setting.CustomConf, false, true, false)
check("Repository Root Path", setting.RepoRootPath, true, true, true)
check("Data Root Path", setting.AppDataPath, true, true, true)
check("Custom File Root Path", setting.CustomPath, true, false, false)
check("Work directory", setting.AppWorkPath, true, true, false)
check("Log Root Path", setting.LogRootPath, true, true, true)

if options.IsDynamic() {
// Do not check/report on StaticRootPath if data is embedded in Gitea (-tags bindata)
check("Static File Root Path", setting.StaticRootPath, true, true, false)
}

if fail {
return res, fmt.Errorf("Please check your configuration file and try again.")
}

return res, nil
}

func runDoctorWritableDir(path string) error {
// There's no platform-independent way of checking if a directory is writable
// https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable

tmpFile, err := ioutil.TempFile(path, "doctors-order")
if err != nil {
return err
}
if err := os.Remove(tmpFile.Name()); err != nil {
fmt.Printf("Warning: can't remove temporary file: '%s'\n", tmpFile.Name())
}
tmpFile.Close()
return nil
}

func runDoctorLocationMoved(ctx *cli.Context) ([]string, error) {
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
return nil, nil
Expand Down
4 changes: 4 additions & 0 deletions modules/options/dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,7 @@ func fileFromDir(name string) ([]byte, error) {

return []byte{}, fmt.Errorf("Asset file does not exist: %s", name)
}

func IsDynamic() bool {
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
return true
}
4 changes: 4 additions & 0 deletions modules/options/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ func fileFromDir(name string) ([]byte, error) {

return ioutil.ReadAll(f)
}

func IsDynamic() bool {
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
return false
}