mirror of https://github.com/minio/minio.git
Enhance fatal errors printing of common issues seen by users (#5878)
This commit is contained in:
parent
54cd29b51c
commit
32700fca52
25
cmd/certs.go
25
cmd/certs.go
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")},
|
||||
|
|
|
@ -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 {
|
||||
|
|
10
cmd/fs-v1.go
10
cmd/fs-v1.go
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
})
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
package cmd
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// errInvalidArgument means that input argument is invalid.
|
||||
var errInvalidArgument = errors.New("Invalid arguments specified")
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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",
|
||||
"",
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue