Enhance fatal errors printing of common issues seen by users (#5878)

This commit is contained in:
Anis Elleuch 2018-05-08 19:04:36 -07:00 committed by Dee Koder
parent 54cd29b51c
commit 32700fca52
20 changed files with 591 additions and 135 deletions

View File

@ -22,7 +22,6 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
)
@ -44,19 +43,19 @@ func parsePublicCertFile(certFile string) (x509Certs []*x509.Certificate, err er
for len(current) > 0 {
var pemBlock *pem.Block
if pemBlock, current = pem.Decode(current); pemBlock == nil {
return nil, fmt.Errorf("Could not read PEM block from file %s", certFile)
return nil, uiErrSSLUnexpectedData(nil).Msg("Could not read PEM block from file %s", certFile)
}
var x509Cert *x509.Certificate
if x509Cert, err = x509.ParseCertificate(pemBlock.Bytes); err != nil {
return nil, err
return nil, uiErrSSLUnexpectedData(err)
}
x509Certs = append(x509Certs, x509Cert)
}
if len(x509Certs) == 0 {
return nil, fmt.Errorf("Empty public certificate file %s", certFile)
return nil, uiErrSSLUnexpectedData(nil).Msg("Empty public certificate file %s", certFile)
}
return x509Certs, nil
@ -107,28 +106,32 @@ func getRootCAs(certsCAsDir string) (*x509.CertPool, error) {
func loadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) {
certPEMBlock, err := ioutil.ReadFile(certFile)
if err != nil {
return tls.Certificate{}, fmt.Errorf("TLS: failed to read cert file: %v", err)
return tls.Certificate{}, uiErrSSLUnexpectedError(err)
}
keyPEMBlock, err := ioutil.ReadFile(keyFile)
if err != nil {
return tls.Certificate{}, fmt.Errorf("TLS: failed to read private key: %v", err)
return tls.Certificate{}, uiErrSSLUnexpectedError(err)
}
key, rest := pem.Decode(keyPEMBlock)
if len(rest) > 0 {
return tls.Certificate{}, fmt.Errorf("TLS: private key contains additional data")
return tls.Certificate{}, uiErrSSLUnexpectedData(nil).Msg("The private key contains additional data")
}
if x509.IsEncryptedPEMBlock(key) {
password, ok := os.LookupEnv(TLSPrivateKeyPassword)
if !ok {
return tls.Certificate{}, fmt.Errorf("TLS: private key is encrypted but no password is present - set env var: %s", TLSPrivateKeyPassword)
return tls.Certificate{}, uiErrSSLNoPassword(nil)
}
decryptedKey, decErr := x509.DecryptPEMBlock(key, []byte(password))
if decErr != nil {
return tls.Certificate{}, fmt.Errorf("TLS: failed to decrypt private key: %v", decErr)
return tls.Certificate{}, uiErrSSLWrongPassword(decErr)
}
keyPEMBlock = pem.EncodeToMemory(&pem.Block{Type: key.Type, Bytes: decryptedKey})
}
return tls.X509KeyPair(certPEMBlock, keyPEMBlock)
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil {
return tls.Certificate{}, uiErrSSLUnexpectedData(nil).Msg(err.Error())
}
return cert, nil
}
func getSSLConfig() (x509Certs []*x509.Certificate, rootCAs *x509.CertPool, tlsCert *tls.Certificate, secureConn bool, err error) {
@ -149,7 +152,7 @@ func getSSLConfig() (x509Certs []*x509.Certificate, rootCAs *x509.CertPool, tlsC
if priv, ok := cert.PrivateKey.(crypto.Signer); ok {
if pub, ok := priv.Public().(*ecdsa.PublicKey); ok {
if name := pub.Params().Name; name == "P-384" || name == "P-521" { // unfortunately there is no cleaner way to check
return nil, nil, nil, false, fmt.Errorf("TLS: the ECDSA curve '%s' is not supported", name)
return nil, nil, nil, false, uiErrSSLUnexpectedData(nil).Msg("tls: the ECDSA curve '%s' is not supported", name)
}
}

View File

@ -45,7 +45,7 @@ func initConfig() {
// Config file does not exist, we create it fresh and return upon success.
if isFile(getConfigFile()) {
logger.FatalIf(migrateConfig(), "Config migration failed.")
logger.FatalIf(loadConfig(), "Unable to load config version: '%s'.", serverConfigVersion)
logger.FatalIf(loadConfig(), "Unable to load the configuration file")
} else {
logger.FatalIf(newConfig(), "Unable to initialize minio config for the first time.")
logger.Info("Created minio configuration file successfully at " + getConfigDir())
@ -95,7 +95,9 @@ func handleCommonEnvVars() {
secretKey := os.Getenv("MINIO_SECRET_KEY")
if accessKey != "" && secretKey != "" {
cred, err := auth.CreateCredentials(accessKey, secretKey)
logger.FatalIf(err, "Invalid access/secret Key set in environment.")
if err != nil {
logger.Fatal(uiErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment")
}
// credential Envs are set globally.
globalIsEnvCreds = true
@ -105,7 +107,7 @@ func handleCommonEnvVars() {
if browser := os.Getenv("MINIO_BROWSER"); browser != "" {
browserFlag, err := ParseBrowserFlag(browser)
if err != nil {
logger.FatalIf(errors.New("invalid value"), "Unknown value %s in MINIO_BROWSER environment variable.", browser)
logger.Fatal(uiErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Unable to validate MINIO_BROWSER environment variable")
}
// browser Envs are set globally, this does not represent
@ -128,18 +130,26 @@ func handleCommonEnvVars() {
if drives := os.Getenv("MINIO_CACHE_DRIVES"); drives != "" {
driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter))
logger.FatalIf(err, "Invalid value set in environment variable MINIO_CACHE_DRIVES %s.", drives)
if err != nil {
logger.Fatal(err, "Unable to parse MINIO_CACHE_DRIVES value (%s)", drives)
}
globalCacheDrives = driveList
globalIsDiskCacheEnabled = true
}
if excludes := os.Getenv("MINIO_CACHE_EXCLUDE"); excludes != "" {
excludeList, err := parseCacheExcludes(strings.Split(excludes, cacheEnvDelimiter))
logger.FatalIf(err, "Invalid value set in environment variable MINIO_CACHE_EXCLUDE %s.", excludes)
if err != nil {
logger.Fatal(err, "Unable to parse MINIO_CACHE_EXCLUDE value (`%s`).", excludes)
}
globalCacheExcludes = excludeList
}
if expiryStr := os.Getenv("MINIO_CACHE_EXPIRY"); expiryStr != "" {
expiry, err := strconv.Atoi(expiryStr)
logger.FatalIf(err, "Invalid value set in environment variable MINIO_CACHE_EXPIRY %s.", expiryStr)
if err != nil {
logger.Fatal(uiErrInvalidCacheExpiryValue(err), "Unable to parse MINIO_CACHE_EXPIRY value (`%s`)", expiryStr)
}
globalCacheExpiry = expiry
}

View File

@ -287,7 +287,7 @@ func getValidConfig() (*serverConfig, error) {
func loadConfig() error {
srvCfg, err := getValidConfig()
if err != nil {
return err
return uiErrInvalidConfig(nil).Msg(err.Error())
}
// If env is set override the credentials from config file.

View File

@ -18,7 +18,6 @@ package cmd
import (
"encoding/json"
"fmt"
"path/filepath"
)
@ -54,7 +53,7 @@ func (cfg *CacheConfig) UnmarshalJSON(data []byte) (err error) {
func parseCacheDrives(drives []string) ([]string, error) {
for _, d := range drives {
if !filepath.IsAbs(d) {
return nil, fmt.Errorf("cache dir should be absolute path: %s", d)
return nil, uiErrInvalidCacheDrivesValue(nil).Msg("cache dir should be absolute path: %s", d)
}
}
return drives, nil
@ -64,10 +63,10 @@ func parseCacheDrives(drives []string) ([]string, error) {
func parseCacheExcludes(excludes []string) ([]string, error) {
for _, e := range excludes {
if len(e) == 0 {
return nil, fmt.Errorf("cache exclude path (%s) cannot be empty", e)
return nil, uiErrInvalidCacheExcludesValue(nil).Msg("cache exclude path (%s) cannot be empty", e)
}
if hasPrefix(e, slashSeparator) {
return nil, fmt.Errorf("cache exclude pattern (%s) cannot start with / as prefix", e)
return nil, uiErrInvalidCacheExcludesValue(nil).Msg("cache exclude pattern (%s) cannot start with / as prefix", e)
}
}
return excludes, nil

View File

@ -70,11 +70,10 @@ func getSetIndexes(args []string, totalSizes []uint64) (setIndexes [][]uint64, e
}
setIndexes = make([][]uint64, len(totalSizes))
for i, totalSize := range totalSizes {
for _, totalSize := range totalSizes {
// Check if totalSize has minimum range upto setSize
if totalSize < setSizes[0] {
return nil, fmt.Errorf("Invalid inputs (%s). Ellipses range or number of args %d should be atleast divisible by least possible set size %d",
args[i], totalSize, setSizes[0])
return nil, uiErrInvalidNumberOfErasureEndpoints(nil)
}
}
@ -103,8 +102,7 @@ func getSetIndexes(args []string, totalSizes []uint64) (setIndexes [][]uint64, e
// Check whether setSize is with the supported range.
if !isValidSetSize(setSize) {
return nil, fmt.Errorf("Invalid inputs (%s). Ellipses range or number of args %d should be atleast divisible by least possible set size %d",
args, setSize, setSizes[0])
return nil, uiErrInvalidNumberOfErasureEndpoints(nil)
}
for i := range totalSizes {
@ -167,14 +165,14 @@ func parseEndpointSet(args ...string) (ep endpointSet, err error) {
for i, arg := range args {
patterns, perr := ellipses.FindEllipsesPatterns(arg)
if perr != nil {
return endpointSet{}, perr
return endpointSet{}, uiErrInvalidErasureEndpoints(nil).Msg(perr.Error())
}
argPatterns[i] = patterns
}
ep.setIndexes, err = getSetIndexes(args, getTotalSizes(argPatterns))
if err != nil {
return endpointSet{}, err
return endpointSet{}, uiErrInvalidErasureEndpoints(nil).Msg(err.Error())
}
ep.argPatterns = argPatterns
@ -223,7 +221,7 @@ func getAllSets(args ...string) ([][]string, error) {
for _, sargs := range setArgs {
for _, arg := range sargs {
if uniqueArgs.Contains(arg) {
return nil, fmt.Errorf("Input args (%s) has duplicate ellipses", args)
return nil, uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("Input args (%s) has duplicate ellipses", args))
}
uniqueArgs.Add(arg)
}

View File

@ -252,14 +252,14 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
return serverAddr, endpoints, setupType, err
}
if endpoint.Type() != PathEndpointType {
return serverAddr, endpoints, setupType, fmt.Errorf("use path style endpoint for FS setup")
return serverAddr, endpoints, setupType, uiErrInvalidFSEndpoint(nil).Msg("use path style endpoint for FS setup")
}
endpoints = append(endpoints, endpoint)
setupType = FSSetupType
// Check for cross device mounts if any.
if err = checkCrossDeviceMounts(endpoints); err != nil {
return serverAddr, endpoints, setupType, err
return serverAddr, endpoints, setupType, uiErrInvalidFSEndpoint(nil).Msg(err.Error())
}
return serverAddr, endpoints, setupType, nil
}
@ -270,12 +270,12 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
var eps EndpointList
eps, err = NewEndpointList(iargs...)
if err != nil {
return serverAddr, endpoints, setupType, err
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error())
}
// Check for cross device mounts if any.
if err = checkCrossDeviceMounts(eps); err != nil {
return serverAddr, endpoints, setupType, err
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error())
}
for _, ep := range eps {
@ -316,7 +316,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
// No local endpoint found.
if localEndpointCount == 0 {
return serverAddr, endpoints, setupType, fmt.Errorf("no endpoint found for this host")
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg("no endpoint pointing to the local machine is found")
}
// Check whether same path is not used in endpoints of a host on different port.
@ -331,8 +331,8 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
hostIPSet, _ := getHostIP4(host)
if IPSet, ok := pathIPMap[endpoint.Path]; ok {
if !IPSet.Intersection(hostIPSet).IsEmpty() {
err = fmt.Errorf("path '%s' can not be served by different port on same address", endpoint.Path)
return serverAddr, endpoints, setupType, err
return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' can not be served by different port on same address", endpoint.Path))
}
pathIPMap[endpoint.Path] = IPSet.Union(hostIPSet)
} else {
@ -349,8 +349,8 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
continue
}
if localPathSet.Contains(endpoint.Path) {
err = fmt.Errorf("path '%s' cannot be served by different address on same server", endpoint.Path)
return serverAddr, endpoints, setupType, err
return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' cannot be served by different address on same server", endpoint.Path))
}
localPathSet.Add(endpoint.Path)
}
@ -360,12 +360,11 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
{
if !localPortSet.Contains(serverAddrPort) {
if len(localPortSet) > 1 {
err = fmt.Errorf("port number in server address must match with one of the port in local endpoints")
} else {
err = fmt.Errorf("server address and local endpoint have different ports")
return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg("port number in server address must match with one of the port in local endpoints")
}
return serverAddr, endpoints, setupType, err
return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg("server address and local endpoint have different ports")
}
}
@ -374,7 +373,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
// If all endpoints have same port number, then this is XL setup using URL style endpoints.
if len(localPortSet) == 1 {
if len(localServerAddrSet) > 1 {
// TODO: Eventhough all endpoints are local, the local host is referred by different IP/name.
// TODO: Even though all endpoints are local, the local host is referred by different IP/name.
// eg '172.0.0.1', 'localhost' and 'mylocalhostname' point to same local host.
//
// In this case, we bind to 0.0.0.0 ie to all interfaces.
@ -388,7 +387,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
return serverAddr, endpoints, setupType, nil
}
// Eventhough all endpoints are local, but those endpoints use different ports.
// Even though all endpoints are local, but those endpoints use different ports.
// This means it is DistXL setup.
} else {
// This is DistXL setup.

View File

@ -225,8 +225,8 @@ func TestCreateEndpoints(t *testing.T) {
{"localhost:10000", [][]string{{"./d1"}}, "localhost:10000", EndpointList{Endpoint{URL: &url.URL{Path: "d1"}, IsLocal: true}}, FSSetupType, nil},
{"localhost:10000", [][]string{{`\d1`}}, "localhost:10000", EndpointList{Endpoint{URL: &url.URL{Path: `\d1`}, IsLocal: true}}, FSSetupType, nil},
{"localhost:10000", [][]string{{`.\d1`}}, "localhost:10000", EndpointList{Endpoint{URL: &url.URL{Path: `.\d1`}, IsLocal: true}}, FSSetupType, nil},
{":8080", [][]string{{"https://example.org/d1", "https://example.org/d2", "https://example.org/d3", "https://example.org/d4"}}, "", EndpointList{}, -1, fmt.Errorf("no endpoint found for this host")},
{":8080", [][]string{{"https://example.org/d1", "https://example.com/d2", "https://example.net:8000/d3", "https://example.edu/d1"}}, "", EndpointList{}, -1, fmt.Errorf("no endpoint found for this host")},
{":8080", [][]string{{"https://example.org/d1", "https://example.org/d2", "https://example.org/d3", "https://example.org/d4"}}, "", EndpointList{}, -1, fmt.Errorf("no endpoint pointing to the local machine is found")},
{":8080", [][]string{{"https://example.org/d1", "https://example.com/d2", "https://example.net:8000/d3", "https://example.edu/d1"}}, "", EndpointList{}, -1, fmt.Errorf("no endpoint pointing to the local machine is found")},
{"localhost:9000", [][]string{{"https://127.0.0.1:9000/d1", "https://localhost:9001/d1", "https://example.com/d1", "https://example.com/d2"}}, "", EndpointList{}, -1, fmt.Errorf("path '/d1' can not be served by different port on same address")},
{"localhost:9000", [][]string{{"https://127.0.0.1:8000/d1", "https://localhost:9001/d2", "https://example.com/d1", "https://example.com/d2"}}, "", EndpointList{}, -1, fmt.Errorf("port number in server address must match with one of the port in local endpoints")},
{"localhost:10000", [][]string{{"https://127.0.0.1:8000/d1", "https://localhost:8000/d2", "https://example.com/d1", "https://example.com/d2"}}, "", EndpointList{}, -1, fmt.Errorf("server address and local endpoint have different ports")},

View File

@ -24,7 +24,6 @@ import (
"path"
"time"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/lock"
)
@ -113,7 +112,6 @@ func formatFSMigrateV1ToV2(ctx context.Context, wlk *lock.LockedFile, fsPath str
}
if err = os.MkdirAll(path.Join(fsPath, minioMetaMultipartBucket), 0755); err != nil {
logger.LogIf(ctx, err)
return err
}
@ -147,7 +145,7 @@ func formatFSMigrate(ctx context.Context, wlk *lock.LockedFile, fsPath string) e
return err
}
if version != formatFSVersionV2 {
return fmt.Errorf(`%s file: expected FS version: %s, found FS version: %s`, formatConfigFile, formatFSVersionV2, version)
return uiErrUnexpectedBackendVersion(fmt.Errorf(`%s file: expected FS version: %s, found FS version: %s`, formatConfigFile, formatFSVersionV2, version))
}
return nil
}
@ -158,7 +156,6 @@ func createFormatFS(ctx context.Context, fsFormatPath string) error {
// file stored in minioMetaBucket(.minio.sys) directory.
lk, err := lock.TryLockedOpenFile(fsFormatPath, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
logger.LogIf(ctx, err)
return err
}
// Close the locked file upon return.
@ -166,7 +163,6 @@ func createFormatFS(ctx context.Context, fsFormatPath string) error {
fi, err := lk.Stat()
if err != nil {
logger.LogIf(ctx, err)
return err
}
if fi.Size() != 0 {
@ -195,7 +191,6 @@ func initFormatFS(ctx context.Context, fsPath string) (rlk *lock.RLockedFile, er
var fi os.FileInfo
fi, err = rlk.Stat()
if err != nil {
logger.LogIf(ctx, err)
return nil, err
}
isEmpty = fi.Size() == 0
@ -214,7 +209,6 @@ func initFormatFS(ctx context.Context, fsPath string) (rlk *lock.RLockedFile, er
continue
}
if err != nil {
logger.LogIf(ctx, err)
return nil, err
}
// After successfully creating format.json try to hold a read-lock on
@ -222,13 +216,11 @@ func initFormatFS(ctx context.Context, fsPath string) (rlk *lock.RLockedFile, er
continue
}
if err != nil {
logger.LogIf(ctx, err)
return nil, err
}
formatBackend, err := formatMetaGetFormatBackendFS(rlk)
if err != nil {
logger.LogIf(ctx, err)
return nil, err
}
if formatBackend != formatBackendFS {

View File

@ -19,7 +19,6 @@ package cmd
import (
"context"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
@ -80,6 +79,7 @@ func initMetaVolumeFS(fsPath, fsUUID string) error {
// optimizing all other calls. Create minio meta volume,
// if it doesn't exist yet.
metaBucketPath := pathJoin(fsPath, minioMetaBucket)
if err := os.MkdirAll(metaBucketPath, 0777); err != nil {
return err
}
@ -103,7 +103,7 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
var err error
if fsPath, err = checkPathValid(fsPath); err != nil {
return nil, err
return nil, uiErrUnableToWriteInBackend(err)
}
// Assign a new UUID for FS minio mode. Each server instance
@ -112,7 +112,7 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
// Initialize meta volume, if volume already exists ignores it.
if err = initMetaVolumeFS(fsPath, fsUUID); err != nil {
return nil, fmt.Errorf("Unable to initialize '.minio.sys' meta volume, %s", err)
return nil, err
}
// Initialize `format.json`, this function also returns.
@ -142,12 +142,12 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
// Initialize notification system.
if err = globalNotificationSys.Init(fs); err != nil {
return nil, fmt.Errorf("Unable to initialize notification system. %v", err)
return nil, uiErrUnableToReadFromBackend(err).Msg("Unable to initialize notification system")
}
// Initialize policy system.
if err = globalPolicySys.Init(fs); err != nil {
return nil, fmt.Errorf("Unable to initialize policy system. %v", err)
return nil, uiErrUnableToReadFromBackend(err).Msg("Unable to initialize policy system")
}
go fs.cleanupStaleMultipartUploads(ctx, globalMultipartCleanupInterval, globalMultipartExpiry, globalServiceDoneCh)

View File

@ -112,6 +112,10 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
logger.FatalIf(errUnexpected, "Gateway implementation not initialized, exiting.")
}
// Disable logging until gateway initialization is complete, any
// error during initialization will be shown as a fatal message
logger.Disable = true
// Validate if we have access, secret set through environment.
gatewayName := gw.Name()
if ctx.Args().First() == "help" {
@ -219,5 +223,8 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
printGatewayStartupMessage(getAPIEndpoints(gatewayAddr), gatewayName)
}
// Reenable logging
logger.Disable = false
handleSignals()
}

View File

@ -192,9 +192,12 @@ var (
// global colors.
var (
colorBold = color.New(color.Bold).SprintFunc()
colorBlue = color.New(color.FgBlue).SprintfFunc()
colorYellow = color.New(color.FgYellow).SprintfFunc()
colorBold = color.New(color.Bold).SprintFunc()
colorRed = color.New(color.FgRed).SprintfFunc()
colorBlue = color.New(color.FgBlue).SprintfFunc()
colorYellow = color.New(color.FgYellow).SprintfFunc()
colorBgYellow = color.New(color.BgYellow).SprintfFunc()
colorBlack = color.New(color.FgBlack).SprintfFunc()
)
// Returns minio global information, as a key value map.

View File

@ -27,14 +27,8 @@ import (
"strings"
"time"
"github.com/fatih/color"
c "github.com/minio/mc/pkg/console"
)
// global colors.
var (
colorBold = color.New(color.Bold).SprintFunc()
colorRed = color.New(color.FgRed).SprintfFunc()
"golang.org/x/crypto/ssh/terminal"
)
// Disable disables all logging, false by default. (used for "go test")
@ -47,9 +41,9 @@ type Level int8
// Enumerated level types
const (
Information Level = iota + 1
Error
Fatal
InformationLvl Level = iota + 1
ErrorLvl
FatalLvl
)
const loggerTimeFormat string = "15:04:05 MST 01/02/2006"
@ -64,11 +58,11 @@ var matchingFuncNames = [...]string{
func (level Level) String() string {
var lvlStr string
switch level {
case Information:
case InformationLvl:
lvlStr = "INFO"
case Error:
case ErrorLvl:
lvlStr = "ERROR"
case Fatal:
case FatalLvl:
lvlStr = "FATAL"
}
return lvlStr
@ -82,10 +76,9 @@ type Console interface {
}
func consoleLog(console Console, msg string, args ...interface{}) {
if Disable {
return
}
if jsonFlag {
// Strip escape control characters from json message
msg = ansiRE.ReplaceAllLiteralString(msg, "")
console.json(msg, args...)
} else if quiet {
console.quiet(msg, args...)
@ -124,6 +117,8 @@ type logEntry struct {
// jsonFlag: Display in JSON format, if enabled
var (
quiet, jsonFlag bool
// Custom function to format error
errorFmtFunc func(string, error, bool) string
)
// EnableQuiet - turns quiet option on.
@ -137,11 +132,18 @@ func EnableJSON() {
quiet = true
}
// RegisterUIError registers the specified rendering function. This latter
// will be called for a pretty rendering of fatal errors.
func RegisterUIError(f func(string, error, bool) string) {
errorFmtFunc = f
}
// Init sets the trimStrings to possible GOPATHs
// and GOROOT directories. Also append github.com/minio/minio
// This is done to clean up the filename, when stack trace is
// displayed when an error happens.
func Init(goPath string) {
var goPathList []string
var defaultgoPathList []string
// Add all possible GOPATH paths into trimStrings
@ -182,8 +184,8 @@ func trimTrace(f string) string {
return filepath.FromSlash(f)
}
func getSource() string {
pc, file, lineNumber, ok := runtime.Caller(5)
func getSource(level int) string {
pc, file, lineNumber, ok := runtime.Caller(level)
if ok {
// Clean up the common prefixes
file = trimTrace(file)
@ -225,7 +227,8 @@ func getTrace(traceLevel int) []string {
return trace
}
// LogIf :
// LogIf prints a detailed error message during
// the execution of the server.
func LogIf(ctx context.Context, err error) {
if Disable {
return
@ -251,15 +254,17 @@ func LogIf(ctx context.Context, err error) {
tags[entry.Key] = entry.Val
}
// Get the cause for the Error
message := err.Error()
// Get full stack trace
trace := getTrace(2)
// Get the cause for the Error
message := err.Error()
// Output the formatted log message at console
var output string
if jsonFlag {
logJSON, err := json.Marshal(&logEntry{
Level: Error.String(),
Level: ErrorLvl.String(),
RemoteHost: req.RemoteHost,
RequestID: req.RequestID,
UserAgent: req.UserAgent,
@ -317,9 +322,10 @@ func LogIf(ctx context.Context, err error) {
tagString = "\n " + tagString
}
var msg = colorFgRed(colorBold(message))
output = fmt.Sprintf("\n%s\n%s%s%s%s\nError: %s%s\n%s",
apiString, timeString, requestID, remoteHost, userAgent,
colorRed(colorBold(message)), tagString, strings.Join(trace, "\n"))
msg, tagString, strings.Join(trace, "\n"))
}
fmt.Println(output)
}
@ -334,17 +340,28 @@ func CriticalIf(ctx context.Context, err error) {
}
}
// FatalIf :
// Just fatal error message, no stack trace
// It'll be called for input validation failures
// FatalIf is similar to Fatal() but it ignores passed nil error
func FatalIf(err error, msg string, data ...interface{}) {
if err != nil {
if msg != "" {
consoleLog(fatalMessage, msg, data...)
} else {
consoleLog(fatalMessage, err.Error())
}
if err == nil {
return
}
fatal(err, msg, data...)
}
// Fatal prints only fatal error message without no stack trace
// it will be called for input validation failures
func Fatal(err error, msg string, data ...interface{}) {
fatal(err, msg, data...)
}
func fatal(err error, msg string, data ...interface{}) {
var errMsg string
if msg != "" {
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
} else {
errMsg = err.Error()
}
consoleLog(fatalMessage, errMsg)
}
var fatalMessage fatalMsg
@ -354,14 +371,15 @@ type fatalMsg struct {
func (f fatalMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&logEntry{
Level: Fatal.String(),
Level: FatalLvl.String(),
Time: time.Now().UTC().Format(time.RFC3339Nano),
Trace: &traceEntry{Message: fmt.Sprintf(msg, args...), Source: []string{getSource()}},
Trace: &traceEntry{Message: fmt.Sprintf(msg, args...), Source: []string{getSource(6)}},
})
if err != nil {
panic(err)
}
fmt.Println(string(logJSON))
os.Exit(1)
}
@ -370,9 +388,65 @@ func (f fatalMsg) quiet(msg string, args ...interface{}) {
f.pretty(msg, args...)
}
var (
logTag = "ERROR"
logBanner = colorBgRed(colorFgWhite(colorBold(logTag))) + " "
emptyBanner = colorBgRed(strings.Repeat(" ", len(logTag))) + " "
minimumWidth = 80
bannerWidth = len(logTag) + 1
)
func (f fatalMsg) pretty(msg string, args ...interface{}) {
// Build the passed error message
errMsg := fmt.Sprintf(msg, args...)
fmt.Println(colorRed(colorBold("Error: " + errMsg)))
// Check terminal width
termWidth, _, err := terminal.GetSize(0)
if err != nil || termWidth < minimumWidth {
termWidth = minimumWidth
}
// Calculate available widht without the banner
width := termWidth - bannerWidth
tagPrinted := false
// Print the error message: the following code takes care
// of splitting error text and always pretty printing the
// red banner along with the error message. Since the error
// message itself contains some colored text, we needed
// to use some ANSI control escapes to cursor color state
// and freely move in the screen.
for _, line := range strings.Split(errMsg, "\n") {
if len(line) == 0 {
// No more text to print, just quit.
break
}
for {
// Save the attributes of the current cursor helps
// us save the text color of the passed error message
ansiSaveAttributes()
// Print banner with or without the log tag
if !tagPrinted {
fmt.Print(logBanner)
tagPrinted = true
} else {
fmt.Print(emptyBanner)
}
// Restore the text color of the error message
ansiRestoreAttributes()
ansiMoveRight(bannerWidth)
// Continue error message printing
if len(line) > width {
fmt.Println(line[:width])
line = line[width:]
} else {
fmt.Println(line)
break
}
}
}
// Exit because this is a fatal error message
os.Exit(1)
}
@ -383,7 +457,7 @@ type infoMsg struct {
func (i infoMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&logEntry{
Level: Information.String(),
Level: InformationLvl.String(),
Message: fmt.Sprintf(msg, args...),
Time: time.Now().UTC().Format(time.RFC3339Nano),
})

52
cmd/logger/utils.go Normal file
View File

@ -0,0 +1,52 @@
/*
* Minio Cloud Storage, (C) 2018 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 logger
import (
"fmt"
"regexp"
"github.com/fatih/color"
)
// Global colors.
var (
colorBold = color.New(color.Bold).SprintFunc()
colorFgRed = color.New(color.FgRed).SprintfFunc()
colorBgRed = color.New(color.BgRed).SprintfFunc()
colorFgWhite = color.New(color.FgWhite).SprintfFunc()
)
var ansiRE = regexp.MustCompile("(\x1b[^m]*m)")
// Print ANSI Control escape
func ansiEscape(format string, args ...interface{}) {
var Esc = "\x1b"
fmt.Printf("%s%s", Esc, fmt.Sprintf(format, args...))
}
func ansiMoveRight(n int) {
ansiEscape("[%dC", n)
}
func ansiSaveAttributes() {
ansiEscape("7")
}
func ansiRestoreAttributes() {
ansiEscape("8")
}

View File

@ -352,15 +352,15 @@ func sameLocalAddrs(addr1, addr2 string) (bool, error) {
func CheckLocalServerAddr(serverAddr string) error {
host, port, err := net.SplitHostPort(serverAddr)
if err != nil {
return err
return uiErrInvalidAddressFlag(err)
}
// Check whether port is a valid port number.
p, err := strconv.Atoi(port)
if err != nil {
return fmt.Errorf("invalid port number")
return uiErrInvalidAddressFlag(err).Msg("invalid port number")
} else if p < 1 || p > 65535 {
return fmt.Errorf("port number must be between 1 to 65535")
return uiErrInvalidAddressFlag(nil).Msg("port number must be between 1 to 65535")
}
// 0.0.0.0 is a wildcard address and refers to local network
@ -372,7 +372,7 @@ func CheckLocalServerAddr(serverAddr string) error {
return err
}
if !isLocalHost {
return fmt.Errorf("host in server address should be this server")
return uiErrInvalidAddressFlag(nil).Msg("host in server address should be this server")
}
}

View File

@ -18,10 +18,10 @@ package cmd
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"runtime"
"syscall"
"github.com/minio/cli"
@ -118,26 +118,26 @@ func serverHandleCmdArgs(ctx *cli.Context) {
// Server address.
serverAddr := ctx.String("address")
logger.FatalIf(CheckLocalServerAddr(serverAddr), "Invalid address %s in command line argument.", serverAddr)
logger.FatalIf(CheckLocalServerAddr(serverAddr), "Unable to validate passed arguments")
var setupType SetupType
var err error
if len(ctx.Args()) > serverCommandLineArgsMax {
logger.FatalIf(errInvalidArgument, "Invalid total number of arguments (%d) passed, supported upto 32 unique arguments", len(ctx.Args()))
uErr := uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("Invalid total number of endpoints (%d) passed, supported upto 32 unique arguments", len(ctx.Args())))
logger.FatalIf(uErr, "Unable to validate passed endpoints")
}
globalMinioAddr, globalEndpoints, setupType, globalXLSetCount, globalXLSetDriveCount, err = createServerEndpoints(serverAddr, ctx.Args()...)
logger.FatalIf(err, "Invalid command line arguments server=%s, args=%s", serverAddr, ctx.Args())
logger.FatalIf(err, "Invalid command line arguments")
globalMinioHost, globalMinioPort = mustSplitHostPort(globalMinioAddr)
if runtime.GOOS == "darwin" {
// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
// to IPv6 address ie minio will start listening on IPv6 address whereas another
// (non-)minio process is listening on IPv4 of given port.
// To avoid this error sutiation we check for port availability only for macOS.
logger.FatalIf(checkPortAvailability(globalMinioPort), "Port %d already in use", globalMinioPort)
}
// On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back
// to IPv6 address ie minio will start listening on IPv6 address whereas another
// (non-)minio process is listening on IPv4 of given port.
// To avoid this error sutiation we check for port availability.
logger.FatalIf(checkPortAvailability(globalMinioPort), "Unable to start the server")
globalIsXL = (setupType == XLSetupType)
globalIsDistXL = (setupType == DistXLSetupType)
@ -168,6 +168,10 @@ func serverMain(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "server", 1)
}
// Disable logging until server initialization is complete, any
// error during initialization will be shown as a fatal message
logger.Disable = true
// Get "json" flag from command line argument and
// enable json and quite modes if jason flag is turned on.
jsonFlag := ctx.IsSet("json") || ctx.GlobalIsSet("json")
@ -181,6 +185,8 @@ func serverMain(ctx *cli.Context) {
logger.EnableQuiet()
}
logger.RegisterUIError(fmtError)
// Handle all server command args.
serverHandleCmdArgs(ctx)
@ -188,7 +194,7 @@ func serverMain(ctx *cli.Context) {
serverHandleEnvVars()
// Create certs path.
logger.FatalIf(createConfigDir(), "Unable to create configuration directories.")
logger.FatalIf(createConfigDir(), "Unable to initialize configuration files")
// Initialize server config.
initConfig()
@ -196,15 +202,15 @@ func serverMain(ctx *cli.Context) {
// Check and load SSL certificates.
var err error
globalPublicCerts, globalRootCAs, globalTLSCertificate, globalIsSSL, err = getSSLConfig()
logger.FatalIf(err, "Invalid SSL certificate file")
logger.FatalIf(err, "Unable to load the TLS configuration")
// Is distributed setup, error out if no certificates are found for HTTPS endpoints.
if globalIsDistXL {
if globalEndpoints.IsHTTPS() && !globalIsSSL {
logger.FatalIf(errInvalidArgument, "No certificates found, use HTTP endpoints (%s)", globalEndpoints)
logger.Fatal(uiErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server")
}
if !globalEndpoints.IsHTTPS() && globalIsSSL {
logger.FatalIf(errInvalidArgument, "TLS Certificates found, use HTTPS endpoints (%s)", globalEndpoints)
logger.Fatal(uiErrCertsAndHTTPEndpoints(nil), "Unable to start the server")
}
}
@ -225,7 +231,9 @@ func serverMain(ctx *cli.Context) {
// Set nodes for dsync for distributed setup.
if globalIsDistXL {
globalDsync, err = dsync.New(newDsyncNodes(globalEndpoints))
logger.FatalIf(err, "Unable to initialize distributed locking on %s", globalEndpoints)
if err != nil {
logger.Fatal(err, "Unable to initialize distributed locking on %s", globalEndpoints)
}
}
// Initialize name space lock.
@ -237,11 +245,15 @@ func serverMain(ctx *cli.Context) {
// Configure server.
var handler http.Handler
handler, err = configureServerHandler(globalEndpoints)
logger.FatalIf(err, "Unable to configure one of server's RPC services.")
if err != nil {
logger.Fatal(uiErrUnexpectedError(err), "Unable to configure one of server's RPC services.")
}
// Create new notification system.
globalNotificationSys, err = NewNotificationSys(globalServerConfig, globalEndpoints)
logger.FatalIf(err, "Unable to create new notification system.")
if err != nil {
logger.Fatal(uiErrUnexpectedError(err), "Unable to create new notification system.")
}
// Create new policy system.
globalPolicySys = NewPolicySys()
@ -262,10 +274,8 @@ func serverMain(ctx *cli.Context) {
newObject, err := newObjectLayer(globalEndpoints)
if err != nil {
logger.LogIf(context.Background(), err)
err = globalHTTPServer.Shutdown()
logger.LogIf(context.Background(), err)
os.Exit(1)
globalHTTPServer.Shutdown()
logger.FatalIf(err, "Unable to initialize backend")
}
globalObjLayerMutex.Lock()
@ -279,6 +289,9 @@ func serverMain(ctx *cli.Context) {
// Set uptime time after object layer has initialized.
globalBootTime = UTCNow()
// Re-enable logging
logger.Disable = false
handleSignals()
}

View File

@ -18,7 +18,6 @@ package cmd
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
@ -104,20 +103,20 @@ func parseStorageClass(storageClassEnv string) (sc storageClass, err error) {
// only two elements allowed in the string - "scheme" and "number of parity disks"
if len(s) > 2 {
return storageClass{}, errors.New("Too many sections in " + storageClassEnv)
return storageClass{}, uiErrStorageClassValue(nil).Msg("Too many sections in " + storageClassEnv)
} else if len(s) < 2 {
return storageClass{}, errors.New("Too few sections in " + storageClassEnv)
return storageClass{}, uiErrStorageClassValue(nil).Msg("Too few sections in " + storageClassEnv)
}
// only allowed scheme is "EC"
if s[0] != supportedStorageClassScheme {
return storageClass{}, errors.New("Unsupported scheme " + s[0] + ". Supported scheme is EC")
return storageClass{}, uiErrStorageClassValue(nil).Msg("Unsupported scheme " + s[0] + ". Supported scheme is EC")
}
// Number of parity disks should be integer
parityDisks, err := strconv.Atoi(s[1])
if err != nil {
return storageClass{}, err
return storageClass{}, uiErrStorageClassValue(err)
}
sc = storageClass{

View File

@ -69,8 +69,8 @@ func testParseStorageClass(obj ObjectLayer, instanceType string, t TestErrHandle
t.Errorf("Test %d, Expected %v, got %v", i+1, tt.wantSc, gotSc)
return
}
if tt.expectedError != nil && !reflect.DeepEqual(err, tt.expectedError) {
t.Errorf("Test %d, Expected %v, got %v", i+1, tt.expectedError, err)
if tt.expectedError != nil && err.Error() != tt.expectedError.Error() {
t.Errorf("Test %d, Expected `%v`, got `%v`", i+1, tt.expectedError, err)
}
}
}

View File

@ -16,7 +16,9 @@
package cmd
import "errors"
import (
"errors"
)
// errInvalidArgument means that input argument is invalid.
var errInvalidArgument = errors.New("Invalid arguments specified")

135
cmd/ui-errors-utils.go Normal file
View File

@ -0,0 +1,135 @@
/*
* Minio Cloud Storage, (C) 2018 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 cmd
import (
"fmt"
"io"
"net"
"os"
)
// uiErr is a structure which contains all information
// to print a fatal error message in json or pretty mode
// uiErr implements error so we can use it anywhere
type uiErr struct {
msg string
detail string
action string
help string
}
// Return the error message
func (u uiErr) Error() string {
if u.detail == "" {
return u.msg
}
return u.detail
}
// Replace the current error's message
func (u uiErr) Msg(m string, args ...interface{}) uiErr {
return uiErr{
msg: fmt.Sprintf(m, args...),
detail: u.detail,
action: u.action,
help: u.help,
}
}
type uiErrFn func(err error) uiErr
// Create a UI error generator, this is needed to simplify
// the update of the detailed error message in several places
// in Minio code
func newUIErrFn(msg, action, help string) uiErrFn {
return func(err error) uiErr {
u := uiErr{
msg: msg,
action: action,
help: help,
}
if err != nil {
u.detail = err.Error()
}
return u
}
}
// errorToUIError inspects the passed error and transforms it
// to the appropriate UI error.
func errorToUIErr(err error) uiErr {
// If this is already a uiErr, do nothing
if e, ok := err.(uiErr); ok {
return e
}
// Show a generic message for known golang errors
switch e := err.(type) {
case *net.OpError:
if e.Op == "listen" {
return uiErrPortAlreadyInUse(e).Msg("Port " + e.Addr.String() + " is already in use")
}
case *os.PathError:
if os.IsPermission(e) {
return uiErrNoPermissionsToAccessDirFiles(e).Msg("Unsufficent permissions to access `" + e.Path + "` path")
}
}
switch err {
case io.ErrUnexpectedEOF:
return uiErrUnexpectedDataContent(err)
default:
// Failed to identify what type of error this, return a simple UI error
return uiErr{msg: err.Error()}
}
}
// fmtError() converts a fatal error message to a more understood error
// using some colors
func fmtError(introMsg string, err error, jsonFlag bool) string {
renderedTxt := ""
uiErr := errorToUIErr(err)
// JSON print
if jsonFlag {
// Message text in json should be simple
if uiErr.detail != "" {
return uiErr.msg + ": " + uiErr.detail
}
return uiErr.msg
}
// Pretty print error message
introMsg += ": "
if uiErr.msg != "" {
introMsg += colorBold(uiErr.msg + ".")
} else {
introMsg += colorBold(err.Error() + ".")
}
renderedTxt += colorRed(introMsg) + "\n"
// Add action message
if uiErr.action != "" {
renderedTxt += "> " + colorBgYellow(colorBlack(uiErr.action+".")) + "\n"
}
// Add help
if uiErr.help != "" {
renderedTxt += colorBold("HELP:") + "\n"
renderedTxt += " " + uiErr.help
}
return renderedTxt
}

170
cmd/ui-errors.go Normal file
View File

@ -0,0 +1,170 @@
/*
* Minio Cloud Storage, (C) 2018 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 cmd
var (
uiErrInvalidConfig = newUIErrFn(
"Invalid value found in the configuration file",
"Please ensure a valid value in the configuration file, for more details refer https://docs.minio.io/docs/minio-server-configuration-guide",
"",
)
uiErrInvalidBrowserValue = newUIErrFn(
"Invalid browser value",
"Please check the passed value",
"Browser can only accept `on` and `off` values. To disable web browser access, set this value to `off`",
)
uiErrInvalidCacheDrivesValue = newUIErrFn(
"Invalid cache drive value",
"Please check the passed value",
"MINIO_CACHE_DRIVES: List of mounted drives or directories delimited by `;`.",
)
uiErrInvalidCacheExcludesValue = newUIErrFn(
"Invalid cache excludes value",
"Please check the passed value",
"MINIO_CACHE_EXCLUDE: List of cache exclusion patterns delimited by `;`.",
)
uiErrInvalidCacheExpiryValue = newUIErrFn(
"Invalid cache expiry value",
"Please check the passed value",
"MINIO_CACHE_EXPIRY: Cache expiry duration in days.",
)
uiErrInvalidCredentials = newUIErrFn(
"Passed credentials are not suitable for use",
"Please provide correct credentials",
`Access key length should be between minimum 3 characters in length.
Secret key should not be between 8 and 40 characters.`,
)
uiErrInvalidErasureEndpoints = newUIErrFn(
"Invalid endpoints to use in erasure mode",
"Please provide correct combinations of local/remote paths",
"For more information, please refer to the following link: https://docs.minio.io/docs/minio-erasure-code-quickstart-guide",
)
uiErrInvalidNumberOfErasureEndpoints = newUIErrFn(
"The total number of endpoints is not suitable for erasure mode",
"Please provide an even number of endpoints greater or equal to 4",
"For more information, please refer to the following link: https://docs.minio.io/docs/minio-erasure-code-quickstart-guide",
)
uiErrStorageClassValue = newUIErrFn(
"Invalid storage class value",
"Please check the passed value",
`MINIO_STORAGE_CLASS_STANDARD: Format "EC:<Default_Parity_Standard_Class>" (e.g. "EC:3"). This sets the number of parity disks for Minio server in Standard mode. Object are stored in Standard mode, if storage class is not defined in Put request.
MINIO_STORAGE_CLASS_RRS: Format "EC:<Default_Parity_Reduced_Redundancy_Class>" (e.g. "EC:3"). This sets the number of parity disks for Minio server in reduced redundancy mode. Objects are stored in Reduced Redundancy mode, if Put request specifies RRS storage class.
Refer to the link https://github.com/minio/minio/tree/master/docs/erasure/storage-class for more information.`,
)
uiErrUnexpectedBackendVersion = newUIErrFn(
"Backend version seems to be too recent",
"Please update to the latest Minio version",
"",
)
uiErrInvalidAddressFlag = newUIErrFn(
"--address input is invalid",
"Please check --address parameter",
`--address binds a specific ADDRESS:PORT, ADDRESS can be an IP or hostname (default:':9000')
Examples: --address ':443'
--address '172.16.34.31:9000'`,
)
uiErrInvalidFSEndpoint = newUIErrFn(
"The given endpoint is not suitable to activate standalone FS mode",
"Please check the given FS endpoint",
`FS mode requires only one writable disk path.
Example 1:
$ minio server /data/minio/`,
)
uiErrUnableToWriteInBackend = newUIErrFn(
"Unable to write to the backend",
"Please ensure that Minio binary has write permissions to the backend",
"",
)
uiErrUnableToReadFromBackend = newUIErrFn(
"Unable to read from the backend",
"Please ensure that Minio binary has read permission from the backend",
"",
)
uiErrPortAlreadyInUse = newUIErrFn(
"Port is already in use",
"Please ensure no other program is using the same address/port",
"",
)
uiErrNoPermissionsToAccessDirFiles = newUIErrFn(
"Missing permissions to access to the specified path",
"Please ensure the specified path can be accessed",
"",
)
uiErrSSLUnexpectedError = newUIErrFn(
"Invalid TLS certificate",
"Please check the content of your certificate data",
`Only PEM (x.509) format is accepted as valid public & private certificates.`,
)
uiErrSSLUnexpectedData = newUIErrFn(
"Something is wrong with your TLS certificate",
"Please check your certificate",
"",
)
uiErrSSLNoPassword = newUIErrFn(
"no password was specified",
"Please set the password to this environment variable `"+TLSPrivateKeyPassword+"` so the private key can be decrypted",
"",
)
uiErrNoCertsAndHTTPSEndpoints = newUIErrFn(
"HTTPS is specified in endpoint URLs but no TLS certificate is found on the local machine",
"Please add a certificate or switch to HTTP.",
"Refer to https://docs.minio.io/docs/how-to-secure-access-to-minio-server-with-tls for information about how to load a TLS certificate in th server.",
)
uiErrCertsAndHTTPEndpoints = newUIErrFn(
"HTTP is specified in endpoint URLs but the server in the local machine is configured with a TLS certificate",
"Please remove the certificate in the configuration directory or switch to HTTPS",
"",
)
uiErrSSLWrongPassword = newUIErrFn(
"Unable to decrypt the private key using the provided password",
"Please set the correct password in "+TLSPrivateKeyPassword,
"",
)
uiErrUnexpectedDataContent = newUIErrFn(
"Unexpected data content",
"Please contact us at https://slack.minio.io",
"",
)
uiErrUnexpectedError = newUIErrFn(
"Unexpected error",
"Please contact us at https://slack.minio.io",
"",
)
)