Fix dependencies graph for minio source compilation (#8717)
We had messy cyclical dependency problem with `mc` due to dependencies in pkg/console, moved the pkg/console to minio for more control and also to avoid any further cyclical dependencies of `mc` clobbering up the dependencies on server. Fixes #8659master
parent
3af70b36fd
commit
0b7bd024fb
@ -0,0 +1,435 @@ |
||||
/* |
||||
* MinIO Cloud Storage, (C) 2019 MinIO, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
// Package console implements console printing helpers
|
||||
package console |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
"sync" |
||||
|
||||
"github.com/fatih/color" |
||||
"github.com/mattn/go-colorable" |
||||
"github.com/mattn/go-isatty" |
||||
) |
||||
|
||||
var ( |
||||
// DebugPrint enables/disables console debug printing.
|
||||
DebugPrint = false |
||||
|
||||
// Used by the caller to print multiple lines atomically. Exposed by Lock/Unlock methods.
|
||||
publicMutex = &sync.Mutex{} |
||||
// Used internally by console.
|
||||
privateMutex = &sync.Mutex{} |
||||
|
||||
stderrColoredOutput = colorable.NewColorableStderr() |
||||
|
||||
// Print prints a message.
|
||||
Print = func(data ...interface{}) { |
||||
consolePrint("Print", Theme["Print"], data...) |
||||
} |
||||
|
||||
// PrintC prints a message with color.
|
||||
PrintC = func(data ...interface{}) { |
||||
consolePrint("PrintC", Theme["PrintC"], data...) |
||||
} |
||||
|
||||
// Printf prints a formatted message.
|
||||
Printf = func(format string, data ...interface{}) { |
||||
consolePrintf("Print", Theme["Print"], format, data...) |
||||
} |
||||
|
||||
// Println prints a message with a newline.
|
||||
Println = func(data ...interface{}) { |
||||
consolePrintln("Print", Theme["Print"], data...) |
||||
} |
||||
|
||||
// Fatal print a error message and exit.
|
||||
Fatal = func(data ...interface{}) { |
||||
consolePrint("Fatal", Theme["Fatal"], data...) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Fatalf print a error message with a format specified and exit.
|
||||
Fatalf = func(format string, data ...interface{}) { |
||||
consolePrintf("Fatal", Theme["Fatal"], format, data...) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Fatalln print a error message with a new line and exit.
|
||||
Fatalln = func(data ...interface{}) { |
||||
consolePrintln("Fatal", Theme["Fatal"], data...) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Error prints a error message.
|
||||
Error = func(data ...interface{}) { |
||||
consolePrint("Error", Theme["Error"], data...) |
||||
} |
||||
|
||||
// Errorf print a error message with a format specified.
|
||||
Errorf = func(format string, data ...interface{}) { |
||||
consolePrintf("Error", Theme["Error"], format, data...) |
||||
} |
||||
|
||||
// Errorln prints a error message with a new line.
|
||||
Errorln = func(data ...interface{}) { |
||||
consolePrintln("Error", Theme["Error"], data...) |
||||
} |
||||
|
||||
// Info prints a informational message.
|
||||
Info = func(data ...interface{}) { |
||||
consolePrint("Info", Theme["Info"], data...) |
||||
} |
||||
|
||||
// Infof prints a informational message in custom format.
|
||||
Infof = func(format string, data ...interface{}) { |
||||
consolePrintf("Info", Theme["Info"], format, data...) |
||||
} |
||||
|
||||
// Infoln prints a informational message with a new line.
|
||||
Infoln = func(data ...interface{}) { |
||||
consolePrintln("Info", Theme["Info"], data...) |
||||
} |
||||
|
||||
// Debug prints a debug message without a new line
|
||||
// Debug prints a debug message.
|
||||
Debug = func(data ...interface{}) { |
||||
if DebugPrint { |
||||
consolePrint("Debug", Theme["Debug"], data...) |
||||
} |
||||
} |
||||
|
||||
// Debugf prints a debug message with a new line.
|
||||
Debugf = func(format string, data ...interface{}) { |
||||
if DebugPrint { |
||||
consolePrintf("Debug", Theme["Debug"], format, data...) |
||||
} |
||||
} |
||||
|
||||
// Debugln prints a debug message with a new line.
|
||||
Debugln = func(data ...interface{}) { |
||||
if DebugPrint { |
||||
consolePrintln("Debug", Theme["Debug"], data...) |
||||
} |
||||
} |
||||
|
||||
// Colorize prints message in a colorized form, dictated by the corresponding tag argument.
|
||||
Colorize = func(tag string, data interface{}) string { |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
colorized, ok := Theme[tag] |
||||
if ok { |
||||
return colorized.SprintFunc()(data) |
||||
} // else: No theme found. Return as string.
|
||||
} |
||||
return fmt.Sprint(data) |
||||
} |
||||
|
||||
// Eraseline Print in new line and adjust to top so that we don't print over the ongoing progress bar.
|
||||
Eraseline = func() { |
||||
consolePrintf("Print", Theme["Print"], "%c[2K\n", 27) |
||||
consolePrintf("Print", Theme["Print"], "%c[A", 27) |
||||
} |
||||
) |
||||
|
||||
// wrap around standard fmt functions.
|
||||
// consolePrint prints a message prefixed with message type and program name.
|
||||
func consolePrint(tag string, c *color.Color, a ...interface{}) { |
||||
privateMutex.Lock() |
||||
defer privateMutex.Unlock() |
||||
|
||||
switch tag { |
||||
case "Debug": |
||||
// if no arguments are given do not invoke debug printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
output := color.Output |
||||
color.Output = stderrColoredOutput |
||||
if isatty.IsTerminal(os.Stderr.Fd()) { |
||||
c.Print(ProgramName() + ": <DEBUG> ") |
||||
c.Print(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": <DEBUG> ") |
||||
fmt.Fprint(color.Output, a...) |
||||
} |
||||
color.Output = output |
||||
case "Fatal": |
||||
fallthrough |
||||
case "Error": |
||||
// if no arguments are given do not invoke fatal and error printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
output := color.Output |
||||
color.Output = stderrColoredOutput |
||||
if isatty.IsTerminal(os.Stderr.Fd()) { |
||||
c.Print(ProgramName() + ": <ERROR> ") |
||||
c.Print(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": <ERROR> ") |
||||
fmt.Fprint(color.Output, a...) |
||||
} |
||||
color.Output = output |
||||
case "Info": |
||||
// if no arguments are given do not invoke info printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
c.Print(ProgramName() + ": ") |
||||
c.Print(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": ") |
||||
fmt.Fprint(color.Output, a...) |
||||
} |
||||
default: |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
c.Print(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, a...) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// consolePrintf - same as print with a new line.
|
||||
func consolePrintf(tag string, c *color.Color, format string, a ...interface{}) { |
||||
privateMutex.Lock() |
||||
defer privateMutex.Unlock() |
||||
|
||||
switch tag { |
||||
case "Debug": |
||||
// if no arguments are given do not invoke debug printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
output := color.Output |
||||
color.Output = stderrColoredOutput |
||||
if isatty.IsTerminal(os.Stderr.Fd()) { |
||||
c.Print(ProgramName() + ": <DEBUG> ") |
||||
c.Printf(format, a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": <DEBUG> ") |
||||
fmt.Fprintf(color.Output, format, a...) |
||||
} |
||||
color.Output = output |
||||
case "Fatal": |
||||
fallthrough |
||||
case "Error": |
||||
// if no arguments are given do not invoke fatal and error printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
output := color.Output |
||||
color.Output = stderrColoredOutput |
||||
if isatty.IsTerminal(os.Stderr.Fd()) { |
||||
c.Print(ProgramName() + ": <ERROR> ") |
||||
c.Printf(format, a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": <ERROR> ") |
||||
fmt.Fprintf(color.Output, format, a...) |
||||
} |
||||
color.Output = output |
||||
case "Info": |
||||
// if no arguments are given do not invoke info printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
c.Print(ProgramName() + ": ") |
||||
c.Printf(format, a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": ") |
||||
fmt.Fprintf(color.Output, format, a...) |
||||
} |
||||
default: |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
c.Printf(format, a...) |
||||
} else { |
||||
fmt.Fprintf(color.Output, format, a...) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// consolePrintln - same as print with a new line.
|
||||
func consolePrintln(tag string, c *color.Color, a ...interface{}) { |
||||
privateMutex.Lock() |
||||
defer privateMutex.Unlock() |
||||
|
||||
switch tag { |
||||
case "Debug": |
||||
// if no arguments are given do not invoke debug printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
output := color.Output |
||||
color.Output = stderrColoredOutput |
||||
if isatty.IsTerminal(os.Stderr.Fd()) { |
||||
c.Print(ProgramName() + ": <DEBUG> ") |
||||
c.Println(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": <DEBUG> ") |
||||
fmt.Fprintln(color.Output, a...) |
||||
} |
||||
color.Output = output |
||||
case "Fatal": |
||||
fallthrough |
||||
case "Error": |
||||
// if no arguments are given do not invoke fatal and error printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
output := color.Output |
||||
color.Output = stderrColoredOutput |
||||
if isatty.IsTerminal(os.Stderr.Fd()) { |
||||
c.Print(ProgramName() + ": <ERROR> ") |
||||
c.Println(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": <ERROR> ") |
||||
fmt.Fprintln(color.Output, a...) |
||||
} |
||||
color.Output = output |
||||
case "Info": |
||||
// if no arguments are given do not invoke info printer.
|
||||
if len(a) == 0 { |
||||
return |
||||
} |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
c.Print(ProgramName() + ": ") |
||||
c.Println(a...) |
||||
} else { |
||||
fmt.Fprint(color.Output, ProgramName()+": ") |
||||
fmt.Fprintln(color.Output, a...) |
||||
} |
||||
default: |
||||
if isatty.IsTerminal(os.Stdout.Fd()) { |
||||
c.Println(a...) |
||||
} else { |
||||
fmt.Fprintln(color.Output, a...) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Lock console.
|
||||
func Lock() { |
||||
publicMutex.Lock() |
||||
} |
||||
|
||||
// Unlock locked console.
|
||||
func Unlock() { |
||||
publicMutex.Unlock() |
||||
} |
||||
|
||||
// ProgramName - return the name of the executable program.
|
||||
func ProgramName() string { |
||||
_, progName := filepath.Split(os.Args[0]) |
||||
return progName |
||||
} |
||||
|
||||
// Table - data to print in table format with fixed row widths.
|
||||
type Table struct { |
||||
// per-row colors
|
||||
RowColors []*color.Color |
||||
|
||||
// per-column align-right flag (aligns left by default)
|
||||
AlignRight []bool |
||||
|
||||
// Left margin width for table
|
||||
TableIndentWidth int |
||||
} |
||||
|
||||
// NewTable - create a new Table instance. Takes per-row colors and
|
||||
// per-column right-align flags and table indentation width (i.e. left
|
||||
// margin width)
|
||||
func NewTable(rowColors []*color.Color, alignRight []bool, indentWidth int) *Table { |
||||
return &Table{rowColors, alignRight, indentWidth} |
||||
} |
||||
|
||||
// DisplayTable - prints the table
|
||||
func (t *Table) DisplayTable(rows [][]string) error { |
||||
numRows := len(rows) |
||||
numCols := len(rows[0]) |
||||
if numRows != len(t.RowColors) { |
||||
return fmt.Errorf("row count and row-colors mismatch") |
||||
} |
||||
|
||||
// Compute max. column widths
|
||||
maxColWidths := make([]int, numCols) |
||||
for _, row := range rows { |
||||
if len(row) != len(t.AlignRight) { |
||||
return fmt.Errorf("col count and align-right mismatch") |
||||
} |
||||
for i, v := range row { |
||||
if len([]rune(v)) > maxColWidths[i] { |
||||
maxColWidths[i] = len([]rune(v)) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Compute per-cell text with padding and alignment applied.
|
||||
paddedText := make([][]string, numRows) |
||||
for r, row := range rows { |
||||
paddedText[r] = make([]string, numCols) |
||||
for c, cell := range row { |
||||
if t.AlignRight[c] { |
||||
fmtStr := fmt.Sprintf("%%%ds", maxColWidths[c]) |
||||
paddedText[r][c] = fmt.Sprintf(fmtStr, cell) |
||||
} else { |
||||
extraWidth := maxColWidths[c] - len([]rune(cell)) |
||||
fmtStr := fmt.Sprintf("%%s%%%ds", extraWidth) |
||||
paddedText[r][c] = fmt.Sprintf(fmtStr, cell, "") |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Draw table top border
|
||||
segments := make([]string, numCols) |
||||
for i, c := range maxColWidths { |
||||
segments[i] = strings.Repeat("─", c+2) |
||||
} |
||||
indentText := strings.Repeat(" ", t.TableIndentWidth) |
||||
border := fmt.Sprintf("%s┌%s┐", indentText, strings.Join(segments, "┬")) |
||||
fmt.Println(border) |
||||
|
||||
// Print the table with colors
|
||||
for r, row := range paddedText { |
||||
fmt.Print(indentText + "│ ") |
||||
for c, text := range row { |
||||
t.RowColors[r].Print(text) |
||||
if c != numCols-1 { |
||||
fmt.Print(" │ ") |
||||
} |
||||
} |
||||
fmt.Println(" │") |
||||
} |
||||
|
||||
// Draw table bottom border
|
||||
border = fmt.Sprintf("%s└%s┘", indentText, strings.Join(segments, "┴")) |
||||
fmt.Println(border) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// RewindLines - uses terminal escape symbols to clear and rewind
|
||||
// upwards on the console for `n` lines.
|
||||
func RewindLines(n int) { |
||||
for i := 0; i < n; i++ { |
||||
fmt.Printf("\033[1A\033[K") |
||||
} |
||||
} |
@ -0,0 +1,37 @@ |
||||
/* |
||||
* MinIO Cloud Storage, (C) 2019 MinIO, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package console |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/fatih/color" |
||||
) |
||||
|
||||
func TestSetColor(t *testing.T) { |
||||
SetColor("unknown", color.New(color.FgWhite)) |
||||
_, ok := Theme["unknown"] |
||||
if !ok { |
||||
t.Fatal("missing theme") |
||||
} |
||||
} |
||||
|
||||
func TestColorLock(t *testing.T) { |
||||
Lock() |
||||
Print("") // Test for deadlocks.
|
||||
Unlock() |
||||
} |
@ -0,0 +1,54 @@ |
||||
/* |
||||
* MinIO Cloud Storage, (C) 2019 MinIO, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package console |
||||
|
||||
import "github.com/fatih/color" |
||||
|
||||
var ( |
||||
// Theme contains default color mapping.
|
||||
Theme = map[string]*color.Color{ |
||||
"Debug": color.New(color.FgWhite, color.Faint, color.Italic), |
||||
"Fatal": color.New(color.FgRed, color.Italic, color.Bold), |
||||
"Error": color.New(color.FgYellow, color.Italic), |
||||
"Info": color.New(color.FgGreen, color.Bold), |
||||
"Print": color.New(), |
||||
"PrintB": color.New(color.FgBlue, color.Bold), |
||||
"PrintC": color.New(color.FgGreen, color.Bold), |
||||
} |
||||
) |
||||
|
||||
// SetColorOff disables coloring for the entire session.
|
||||
func SetColorOff() { |
||||
privateMutex.Lock() |
||||
defer privateMutex.Unlock() |
||||
color.NoColor = true |
||||
} |
||||
|
||||
// SetColorOn enables coloring for the entire session.
|
||||
func SetColorOn() { |
||||
privateMutex.Lock() |
||||
defer privateMutex.Unlock() |
||||
color.NoColor = false |
||||
} |
||||
|
||||
// SetColor sets a color for a particular tag.
|
||||
func SetColor(tag string, cl *color.Color) { |
||||
privateMutex.Lock() |
||||
defer privateMutex.Unlock() |
||||
// add new theme
|
||||
Theme[tag] = cl |
||||
} |
Loading…
Reference in new issue