diff --git a/docs/appendices/0.22.0-migration-guide.md b/docs/appendices/0.22.0-migration-guide.md index 53c88b60d..bcef67a11 100644 --- a/docs/appendices/0.22.0-migration-guide.md +++ b/docs/appendices/0.22.0-migration-guide.md @@ -1,5 +1,9 @@ # 0.22.0 Migration Guide +## Deprecations + +- Calling `logs:failed` without either a `` or the `--all` flag is deprecated. + ## Changes - Underscores are no longer valid characters in app names. Please rename applications before upgrading. diff --git a/docs/deployment/logs.md b/docs/deployment/logs.md index 712065f31..fb251842b 100644 --- a/docs/deployment/logs.md +++ b/docs/deployment/logs.md @@ -2,7 +2,7 @@ ``` logs [-h] [-t] [-n num] [-q] [-p process] # Display recent log output -logs:failed [] # Shows the last failed deploy logs +logs:failed [--parallel count] [--all|] # Shows the last failed deploy logs ``` ## Usage @@ -46,8 +46,8 @@ You can retrieve these logs by using the `logs:failed` command dokku logs:failed node-js-app ``` -You can also retrieve the failed logs for each app: +You may also fetch all failed app logs by using the `--all` flag. ```shell -dokku logs:failed node-js-app +dokku logs:failed --all ``` diff --git a/plugins/common/parallel.go b/plugins/common/parallel.go index 779bc4f90..7f1558231 100644 --- a/plugins/common/parallel.go +++ b/plugins/common/parallel.go @@ -43,13 +43,14 @@ func RunCommandAgainstAllApps(command ParallelCommand, commandName string, paral } if runInSerial { - return runCommandAgainstAllAppsSerially(command, commandName) + return RunCommandAgainstAllAppsSerially(command, commandName) } - return runCommandAgainstAllAppsInParallel(command, commandName, parallelCount) + return RunCommandAgainstAllAppsInParallel(command, commandName, parallelCount) } -func runCommandAgainstAllAppsInParallel(command ParallelCommand, commandName string, parallelCount int) error { +// RunCommandAgainstAllAppsInParallel runs a given ParallelCommand against all apps in parallel +func RunCommandAgainstAllAppsInParallel(command ParallelCommand, commandName string, parallelCount int) error { apps, err := DokkuApps() if err != nil { LogWarn(err.Error()) @@ -68,7 +69,8 @@ func runCommandAgainstAllAppsInParallel(command ParallelCommand, commandName str return err } -func runCommandAgainstAllAppsSerially(command ParallelCommand, commandName string) error { +// RunCommandAgainstAllAppsSerially runs a given ParallelCommand against all apps serially +func RunCommandAgainstAllAppsSerially(command ParallelCommand, commandName string) error { apps, err := DokkuApps() if err != nil { LogWarn(err.Error()) diff --git a/plugins/logs/.gitignore b/plugins/logs/.gitignore new file mode 100644 index 000000000..0f6770b1e --- /dev/null +++ b/plugins/logs/.gitignore @@ -0,0 +1,2 @@ +/commands +/subcommands/* diff --git a/plugins/logs/Makefile b/plugins/logs/Makefile new file mode 100644 index 000000000..abb84bed4 --- /dev/null +++ b/plugins/logs/Makefile @@ -0,0 +1,5 @@ +SUBCOMMANDS = subcommands/failed +BUILD = commands subcommands +PLUGIN_NAME = logs + +include ../../common.mk diff --git a/plugins/logs/commands b/plugins/logs/commands deleted file mode 100755 index 816849610..000000000 --- a/plugins/logs/commands +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -[[ " help logs:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" -source "$PLUGIN_AVAILABLE_PATH/logs/help-functions" -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x - -case "$1" in - help | logs:help) - cmd-logs-help "$@" - ;; - - *) - exit "$DOKKU_NOT_IMPLEMENTED_EXIT" - ;; - -esac diff --git a/plugins/logs/go.mod b/plugins/logs/go.mod new file mode 100644 index 000000000..9c7166d02 --- /dev/null +++ b/plugins/logs/go.mod @@ -0,0 +1,11 @@ +module github.com/dokku/dokku/plugins/logs + +go 1.14 + +require ( + github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 + github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 + github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3 // indirect +) + +replace github.com/dokku/dokku/plugins/common => ../common diff --git a/plugins/logs/go.sum b/plugins/logs/go.sum new file mode 100644 index 000000000..f05068f8d --- /dev/null +++ b/plugins/logs/go.sum @@ -0,0 +1,6 @@ +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA= +github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= +github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3 h1:utdYOikI1XjNtTFGCwSM6OmFJblU4ld4gACoJsbadJg= +github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= diff --git a/plugins/logs/help-functions b/plugins/logs/help-functions deleted file mode 100755 index be7dd3b33..000000000 --- a/plugins/logs/help-functions +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x - -cmd-logs-help() { - declare desc="help command" - declare CMD="$1" - local plugin_name="logs" - local plugin_description="Manage log integration for an app" - - if [[ "$CMD" == "$plugin_name:help" ]]; then - echo -e "Usage: dokku $plugin_name[:COMMAND]" - echo '' - echo "$plugin_description" - echo '' - echo 'Additional commands:' - fn-help-content | sort | column -c2 -t -s, - elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then - fn-help-content - else - cat < [-h] [-t] [-n num] [-q] [-p process], Display recent log output - logs:failed , Show the last logs for an application -help_content -} diff --git a/plugins/logs/internal-functions b/plugins/logs/internal-functions deleted file mode 100755 index dbefa3041..000000000 --- a/plugins/logs/internal-functions +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x - -fn-logs-usage() { - declare desc="logs specific usage" - echo "Usage: dokku logs " - echo " display recent log output" - echo "" - echo " -n, --num NUM # the number of lines to display" - echo " -p, --ps PS # only display logs from the given process" - echo " -t, --tail # continually stream logs" - echo " -q, --quiet # display raw logs without colors, time and names" -} diff --git a/plugins/logs/logs.go b/plugins/logs/logs.go new file mode 100644 index 000000000..5d2bb4636 --- /dev/null +++ b/plugins/logs/logs.go @@ -0,0 +1,17 @@ +package logs + +import ( + "fmt" + + "github.com/dokku/dokku/plugins/common" +) + +// GetFailedLogs outputs failed deploy logs for a given app +func GetFailedLogs(appName string) error { + common.LogInfo2Quiet(fmt.Sprintf("%s failed deploy logs", appName)) + s := common.GetAppScheduler(appName) + if err := common.PlugnTrigger("scheduler-logs-failed", s, appName); err != nil { + return err + } + return nil +} diff --git a/plugins/logs/src/commands/commands.go b/plugins/logs/src/commands/commands.go new file mode 100644 index 000000000..036872295 --- /dev/null +++ b/plugins/logs/src/commands/commands.go @@ -0,0 +1,82 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/logs" +) + +const ( + helpHeader = `Usage: dokku logs[:COMMAND] + +Manage log integration for an app + +Additional commands:` + + helpContent = ` + logs [-h] [-t] [-n num] [-q] [-p process] , Display recent log output + logs:failed [], Shows the last failed deploy logs +` +) + +func main() { + flag.Usage = usage + flag.Parse() + + cmd := flag.Arg(0) + switch cmd { + case "logs": + args := flag.NewFlagSet("logs", flag.ExitOnError) + var num int64 + var ps string + var tail bool + var quiet bool + help := args.Bool("h", false, "-h: print help for the command") + args.Int64Var(&num, "n", 0, "-n: the number of lines to display") + args.Int64Var(&num, "num", 0, "--num: the number of lines to display") + args.StringVar(&ps, "p", "", "-p: only display logs from the given process") + args.StringVar(&ps, "ps", "", "--p: only display logs from the given process") + args.BoolVar(&tail, "t", true, "-t: continually stream logs") + args.BoolVar(&tail, "tail", true, "--tail: continually stream logs") + args.BoolVar(&quiet, "q", true, "-q: display raw logs without colors, time and names") + args.BoolVar(&quiet, "quiet", true, "--quiet: display raw logs without colors, time and names") + args.Parse(flag.Args()[1:]) + if *help { + usage() + return + } + appName := args.Arg(0) + err := logs.CommandDefault(appName, num, ps, tail, quiet) + if err != nil { + common.LogFail(err.Error()) + } + case "logs:help": + usage() + case "help": + command := common.NewShellCmd(fmt.Sprintf("ps -o command= %d", os.Getppid())) + command.ShowOutput = false + output, err := command.Output() + + if err == nil && strings.Contains(string(output), "--all") { + fmt.Println(helpContent) + } else { + fmt.Print("\n logs, Manage log integration for an app\n") + } + default: + dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT")) + if err != nil { + fmt.Println("failed to retrieve DOKKU_NOT_IMPLEMENTED_EXIT environment variable") + dokkuNotImplementExitCode = 10 + } + os.Exit(dokkuNotImplementExitCode) + } +} + +func usage() { + common.CommandUsage(helpHeader, helpContent) +} diff --git a/plugins/logs/src/subcommands/subcommands.go b/plugins/logs/src/subcommands/subcommands.go new file mode 100644 index 000000000..1e438bda5 --- /dev/null +++ b/plugins/logs/src/subcommands/subcommands.go @@ -0,0 +1,33 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/logs" +) + +// main entrypoint to all subcommands +func main() { + parts := strings.Split(os.Args[0], "/") + subcommand := parts[len(parts)-1] + + var err error + switch subcommand { + case "failed": + args := flag.NewFlagSet("logs:failed", flag.ExitOnError) + allApps := args.Bool("all", false, "--all: restore all apps") + args.Parse(os.Args[2:]) + appName := args.Arg(0) + err = logs.CommandFailed(appName, *allApps) + default: + common.LogFail(fmt.Sprintf("Invalid plugin subcommand call: %s", subcommand)) + } + + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/logs/subcommands.go b/plugins/logs/subcommands.go new file mode 100644 index 000000000..f091388a9 --- /dev/null +++ b/plugins/logs/subcommands.go @@ -0,0 +1,51 @@ +package logs + +import ( + "errors" + "fmt" + "strconv" + + "github.com/dokku/dokku/plugins/common" +) + +// CommandDefault displays recent log output +func CommandDefault(appName string, num int64, process string, tail, quiet bool) error { + if appName == "" { + return errors.New("Please specify an app to run the command on") + } + + if err := common.VerifyAppName(appName); err != nil { + return err + } + + if !common.IsDeployed(appName) { + return fmt.Errorf("App %s has not been deployed", appName) + } + + s := common.GetAppScheduler(appName) + t := strconv.FormatBool(tail) + q := strconv.FormatBool(quiet) + n := strconv.FormatInt(num, 10) + if err := common.PlugnTrigger("scheduler-logs", []string{s, appName, process, t, q, n}...); err != nil { + return err + } + return nil +} + +// CommandFailed shows the last failed deploy logs +func CommandFailed(appName string, allApps bool) error { + if allApps { + return common.RunCommandAgainstAllAppsSerially(GetFailedLogs, "logs:failed") + } + + if appName == "" { + common.LogWarn("Deprecated: Restore specified without app, assuming --all") + return common.RunCommandAgainstAllAppsSerially(GetFailedLogs, "logs:failed") + } + + if err := common.VerifyAppName(appName); err != nil { + return err + } + + return GetFailedLogs(appName) +} diff --git a/plugins/logs/subcommands/default b/plugins/logs/subcommands/default deleted file mode 100755 index 9cf68f1cc..000000000 --- a/plugins/logs/subcommands/default +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/logs/internal-functions" - -cmd-logs-default() { - declare desc="display recent log output" - declare cmd="logs" - [[ "$1" == "$cmd" ]] && shift 1 - declare APP="$1" - - [[ -z "$APP" ]] && dokku_log_fail "Please specify an app to run the command on" - verify_app_name "$APP" - - ! (is_deployed "$APP") && dokku_log_fail "App $APP has not been deployed" - - shift 1 - local TEMP=$(getopt -o htqn:p: --long help,tail,quiet,num:,ps: -n 'dokku logs' -- "$@") - local EXIT_CODE="$?" - if [[ "$EXIT_CODE" != 0 ]]; then - fn-logs-usage >&2 - exit 1 - fi - eval set -- "$TEMP" - - local NUM="100" - while true; do - case "$1" in - -t | --tail) - local TAIL=true - shift - ;; - -n | --num) - local NUM="$2" - shift 2 - ;; - -p | --ps) - local PROCESS_TYPE="$2" - shift 2 - ;; - -q | --quiet) - local PRETTY_PRINT=true - shift - ;; - --) - shift - break - ;; - *) dokku_log_fail "Internal error" ;; - esac - done - - DOKKU_SCHEDULER=$(get_app_scheduler "$APP") - plugn trigger scheduler-logs "$DOKKU_SCHEDULER" "$APP" "$PROCESS_TYPE" "$TAIL" "$PRETTY_PRINT" "$NUM" -} - -cmd-logs-default "$@" diff --git a/plugins/logs/subcommands/failed b/plugins/logs/subcommands/failed deleted file mode 100755 index f2c2339b9..000000000 --- a/plugins/logs/subcommands/failed +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/apps/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x - -cmd-logs-failed() { - declare desc="shows the last failed deploy logs" - declare cmd="logs:failed" - [[ "$1" == "$cmd" ]] && shift 1 - declare APP="$1" - local DOKKU_SCHEDULER - - if [[ -z "$APP" ]]; then - local INSTALLED_APPS=$(dokku_apps) - for app in $INSTALLED_APPS; do - fn-logs-failed-single "$app" | tee || true - done - else - fn-logs-failed-single "$APP" - fi -} - -fn-logs-failed-single() { - declare desc="shows the last failed deploy logs for an app" - declare APP="$1" - local DOKKU_SCHEDULER - - dokku_log_info2_quiet "${APP} failed deploy logs" - DOKKU_SCHEDULER=$(get_app_scheduler "$APP") - plugn trigger scheduler-logs-failed "$DOKKU_SCHEDULER" "$APP" -} - -cmd-logs-failed "$@"