2017-09-03 19:19:28 -04:00
|
|
|
package common
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2024-11-04 02:49:20 -05:00
|
|
|
"io"
|
2017-09-03 19:19:28 -04:00
|
|
|
"os"
|
2021-01-07 01:43:55 -05:00
|
|
|
"strconv"
|
2020-08-30 13:25:06 -04:00
|
|
|
"strings"
|
2021-10-24 20:20:35 -04:00
|
|
|
"sync"
|
2024-01-20 06:56:14 -05:00
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-multierror"
|
2017-09-03 19:19:28 -04:00
|
|
|
)
|
|
|
|
|
|
2021-02-03 15:20:46 -05:00
|
|
|
// ErrWithExitCode wraps error and exposes an ExitCode method
|
|
|
|
|
type ErrWithExitCode interface {
|
|
|
|
|
ExitCode() int
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 20:20:35 -04:00
|
|
|
type writer struct {
|
|
|
|
|
mu *sync.Mutex
|
|
|
|
|
source string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write prints the data to either stdout or stderr using the log helper functions
|
|
|
|
|
func (w *writer) Write(bytes []byte) (int, error) {
|
|
|
|
|
w.mu.Lock()
|
|
|
|
|
defer w.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
if w.source == "stdout" {
|
|
|
|
|
for _, line := range strings.Split(string(bytes), "\n") {
|
|
|
|
|
if line == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogVerboseQuiet(line)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for _, line := range strings.Split(string(bytes), "\n") {
|
|
|
|
|
if line == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogVerboseStderrQuiet(line)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return len(bytes), nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 02:49:20 -05:00
|
|
|
// PrefixingWriter is a writer that prefixes all writes with a given prefix
|
|
|
|
|
type PrefixingWriter struct {
|
|
|
|
|
Prefix []byte
|
|
|
|
|
Writer io.Writer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write writes the given bytes to the writer with the prefix
|
|
|
|
|
func (pw *PrefixingWriter) Write(p []byte) (int, error) {
|
|
|
|
|
if len(p) == 0 {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Perform an "atomic" write of a prefix and p to make sure that it doesn't interleave
|
|
|
|
|
// sub-line when used concurrently with io.PipeWrite.
|
|
|
|
|
n, err := pw.Writer.Write(append(pw.Prefix, p...))
|
|
|
|
|
if n > len(p) {
|
|
|
|
|
// To comply with the io.Writer interface requirements we must
|
|
|
|
|
// return a number of bytes written from p (0 <= n <= len(p)),
|
|
|
|
|
// so we are ignoring the length of the prefix here.
|
|
|
|
|
return len(p), err
|
|
|
|
|
}
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 19:19:28 -04:00
|
|
|
// LogFail is the failure log formatter
|
|
|
|
|
// prints text to stderr and exits with status 1
|
|
|
|
|
func LogFail(text string) {
|
2020-11-28 03:32:11 -05:00
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
2017-09-03 19:19:28 -04:00
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 15:20:46 -05:00
|
|
|
// LogFailWithError is the failure log formatter
|
|
|
|
|
// prints text to stderr and exits with the specified exit code
|
|
|
|
|
func LogFailWithError(err error) {
|
2024-01-20 06:56:14 -05:00
|
|
|
if err == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if merr, ok := err.(*multierror.Error); ok {
|
|
|
|
|
for _, e := range merr.Errors {
|
|
|
|
|
fmt.Fprintf(os.Stderr, " ! %s\n", e.Error())
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-11-13 01:52:08 -05:00
|
|
|
if err.Error() != "" {
|
|
|
|
|
fmt.Fprintf(os.Stderr, " ! %s\n", err.Error())
|
|
|
|
|
}
|
2024-01-20 06:56:14 -05:00
|
|
|
}
|
2021-02-03 15:20:46 -05:00
|
|
|
if errExit, ok := err.(ErrWithExitCode); ok {
|
|
|
|
|
os.Exit(errExit.ExitCode())
|
|
|
|
|
}
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-29 11:28:34 -04:00
|
|
|
// LogFailWithErrorQuiet is the failure log formatter (with quiet option)
|
|
|
|
|
// prints text to stderr and exits with the specified exit code
|
|
|
|
|
// The error message is not printed if DOKKU_QUIET_OUTPUT has any value
|
|
|
|
|
func LogFailWithErrorQuiet(err error) {
|
|
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", err.Error()))
|
|
|
|
|
}
|
|
|
|
|
if errExit, ok := err.(ErrWithExitCode); ok {
|
|
|
|
|
os.Exit(errExit.ExitCode())
|
|
|
|
|
}
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-10 14:14:37 -04:00
|
|
|
// LogFailQuiet is the failure log formatter (with quiet option)
|
|
|
|
|
// prints text to stderr and exits with status 1
|
|
|
|
|
func LogFailQuiet(text string) {
|
|
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
2020-11-28 03:32:11 -05:00
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
2020-03-10 14:14:37 -04:00
|
|
|
}
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Log is the log formatter
|
|
|
|
|
func Log(text string) {
|
|
|
|
|
fmt.Println(text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LogQuiet is the log formatter (with quiet option)
|
|
|
|
|
func LogQuiet(text string) {
|
|
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
|
|
|
fmt.Println(text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 19:19:28 -04:00
|
|
|
// LogInfo1 is the info1 header formatter
|
|
|
|
|
func LogInfo1(text string) {
|
2020-02-22 04:32:51 -05:00
|
|
|
fmt.Println(fmt.Sprintf("-----> %s", text))
|
2017-09-03 19:19:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LogInfo1Quiet is the info1 header formatter (with quiet option)
|
|
|
|
|
func LogInfo1Quiet(text string) {
|
2019-01-21 17:09:28 -05:00
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
2017-09-03 19:19:28 -04:00
|
|
|
LogInfo1(text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LogInfo2 is the info2 header formatter
|
|
|
|
|
func LogInfo2(text string) {
|
2020-02-22 04:32:51 -05:00
|
|
|
fmt.Println(fmt.Sprintf("=====> %s", text))
|
2017-09-03 19:19:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LogInfo2Quiet is the info2 header formatter (with quiet option)
|
|
|
|
|
func LogInfo2Quiet(text string) {
|
2017-10-04 00:48:02 -04:00
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
2017-09-03 19:19:28 -04:00
|
|
|
LogInfo2(text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 20:36:09 -04:00
|
|
|
// LogVerbose is the verbose log formatter
|
|
|
|
|
// prints indented text to stdout
|
2017-09-03 20:05:15 -04:00
|
|
|
func LogVerbose(text string) {
|
2020-02-22 04:32:51 -05:00
|
|
|
fmt.Println(fmt.Sprintf(" %s", text))
|
2017-09-03 20:05:15 -04:00
|
|
|
}
|
|
|
|
|
|
2021-10-24 20:20:35 -04:00
|
|
|
// LogVerboseStderr is the verbose log formatter
|
|
|
|
|
// prints indented text to stderr
|
|
|
|
|
func LogVerboseStderr(text string) {
|
|
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 20:36:09 -04:00
|
|
|
// LogVerboseQuiet is the verbose log formatter
|
|
|
|
|
// prints indented text to stdout (with quiet option)
|
2017-09-03 20:05:15 -04:00
|
|
|
func LogVerboseQuiet(text string) {
|
2019-01-21 17:09:28 -05:00
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
2017-09-03 20:05:15 -04:00
|
|
|
LogVerbose(text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 20:20:35 -04:00
|
|
|
// LogVerboseStderrQuiet is the verbose log formatter
|
|
|
|
|
// prints indented text to stderr (with quiet option)
|
|
|
|
|
func LogVerboseStderrQuiet(text string) {
|
|
|
|
|
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
|
|
|
|
|
LogVerboseStderr(text)
|
2020-08-31 15:01:14 -04:00
|
|
|
}
|
2021-10-24 20:20:35 -04:00
|
|
|
}
|
2020-08-31 15:01:14 -04:00
|
|
|
|
2021-10-24 20:20:35 -04:00
|
|
|
// LogVerboseQuietContainerLogs is the verbose log formatter for container logs
|
|
|
|
|
func LogVerboseQuietContainerLogs(containerID string) {
|
|
|
|
|
LogVerboseQuietContainerLogsTail(containerID, 0, false)
|
2020-08-30 13:25:06 -04:00
|
|
|
}
|
|
|
|
|
|
2021-01-04 01:14:40 -05:00
|
|
|
// LogVerboseQuietContainerLogsTail is the verbose log formatter for container logs with tail mode enabled
|
2021-01-07 01:43:55 -05:00
|
|
|
func LogVerboseQuietContainerLogsTail(containerID string, lines int, tail bool) {
|
|
|
|
|
args := []string{"container", "logs", containerID}
|
|
|
|
|
if lines > 0 {
|
|
|
|
|
args = append(args, "--tail", strconv.Itoa(lines))
|
|
|
|
|
}
|
|
|
|
|
if tail {
|
|
|
|
|
args = append(args, "--follow")
|
|
|
|
|
}
|
2021-10-24 20:20:35 -04:00
|
|
|
|
|
|
|
|
var mu sync.Mutex
|
2024-02-12 20:58:51 -05:00
|
|
|
result, err := CallExecCommand(ExecCommandInput{
|
|
|
|
|
Command: DockerBin(),
|
|
|
|
|
Args: args,
|
|
|
|
|
DisableStdioBuffer: true,
|
|
|
|
|
StdoutWriter: &writer{
|
|
|
|
|
mu: &mu,
|
|
|
|
|
source: "stdout",
|
|
|
|
|
},
|
|
|
|
|
StderrWriter: &writer{
|
|
|
|
|
mu: &mu,
|
|
|
|
|
source: "stderr",
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
2021-10-24 20:20:35 -04:00
|
|
|
LogExclaim(fmt.Sprintf("Failed to fetch container logs: %s", containerID))
|
2024-02-12 20:58:51 -05:00
|
|
|
return
|
2021-10-24 20:20:35 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-12 20:58:51 -05:00
|
|
|
if !tail && result.ExitCode != 0 {
|
2021-10-24 20:20:35 -04:00
|
|
|
LogExclaim(fmt.Sprintf("Failed to fetch container logs: %s", containerID))
|
2021-01-04 00:30:22 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 19:19:28 -04:00
|
|
|
// LogWarn is the warning log formatter
|
|
|
|
|
func LogWarn(text string) {
|
|
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", text))
|
|
|
|
|
}
|
2020-02-08 15:56:45 -05:00
|
|
|
|
2020-07-17 19:46:02 -04:00
|
|
|
// LogExclaim is the log exclaim formatter
|
|
|
|
|
func LogExclaim(text string) {
|
|
|
|
|
fmt.Println(fmt.Sprintf(" ! %s", text))
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-08 15:56:45 -05:00
|
|
|
// LogStderr is the stderr log formatter
|
|
|
|
|
func LogStderr(text string) {
|
|
|
|
|
fmt.Fprintln(os.Stderr, text)
|
|
|
|
|
}
|
2020-08-30 13:38:38 -04:00
|
|
|
|
|
|
|
|
// LogDebug is the debug log formatter
|
|
|
|
|
func LogDebug(text string) {
|
2021-01-02 06:11:51 -05:00
|
|
|
if os.Getenv("DOKKU_TRACE") == "1" {
|
|
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ? %s", strings.TrimPrefix(text, " ? ")))
|
2020-08-30 13:38:38 -04:00
|
|
|
}
|
|
|
|
|
}
|