Refactor config and split them in packages (#8351)

This change is related to larger config migration PR
change, this is a first stage change to move our
configs to `cmd/config/` - divided into its subsystems
master
Harshavardhana 5 years ago committed by kannappanr
parent 74008446fe
commit 589e32a4ed
  1. 28
      cmd/certs.go
  2. 116
      cmd/common-main.go
  3. 73
      cmd/config-current.go
  4. 42
      cmd/config-versions.go
  5. 23
      cmd/config/cache/config.go
  6. 6
      cmd/config/cache/config_test.go
  7. 79
      cmd/config/cache/lookup.go
  8. 79
      cmd/config/compress/compress.go
  9. 61
      cmd/config/errors-utls.go
  10. 75
      cmd/config/errors.go
  11. 201
      cmd/config/ldap/config.go
  12. 65
      cmd/config/ldap/config_test.go
  13. 2
      cmd/disk-cache-backend.go
  14. 10
      cmd/disk-cache.go
  15. 23
      cmd/endpoint-ellipses.go
  16. 29
      cmd/endpoint.go
  17. 28
      cmd/environment.go
  18. 3
      cmd/format-fs.go
  19. 3
      cmd/fs-v1.go
  20. 9
      cmd/gateway-common.go
  21. 11
      cmd/gateway-main.go
  22. 12
      cmd/gateway-startup-msg.go
  23. 6
      cmd/gateway/gcs/gateway-gcs.go
  24. 35
      cmd/gateway/hdfs/gateway-hdfs.go
  25. 63
      cmd/globals.go
  26. 180
      cmd/ldap-ops.go
  27. 5
      cmd/logger/console.go
  28. 4
      cmd/logger/logger.go
  29. 3
      cmd/logger/target/console/console.go
  30. 44
      cmd/logger/utils.go
  31. 9
      cmd/net.go
  32. 34
      cmd/server-main.go
  33. 47
      cmd/server-startup-msg.go
  34. 6
      cmd/server-startup-msg_test.go
  35. 10
      cmd/storage-class.go
  36. 4
      cmd/storage-rest-server.go
  37. 17
      cmd/sts-handlers.go
  38. 9
      cmd/update-notifier.go
  39. 6
      cmd/update-notifier_test.go
  40. 9
      cmd/update.go
  41. 85
      pkg/color/color.go
  42. 21
      pkg/env/env.go

@ -24,9 +24,10 @@ import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"io/ioutil" "io/ioutil"
"os"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/certs" "github.com/minio/minio/pkg/certs"
"github.com/minio/minio/pkg/env"
) )
// TLSPrivateKeyPassword is the environment variable which contains the password used // TLSPrivateKeyPassword is the environment variable which contains the password used
@ -49,19 +50,19 @@ func parsePublicCertFile(certFile string) (x509Certs []*x509.Certificate, err er
for len(current) > 0 { for len(current) > 0 {
var pemBlock *pem.Block var pemBlock *pem.Block
if pemBlock, current = pem.Decode(current); pemBlock == nil { if pemBlock, current = pem.Decode(current); pemBlock == nil {
return nil, uiErrSSLUnexpectedData(nil).Msg("Could not read PEM block from file %s", certFile) return nil, config.ErrSSLUnexpectedData(nil).Msg("Could not read PEM block from file %s", certFile)
} }
var x509Cert *x509.Certificate var x509Cert *x509.Certificate
if x509Cert, err = x509.ParseCertificate(pemBlock.Bytes); err != nil { if x509Cert, err = x509.ParseCertificate(pemBlock.Bytes); err != nil {
return nil, uiErrSSLUnexpectedData(err) return nil, config.ErrSSLUnexpectedData(err)
} }
x509Certs = append(x509Certs, x509Cert) x509Certs = append(x509Certs, x509Cert)
} }
if len(x509Certs) == 0 { if len(x509Certs) == 0 {
return nil, uiErrSSLUnexpectedData(nil).Msg("Empty public certificate file %s", certFile) return nil, config.ErrSSLUnexpectedData(nil).Msg("Empty public certificate file %s", certFile)
} }
return x509Certs, nil return x509Certs, nil
@ -105,37 +106,38 @@ func getRootCAs(certsCAsDir string) (*x509.CertPool, error) {
func loadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) { func loadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) {
certPEMBlock, err := ioutil.ReadFile(certFile) certPEMBlock, err := ioutil.ReadFile(certFile)
if err != nil { if err != nil {
return tls.Certificate{}, uiErrSSLUnexpectedError(err) return tls.Certificate{}, config.ErrSSLUnexpectedError(err)
} }
keyPEMBlock, err := ioutil.ReadFile(keyFile) keyPEMBlock, err := ioutil.ReadFile(keyFile)
if err != nil { if err != nil {
return tls.Certificate{}, uiErrSSLUnexpectedError(err) return tls.Certificate{}, config.ErrSSLUnexpectedError(err)
} }
key, rest := pem.Decode(keyPEMBlock) key, rest := pem.Decode(keyPEMBlock)
if len(rest) > 0 { if len(rest) > 0 {
return tls.Certificate{}, uiErrSSLUnexpectedData(nil).Msg("The private key contains additional data") return tls.Certificate{}, config.ErrSSLUnexpectedData(nil).Msg("The private key contains additional data")
} }
if x509.IsEncryptedPEMBlock(key) { if x509.IsEncryptedPEMBlock(key) {
password, ok := os.LookupEnv(TLSPrivateKeyPassword) password, ok := env.Lookup(TLSPrivateKeyPassword)
if !ok { if !ok {
return tls.Certificate{}, uiErrSSLNoPassword(nil) return tls.Certificate{}, config.ErrSSLNoPassword(nil)
} }
decryptedKey, decErr := x509.DecryptPEMBlock(key, []byte(password)) decryptedKey, decErr := x509.DecryptPEMBlock(key, []byte(password))
if decErr != nil { if decErr != nil {
return tls.Certificate{}, uiErrSSLWrongPassword(decErr) return tls.Certificate{}, config.ErrSSLWrongPassword(decErr)
} }
keyPEMBlock = pem.EncodeToMemory(&pem.Block{Type: key.Type, Bytes: decryptedKey}) keyPEMBlock = pem.EncodeToMemory(&pem.Block{Type: key.Type, Bytes: decryptedKey})
} }
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil { if err != nil {
return tls.Certificate{}, uiErrSSLUnexpectedData(nil).Msg(err.Error()) return tls.Certificate{}, config.ErrSSLUnexpectedData(nil).Msg(err.Error())
} }
// Ensure that the private key is not a P-384 or P-521 EC key. // Ensure that the private key is not a P-384 or P-521 EC key.
// The Go TLS stack does not provide constant-time implementations of P-384 and P-521. // The Go TLS stack does not provide constant-time implementations of P-384 and P-521.
if priv, ok := cert.PrivateKey.(crypto.Signer); ok { if priv, ok := cert.PrivateKey.(crypto.Signer); ok {
if pub, ok := priv.Public().(*ecdsa.PublicKey); 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 if name := pub.Params().Name; name == "P-384" || name == "P-521" {
return tls.Certificate{}, uiErrSSLUnexpectedData(nil).Msg("tls: the ECDSA curve '%s' is not supported", name) // unfortunately there is no cleaner way to check
return tls.Certificate{}, config.ErrSSLUnexpectedData(nil).Msg("tls: the ECDSA curve '%s' is not supported", name)
} }
} }
} }

@ -22,7 +22,6 @@ import (
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@ -30,10 +29,12 @@ import (
dns2 "github.com/miekg/dns" dns2 "github.com/miekg/dns"
"github.com/minio/cli" "github.com/minio/cli"
"github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio-go/v6/pkg/set"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/cmd/logger/target/http" "github.com/minio/minio/cmd/logger/target/http"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/dns"
"github.com/minio/minio/pkg/env"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
) )
@ -45,7 +46,7 @@ func verifyObjectLayerFeatures(name string, objAPI ObjectLayer) {
if strings.HasPrefix(name, "gateway") { if strings.HasPrefix(name, "gateway") {
if GlobalGatewaySSE.IsSet() && GlobalKMS == nil { if GlobalGatewaySSE.IsSet() && GlobalKMS == nil {
uiErr := uiErrInvalidGWSSEEnvValue(nil).Msg("MINIO_GATEWAY_SSE set but KMS is not configured") uiErr := config.ErrInvalidGWSSEEnvValue(nil).Msg("MINIO_GATEWAY_SSE set but KMS is not configured")
logger.Fatal(uiErr, "Unable to start gateway with SSE") logger.Fatal(uiErr, "Unable to start gateway with SSE")
} }
} }
@ -75,13 +76,13 @@ func checkUpdate(mode string) {
func loadLoggers() { func loadLoggers() {
loggerUserAgent := getUserAgent(getMinioMode()) loggerUserAgent := getUserAgent(getMinioMode())
auditEndpoint, ok := os.LookupEnv("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT") auditEndpoint, ok := env.Lookup("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT")
if ok { if ok {
// Enable audit HTTP logging through ENV. // Enable audit HTTP logging through ENV.
logger.AddAuditTarget(http.New(auditEndpoint, loggerUserAgent, NewCustomHTTPTransport())) logger.AddAuditTarget(http.New(auditEndpoint, loggerUserAgent, NewCustomHTTPTransport()))
} }
loggerEndpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT") loggerEndpoint, ok := env.Lookup("MINIO_LOGGER_HTTP_ENDPOINT")
if ok { if ok {
// Enable HTTP logging through ENV. // Enable HTTP logging through ENV.
logger.AddTarget(http.New(loggerEndpoint, loggerUserAgent, NewCustomHTTPTransport())) logger.AddTarget(http.New(loggerEndpoint, loggerUserAgent, NewCustomHTTPTransport()))
@ -189,31 +190,20 @@ func handleCommonCmdArgs(ctx *cli.Context) {
globalCLIContext.StrictS3Compat = ctx.IsSet("compat") || ctx.GlobalIsSet("compat") globalCLIContext.StrictS3Compat = ctx.IsSet("compat") || ctx.GlobalIsSet("compat")
} }
// Parses the given compression exclude list `extensions` or `content-types`.
func parseCompressIncludes(includes []string) ([]string, error) {
for _, e := range includes {
if len(e) == 0 {
return nil, uiErrInvalidCompressionIncludesValue(nil).Msg("extension/mime-type (%s) cannot be empty", e)
}
}
return includes, nil
}
func handleCommonEnvVars() { func handleCommonEnvVars() {
compressEnvDelimiter := ","
// Start profiler if env is set. // Start profiler if env is set.
if profiler := os.Getenv("_MINIO_PROFILER"); profiler != "" { if profiler := env.Get("_MINIO_PROFILER", ""); profiler != "" {
var err error var err error
globalProfiler, err = startProfiler(profiler, "") globalProfiler, err = startProfiler(profiler, "")
logger.FatalIf(err, "Unable to setup a profiler") logger.FatalIf(err, "Unable to setup a profiler")
} }
accessKey := os.Getenv("MINIO_ACCESS_KEY") accessKey := env.Get("MINIO_ACCESS_KEY", "")
secretKey := os.Getenv("MINIO_SECRET_KEY") secretKey := env.Get("MINIO_SECRET_KEY", "")
if accessKey != "" && secretKey != "" { if accessKey != "" && secretKey != "" {
cred, err := auth.CreateCredentials(accessKey, secretKey) cred, err := auth.CreateCredentials(accessKey, secretKey)
if err != nil { if err != nil {
logger.Fatal(uiErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment") logger.Fatal(config.ErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment")
} }
cred.Expiration = timeSentinel cred.Expiration = timeSentinel
@ -222,10 +212,10 @@ func handleCommonEnvVars() {
globalActiveCred = cred globalActiveCred = cred
} }
if browser := os.Getenv("MINIO_BROWSER"); browser != "" { if browser := env.Get("MINIO_BROWSER", "on"); browser != "" {
browserFlag, err := ParseBoolFlag(browser) browserFlag, err := ParseBoolFlag(browser)
if err != nil { if err != nil {
logger.Fatal(uiErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Invalid MINIO_BROWSER value in environment variable") logger.Fatal(config.ErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Invalid MINIO_BROWSER value in environment variable")
} }
// browser Envs are set globally, this does not represent // browser Envs are set globally, this does not represent
@ -234,7 +224,7 @@ func handleCommonEnvVars() {
globalIsBrowserEnabled = bool(browserFlag) globalIsBrowserEnabled = bool(browserFlag)
} }
etcdEndpointsEnv, ok := os.LookupEnv("MINIO_ETCD_ENDPOINTS") etcdEndpointsEnv, ok := env.Lookup("MINIO_ETCD_ENDPOINTS")
if ok { if ok {
etcdEndpoints := strings.Split(etcdEndpointsEnv, ",") etcdEndpoints := strings.Split(etcdEndpointsEnv, ",")
@ -252,8 +242,8 @@ func handleCommonEnvVars() {
if etcdSecure { if etcdSecure {
// This is only to support client side certificate authentication // This is only to support client side certificate authentication
// https://coreos.com/etcd/docs/latest/op-guide/security.html // https://coreos.com/etcd/docs/latest/op-guide/security.html
etcdClientCertFile, ok1 := os.LookupEnv("MINIO_ETCD_CLIENT_CERT") etcdClientCertFile, ok1 := env.Lookup("MINIO_ETCD_CLIENT_CERT")
etcdClientCertKey, ok2 := os.LookupEnv("MINIO_ETCD_CLIENT_CERT_KEY") etcdClientCertKey, ok2 := env.Lookup("MINIO_ETCD_CLIENT_CERT_KEY")
var getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error) var getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
if ok1 && ok2 { if ok1 && ok2 {
getClientCertificate = func(unused *tls.CertificateRequestInfo) (*tls.Certificate, error) { getClientCertificate = func(unused *tls.CertificateRequestInfo) (*tls.Certificate, error) {
@ -281,18 +271,18 @@ func handleCommonEnvVars() {
logger.FatalIf(err, "Unable to initialize etcd with %s", etcdEndpoints) logger.FatalIf(err, "Unable to initialize etcd with %s", etcdEndpoints)
} }
v, ok := os.LookupEnv("MINIO_DOMAIN") v, ok := env.Lookup("MINIO_DOMAIN")
if ok { if ok {
for _, domainName := range strings.Split(v, ",") { for _, domainName := range strings.Split(v, ",") {
if _, ok = dns2.IsDomainName(domainName); !ok { if _, ok = dns2.IsDomainName(domainName); !ok {
logger.Fatal(uiErrInvalidDomainValue(nil).Msg("Unknown value `%s`", domainName), logger.Fatal(config.ErrInvalidDomainValue(nil).Msg("Unknown value `%s`", domainName),
"Invalid MINIO_DOMAIN value in environment variable") "Invalid MINIO_DOMAIN value in environment variable")
} }
globalDomainNames = append(globalDomainNames, domainName) globalDomainNames = append(globalDomainNames, domainName)
} }
} }
minioEndpointsEnv, ok := os.LookupEnv("MINIO_PUBLIC_IPS") minioEndpointsEnv, ok := env.Lookup("MINIO_PUBLIC_IPS")
if ok { if ok {
minioEndpoints := strings.Split(minioEndpointsEnv, ",") minioEndpoints := strings.Split(minioEndpointsEnv, ",")
var domainIPs = set.NewStringSet() var domainIPs = set.NewStringSet()
@ -323,53 +313,10 @@ func handleCommonEnvVars() {
logger.FatalIf(err, "Unable to initialize DNS config for %s.", globalDomainNames) logger.FatalIf(err, "Unable to initialize DNS config for %s.", globalDomainNames)
} }
if drives := os.Getenv("MINIO_CACHE_DRIVES"); drives != "" {
driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter))
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))
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)
if err != nil {
logger.Fatal(uiErrInvalidCacheExpiryValue(err), "Unable to parse MINIO_CACHE_EXPIRY value (`%s`)", expiryStr)
}
globalCacheExpiry = expiry
}
if maxUseStr := os.Getenv("MINIO_CACHE_MAXUSE"); maxUseStr != "" {
maxUse, err := strconv.Atoi(maxUseStr)
if err != nil {
logger.Fatal(uiErrInvalidCacheMaxUse(err), "Unable to parse MINIO_CACHE_MAXUSE value (`%s`)", maxUseStr)
}
// maxUse should be a valid percentage.
if maxUse > 0 && maxUse <= 100 {
globalCacheMaxUse = maxUse
}
}
var err error
if cacheEncKey := os.Getenv("MINIO_CACHE_ENCRYPTION_MASTER_KEY"); cacheEncKey != "" {
globalCacheKMSKeyID, globalCacheKMS, err = parseKMSMasterKey(cacheEncKey)
if err != nil {
logger.Fatal(uiErrInvalidCacheEncryptionKey(err), "Invalid cache encryption master key")
}
}
// In place update is true by default if the MINIO_UPDATE is not set // In place update is true by default if the MINIO_UPDATE is not set
// or is not set to 'off', if MINIO_UPDATE is set to 'off' then // or is not set to 'off', if MINIO_UPDATE is set to 'off' then
// in-place update is off. // in-place update is off.
globalInplaceUpdateDisabled = strings.EqualFold(os.Getenv("MINIO_UPDATE"), "off") globalInplaceUpdateDisabled = strings.EqualFold(env.Get("MINIO_UPDATE", "off"), "off")
// Validate and store the storage class env variables only for XL/Dist XL setups // Validate and store the storage class env variables only for XL/Dist XL setups
if globalIsXL { if globalIsXL {
@ -402,10 +349,10 @@ func handleCommonEnvVars() {
} }
// Get WORM environment variable. // Get WORM environment variable.
if worm := os.Getenv("MINIO_WORM"); worm != "" { if worm := env.Get("MINIO_WORM", "off"); worm != "" {
wormFlag, err := ParseBoolFlag(worm) wormFlag, err := ParseBoolFlag(worm)
if err != nil { if err != nil {
logger.Fatal(uiErrInvalidWormValue(nil).Msg("Unknown value `%s`", worm), "Invalid MINIO_WORM value in environment variable") logger.Fatal(config.ErrInvalidWormValue(nil).Msg("Unknown value `%s`", worm), "Invalid MINIO_WORM value in environment variable")
} }
// worm Envs are set globally, this does not represent // worm Envs are set globally, this does not represent
@ -414,29 +361,6 @@ func handleCommonEnvVars() {
globalWORMEnabled = bool(wormFlag) globalWORMEnabled = bool(wormFlag)
} }
if compress := os.Getenv("MINIO_COMPRESS"); compress != "" {
globalIsCompressionEnabled = strings.EqualFold(compress, "true")
}
compressExtensions := os.Getenv("MINIO_COMPRESS_EXTENSIONS")
compressMimeTypes := os.Getenv("MINIO_COMPRESS_MIMETYPES")
if compressExtensions != "" || compressMimeTypes != "" {
globalIsEnvCompression = true
if compressExtensions != "" {
extensions, err := parseCompressIncludes(strings.Split(compressExtensions, compressEnvDelimiter))
if err != nil {
logger.Fatal(err, "Invalid MINIO_COMPRESS_EXTENSIONS value (`%s`)", extensions)
}
globalCompressExtensions = extensions
}
if compressMimeTypes != "" {
contenttypes, err := parseCompressIncludes(strings.Split(compressMimeTypes, compressEnvDelimiter))
if err != nil {
logger.Fatal(err, "Invalid MINIO_COMPRESS_MIMETYPES value (`%s`)", contenttypes)
}
globalCompressMimeTypes = contenttypes
}
}
} }
func logStartupMessage(msg string, data ...interface{}) { func logStartupMessage(msg string, data ...interface{}) {

@ -20,14 +20,18 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"reflect" "reflect"
"sync" "sync"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/config/cache"
"github.com/minio/minio/cmd/config/compress"
xldap "github.com/minio/minio/cmd/config/ldap"
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/env"
"github.com/minio/minio/pkg/event" "github.com/minio/minio/pkg/event"
"github.com/minio/minio/pkg/event/target" "github.com/minio/minio/pkg/event/target"
"github.com/minio/minio/pkg/iam/openid" "github.com/minio/minio/pkg/iam/openid"
@ -134,18 +138,10 @@ func (s *serverConfig) GetWorm() bool {
return bool(s.Worm) return bool(s.Worm)
} }
// SetCacheConfig sets the current cache config
func (s *serverConfig) SetCacheConfig(drives, exclude []string, expiry int, maxuse int) {
s.Cache.Drives = drives
s.Cache.Exclude = exclude
s.Cache.Expiry = expiry
s.Cache.MaxUse = maxuse
}
// GetCacheConfig gets the current cache config // GetCacheConfig gets the current cache config
func (s *serverConfig) GetCacheConfig() CacheConfig { func (s *serverConfig) GetCacheConfig() cache.Config {
if globalIsDiskCacheEnabled { if globalIsDiskCacheEnabled {
return CacheConfig{ return cache.Config{
Drives: globalCacheDrives, Drives: globalCacheDrives,
Exclude: globalCacheExcludes, Exclude: globalCacheExcludes,
Expiry: globalCacheExpiry, Expiry: globalCacheExpiry,
@ -153,7 +149,7 @@ func (s *serverConfig) GetCacheConfig() CacheConfig {
} }
} }
if s == nil { if s == nil {
return CacheConfig{} return cache.Config{}
} }
return s.Cache return s.Cache
} }
@ -246,7 +242,7 @@ func (s *serverConfig) SetCompressionConfig(extensions []string, mimeTypes []str
} }
// GetCompressionConfig gets the current compression config // GetCompressionConfig gets the current compression config
func (s *serverConfig) GetCompressionConfig() compressionConfig { func (s *serverConfig) GetCompressionConfig() compress.Config {
return s.Compression return s.Compression
} }
@ -268,19 +264,39 @@ func (s *serverConfig) loadFromEnvs() {
s.SetStorageClass(globalStandardStorageClass, globalRRStorageClass) s.SetStorageClass(globalStandardStorageClass, globalRRStorageClass)
} }
if globalIsDiskCacheEnabled { var err error
s.SetCacheConfig(globalCacheDrives, globalCacheExcludes, globalCacheExpiry, globalCacheMaxUse) s.Cache, err = cache.LookupConfig(s.Cache)
if err != nil {
logger.FatalIf(err, "Unable to setup cache")
}
if len(s.Cache.Drives) > 0 {
globalIsDiskCacheEnabled = true
globalCacheDrives = s.Cache.Drives
globalCacheExcludes = s.Cache.Exclude
globalCacheExpiry = s.Cache.Expiry
globalCacheMaxUse = s.Cache.MaxUse
var err error
if cacheEncKey := env.Get(cache.EnvCacheEncryptionMasterKey, ""); cacheEncKey != "" {
globalCacheKMSKeyID, globalCacheKMS, err = parseKMSMasterKey(cacheEncKey)
if err != nil {
logger.FatalIf(config.ErrInvalidCacheEncryptionKey(err),
"Unable to setup encryption cache")
}
}
} }
if err := Environment.LookupKMSConfig(s.KMS); err != nil { if err = LookupKMSConfig(s.KMS); err != nil {
logger.FatalIf(err, "Unable to setup the KMS") logger.FatalIf(err, "Unable to setup KMS")
} }
if globalIsEnvCompression { s.Compression, err = compress.LookupConfig(s.Compression)
s.SetCompressionConfig(globalCompressExtensions, globalCompressMimeTypes) if err != nil {
logger.FatalIf(err, "Unable to setup Compression")
} }
if jwksURL, ok := os.LookupEnv("MINIO_IAM_JWKS_URL"); ok { if jwksURL, ok := env.Lookup("MINIO_IAM_JWKS_URL"); ok {
u, err := xnet.ParseURL(jwksURL) u, err := xnet.ParseURL(jwksURL)
if err != nil { if err != nil {
logger.FatalIf(err, "Unable to parse MINIO_IAM_JWKS_URL %s", jwksURL) logger.FatalIf(err, "Unable to parse MINIO_IAM_JWKS_URL %s", jwksURL)
@ -288,14 +304,14 @@ func (s *serverConfig) loadFromEnvs() {
s.OpenID.JWKS.URL = u s.OpenID.JWKS.URL = u
} }
if opaURL, ok := os.LookupEnv("MINIO_IAM_OPA_URL"); ok { if opaURL, ok := env.Lookup("MINIO_IAM_OPA_URL"); ok {
u, err := xnet.ParseURL(opaURL) u, err := xnet.ParseURL(opaURL)
if err != nil { if err != nil {
logger.FatalIf(err, "Unable to parse MINIO_IAM_OPA_URL %s", opaURL) logger.FatalIf(err, "Unable to parse MINIO_IAM_OPA_URL %s", opaURL)
} }
opaArgs := iampolicy.OpaArgs{ opaArgs := iampolicy.OpaArgs{
URL: u, URL: u,
AuthToken: os.Getenv("MINIO_IAM_OPA_AUTHTOKEN"), AuthToken: env.Get("MINIO_IAM_OPA_AUTHTOKEN", ""),
Transport: NewCustomHTTPTransport(), Transport: NewCustomHTTPTransport(),
CloseRespFn: xhttp.DrainBody, CloseRespFn: xhttp.DrainBody,
} }
@ -304,8 +320,7 @@ func (s *serverConfig) loadFromEnvs() {
s.Policy.OPA.AuthToken = opaArgs.AuthToken s.Policy.OPA.AuthToken = opaArgs.AuthToken
} }
var err error s.LDAPServerConfig, err = xldap.Lookup(s.LDAPServerConfig, globalRootCAs)
s.LDAPServerConfig, err = newLDAPConfigFromEnv(globalRootCAs)
if err != nil { if err != nil {
logger.FatalIf(err, "Unable to parse LDAP configuration from env") logger.FatalIf(err, "Unable to parse LDAP configuration from env")
} }
@ -484,7 +499,7 @@ func newServerConfig() *serverConfig {
Standard: storageClass{}, Standard: storageClass{},
RRS: storageClass{}, RRS: storageClass{},
}, },
Cache: CacheConfig{ Cache: cache.Config{
Drives: []string{}, Drives: []string{},
Exclude: []string{}, Exclude: []string{},
Expiry: globalCacheExpiry, Expiry: globalCacheExpiry,
@ -492,7 +507,7 @@ func newServerConfig() *serverConfig {
}, },
KMS: crypto.KMSConfig{}, KMS: crypto.KMSConfig{},
Notify: notifier{}, Notify: notifier{},
Compression: compressionConfig{ Compression: compress.Config{
Enabled: false, Enabled: false,
Extensions: globalCompressExtensions, Extensions: globalCompressExtensions,
MimeTypes: globalCompressMimeTypes, MimeTypes: globalCompressMimeTypes,
@ -555,7 +570,7 @@ func (s *serverConfig) loadToCachedConfigs() {
globalCacheExpiry = cacheConf.Expiry globalCacheExpiry = cacheConf.Expiry
globalCacheMaxUse = cacheConf.MaxUse globalCacheMaxUse = cacheConf.MaxUse
} }
if err := Environment.LookupKMSConfig(s.KMS); err != nil { if err := LookupKMSConfig(s.KMS); err != nil {
logger.FatalIf(err, "Unable to setup the KMS %s", s.KMS.Vault.Endpoint) logger.FatalIf(err, "Unable to setup the KMS %s", s.KMS.Vault.Endpoint)
} }
@ -571,7 +586,7 @@ func (s *serverConfig) loadToCachedConfigs() {
"Unable to populate public key from JWKS URL %s", s.OpenID.JWKS.URL) "Unable to populate public key from JWKS URL %s", s.OpenID.JWKS.URL)
} }
globalIAMValidators = getOpenIDValidators(s) globalOpenIDValidators = getOpenIDValidators(s)
if s.Policy.OPA.URL != nil && s.Policy.OPA.URL.String() != "" { if s.Policy.OPA.URL != nil && s.Policy.OPA.URL.String() != "" {
opaArgs := iampolicy.OpaArgs{ opaArgs := iampolicy.OpaArgs{
@ -621,7 +636,7 @@ func getValidConfig(objAPI ObjectLayer) (*serverConfig, error) {
func loadConfig(objAPI ObjectLayer) error { func loadConfig(objAPI ObjectLayer) error {
srvCfg, err := getValidConfig(objAPI) srvCfg, err := getValidConfig(objAPI)
if err != nil { if err != nil {
return uiErrInvalidConfig(nil).Msg(err.Error()) return config.ErrInvalidConfig(nil).Msg(err.Error())
} }
// Override any values from ENVs. // Override any values from ENVs.

@ -19,6 +19,9 @@ package cmd
import ( import (
"sync" "sync"
"github.com/minio/minio/cmd/config/cache"
"github.com/minio/minio/cmd/config/compress"
xldap "github.com/minio/minio/cmd/config/ldap"
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/event/target" "github.com/minio/minio/pkg/event/target"
@ -592,7 +595,7 @@ type serverConfigV23 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// Notification queue configuration. // Notification queue configuration.
Notify notifierV3 `json:"notify"` Notify notifierV3 `json:"notify"`
@ -616,7 +619,7 @@ type serverConfigV24 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// Notification queue configuration. // Notification queue configuration.
Notify notifierV3 `json:"notify"` Notify notifierV3 `json:"notify"`
@ -643,14 +646,14 @@ type serverConfigV25 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// Notification queue configuration. // Notification queue configuration.
Notify notifierV3 `json:"notify"` Notify notifierV3 `json:"notify"`
} }
// serverConfigV26 is just like version '25', stores additionally // serverConfigV26 is just like version '25', stores additionally
// cache max use value in 'CacheConfig'. // cache max use value in 'cache.Config'.
type serverConfigV26 struct { type serverConfigV26 struct {
quick.Config `json:"-"` // ignore interfaces quick.Config `json:"-"` // ignore interfaces
@ -667,7 +670,7 @@ type serverConfigV26 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// Notification queue configuration. // Notification queue configuration.
Notify notifierV3 `json:"notify"` Notify notifierV3 `json:"notify"`
@ -708,7 +711,7 @@ type serverConfigV27 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// Notification queue configuration. // Notification queue configuration.
Notify notifierV3 `json:"notify"` Notify notifierV3 `json:"notify"`
@ -736,7 +739,7 @@ type serverConfigV28 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// KMS configuration // KMS configuration
KMS crypto.KMSConfig `json:"kms"` KMS crypto.KMSConfig `json:"kms"`
@ -751,13 +754,6 @@ type serverConfigV28 struct {
// serverConfigV29 is just like version '28'. // serverConfigV29 is just like version '28'.
type serverConfigV29 serverConfigV28 type serverConfigV29 serverConfigV28
// compressionConfig represents the compression settings.
type compressionConfig struct {
Enabled bool `json:"enabled"`
Extensions []string `json:"extensions"`
MimeTypes []string `json:"mime-types"`
}
// serverConfigV30 is just like version '29', stores additionally // serverConfigV30 is just like version '29', stores additionally
// extensions and mimetypes fields for compression. // extensions and mimetypes fields for compression.
type serverConfigV30 struct { type serverConfigV30 struct {
@ -772,7 +768,7 @@ type serverConfigV30 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// KMS configuration // KMS configuration
KMS crypto.KMSConfig `json:"kms"` KMS crypto.KMSConfig `json:"kms"`
@ -784,7 +780,7 @@ type serverConfigV30 struct {
Logger loggerConfig `json:"logger"` Logger loggerConfig `json:"logger"`
// Compression configuration // Compression configuration
Compression compressionConfig `json:"compress"` Compression compress.Config `json:"compress"`
} }
// serverConfigV31 is just like version '30', with OPA and OpenID configuration. // serverConfigV31 is just like version '30', with OPA and OpenID configuration.
@ -800,7 +796,7 @@ type serverConfigV31 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// KMS configuration // KMS configuration
KMS crypto.KMSConfig `json:"kms"` KMS crypto.KMSConfig `json:"kms"`
@ -812,7 +808,7 @@ type serverConfigV31 struct {
Logger loggerConfig `json:"logger"` Logger loggerConfig `json:"logger"`
// Compression configuration // Compression configuration
Compression compressionConfig `json:"compress"` Compression compress.Config `json:"compress"`
// OpenID configuration // OpenID configuration
OpenID struct { OpenID struct {
@ -855,7 +851,7 @@ type serverConfigV32 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// KMS configuration // KMS configuration
KMS crypto.KMSConfig `json:"kms"` KMS crypto.KMSConfig `json:"kms"`
@ -867,7 +863,7 @@ type serverConfigV32 struct {
Logger loggerConfig `json:"logger"` Logger loggerConfig `json:"logger"`
// Compression configuration // Compression configuration
Compression compressionConfig `json:"compress"` Compression compress.Config `json:"compress"`
// OpenID configuration // OpenID configuration
OpenID struct { OpenID struct {
@ -899,7 +895,7 @@ type serverConfigV33 struct {
StorageClass storageClassConfig `json:"storageclass"` StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration // Cache configuration
Cache CacheConfig `json:"cache"` Cache cache.Config `json:"cache"`
// KMS configuration // KMS configuration
KMS crypto.KMSConfig `json:"kms"` KMS crypto.KMSConfig `json:"kms"`
@ -911,7 +907,7 @@ type serverConfigV33 struct {
Logger loggerConfig `json:"logger"` Logger loggerConfig `json:"logger"`
// Compression configuration // Compression configuration
Compression compressionConfig `json:"compress"` Compression compress.Config `json:"compress"`
// OpenID configuration // OpenID configuration
OpenID struct { OpenID struct {
@ -927,5 +923,5 @@ type serverConfigV33 struct {
// Add new external policy enforcements here. // Add new external policy enforcements here.
} `json:"policy"` } `json:"policy"`
LDAPServerConfig ldapServerConfig `json:"ldapserverconfig"` LDAPServerConfig xldap.Config `json:"ldapserverconfig"`
} }

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cmd package cache
import ( import (
"encoding/json" "encoding/json"
@ -22,11 +22,12 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/ellipses" "github.com/minio/minio/pkg/ellipses"
) )
// CacheConfig represents cache config settings // Config represents cache config settings
type CacheConfig struct { type Config struct {
Drives []string `json:"drives"` Drives []string `json:"drives"`
Expiry int `json:"expiry"` Expiry int `json:"expiry"`
MaxUse int `json:"maxuse"` MaxUse int `json:"maxuse"`
@ -35,8 +36,8 @@ type CacheConfig struct {
// UnmarshalJSON - implements JSON unmarshal interface for unmarshalling // UnmarshalJSON - implements JSON unmarshal interface for unmarshalling
// json entries for CacheConfig. // json entries for CacheConfig.
func (cfg *CacheConfig) UnmarshalJSON(data []byte) (err error) { func (cfg *Config) UnmarshalJSON(data []byte) (err error) {
type Alias CacheConfig type Alias Config
var _cfg = &struct { var _cfg = &struct {
*Alias *Alias
}{ }{
@ -83,7 +84,7 @@ func parseCacheDrives(drives []string) ([]string, error) {
for _, d := range endpoints { for _, d := range endpoints {
if !filepath.IsAbs(d) { if !filepath.IsAbs(d) {
return nil, uiErrInvalidCacheDrivesValue(nil).Msg("cache dir should be absolute path: %s", d) return nil, config.ErrInvalidCacheDrivesValue(nil).Msg("cache dir should be absolute path: %s", d)
} }
} }
return endpoints, nil return endpoints, nil
@ -93,7 +94,7 @@ func parseCacheDrives(drives []string) ([]string, error) {
func parseCacheDrivePaths(arg string) (ep []string, err error) { func parseCacheDrivePaths(arg string) (ep []string, err error) {
patterns, perr := ellipses.FindEllipsesPatterns(arg) patterns, perr := ellipses.FindEllipsesPatterns(arg)
if perr != nil { if perr != nil {
return []string{}, uiErrInvalidCacheDrivesValue(nil).Msg(perr.Error()) return []string{}, config.ErrInvalidCacheDrivesValue(nil).Msg(perr.Error())
} }
for _, lbls := range patterns.Expand() { for _, lbls := range patterns.Expand() {
@ -107,10 +108,10 @@ func parseCacheDrivePaths(arg string) (ep []string, err error) {
func parseCacheExcludes(excludes []string) ([]string, error) { func parseCacheExcludes(excludes []string) ([]string, error) {
for _, e := range excludes { for _, e := range excludes {
if len(e) == 0 { if len(e) == 0 {
return nil, uiErrInvalidCacheExcludesValue(nil).Msg("cache exclude path (%s) cannot be empty", e) return nil, config.ErrInvalidCacheExcludesValue(nil).Msg("cache exclude path (%s) cannot be empty", e)
} }
if hasPrefix(e, SlashSeparator) { if strings.HasPrefix(e, "/") {
return nil, uiErrInvalidCacheExcludesValue(nil).Msg("cache exclude pattern (%s) cannot start with / as prefix", e) return nil, config.ErrInvalidCacheExcludesValue(nil).Msg("cache exclude pattern (%s) cannot start with / as prefix", e)
} }
} }
return excludes, nil return excludes, nil

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cmd package cache
import ( import (
"reflect" "reflect"
@ -35,7 +35,7 @@ func TestParseCacheDrives(t *testing.T) {
{"bucket1/*;*.png;images/trip/barcelona/*", []string{}, false}, {"bucket1/*;*.png;images/trip/barcelona/*", []string{}, false},
{"bucket1", []string{}, false}, {"bucket1", []string{}, false},
} }
if runtime.GOOS == globalWindowsOSName { if runtime.GOOS == "windows" {
testCases = append(testCases, struct { testCases = append(testCases, struct {
driveStr string driveStr string
expectedPatterns []string expectedPatterns []string

@ -0,0 +1,79 @@
/*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cache
import (
"strconv"
"strings"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/env"
)
// Cache ENVs
const (
EnvCacheDrives = "MINIO_CACHE_DRIVES"
EnvCacheExclude = "MINIO_CACHE_EXCLUDE"
EnvCacheExpiry = "MINIO_CACHE_EXPIRY"
EnvCacheMaxUse = "MINIO_CACHE_MAXUSE"
EnvCacheEncryptionMasterKey = "MINIO_CACHE_ENCRYPTION_MASTER_KEY"
)
const (
cacheEnvDelimiter = ";"
)
// LookupConfig - extracts cache configuration provided by environment
// variables and merge them with provided CacheConfiguration.
func LookupConfig(cfg Config) (Config, error) {
if drives := env.Get(EnvCacheDrives, strings.Join(cfg.Drives, ",")); drives != "" {
driveList, err := parseCacheDrives(strings.Split(drives, cacheEnvDelimiter))
if err != nil {
return cfg, err
}
cfg.Drives = driveList
}
if excludes := env.Get(EnvCacheExclude, strings.Join(cfg.Exclude, ",")); excludes != "" {
excludeList, err := parseCacheExcludes(strings.Split(excludes, cacheEnvDelimiter))
if err != nil {
return cfg, err
}
cfg.Exclude = excludeList
}
if expiryStr := env.Get(EnvCacheExpiry, strconv.Itoa(cfg.Expiry)); expiryStr != "" {
expiry, err := strconv.Atoi(expiryStr)
if err != nil {
return cfg, config.ErrInvalidCacheExpiryValue(err)
}
cfg.Expiry = expiry
}
if maxUseStr := env.Get(EnvCacheMaxUse, strconv.Itoa(cfg.MaxUse)); maxUseStr != "" {
maxUse, err := strconv.Atoi(maxUseStr)
if err != nil {
return cfg, config.ErrInvalidCacheMaxUse(err)
}
// maxUse should be a valid percentage.
if maxUse > 0 && maxUse <= 100 {
cfg.MaxUse = maxUse
}
}
return cfg, nil
}

@ -0,0 +1,79 @@
/*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package compress
import (
"fmt"
"strconv"
"strings"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/env"
)
// Config represents the compression settings.
type Config struct {
Enabled bool `json:"enabled"`
Extensions []string `json:"extensions"`
MimeTypes []string `json:"mime-types"`
}
// Compression environment variables
const (
EnvMinioCompress = "MINIO_COMPRESS"
EnvMinioCompressExtensions = "MINIO_COMPRESS_EXTENSIONS"
EnvMinioCompressMimeTypes = "MINIO_COMPRESS_MIMETYPES"
)
// Parses the given compression exclude list `extensions` or `content-types`.
func parseCompressIncludes(includes []string) ([]string, error) {
for _, e := range includes {
if len(e) == 0 {
return nil, config.ErrInvalidCompressionIncludesValue(nil).Msg("extension/mime-type (%s) cannot be empty", e)
}
}
return includes, nil
}
// LookupConfig - lookup compression config.
func LookupConfig(cfg Config) (Config, error) {
const compressEnvDelimiter = ","
if compress := env.Get(EnvMinioCompress, strconv.FormatBool(cfg.Enabled)); compress != "" {
cfg.Enabled = strings.EqualFold(compress, "true")
}
compressExtensions := env.Get(EnvMinioCompressExtensions, strings.Join(cfg.Extensions, ","))
compressMimeTypes := env.Get(EnvMinioCompressMimeTypes, strings.Join(cfg.MimeTypes, ","))
if compressExtensions != "" || compressMimeTypes != "" {
if compressExtensions != "" {
extensions, err := parseCompressIncludes(strings.Split(compressExtensions, compressEnvDelimiter))
if err != nil {
return cfg, fmt.Errorf("%s: Invalid MINIO_COMPRESS_EXTENSIONS value (`%s`)", err, extensions)
}
cfg.Extensions = extensions
}
if compressMimeTypes != "" {
contenttypes, err := parseCompressIncludes(strings.Split(compressMimeTypes, compressEnvDelimiter))
if err != nil {
return cfg, fmt.Errorf("%s: Invalid MINIO_COMPRESS_MIMETYPES value (`%s`)", err, contenttypes)
}
cfg.MimeTypes = contenttypes
}
}
return cfg, nil
}

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cmd package config
import ( import (
"errors" "errors"
@ -22,12 +22,14 @@ import (
"io" "io"
"os" "os"
"syscall" "syscall"
"github.com/minio/minio/pkg/color"
) )
// uiErr is a structure which contains all information // Err is a structure which contains all information
// to print a fatal error message in json or pretty mode // to print a fatal error message in json or pretty mode
// uiErr implements error so we can use it anywhere // Err implements error so we can use it anywhere
type uiErr struct { type Err struct {
msg string msg string
detail string detail string
action string action string
@ -35,16 +37,16 @@ type uiErr struct {
} }
// Return the error message // Return the error message
func (u uiErr) Error() string { func (u Err) Error() string {
if u.detail == "" { if u.detail == "" {
return u.msg return u.msg
} }
return u.detail return u.detail
} }
// Replace the current error's message // Msg - Replace the current error's message
func (u uiErr) Msg(m string, args ...interface{}) uiErr { func (u Err) Msg(m string, args ...interface{}) Err {
return uiErr{ return Err{
msg: fmt.Sprintf(m, args...), msg: fmt.Sprintf(m, args...),
detail: u.detail, detail: u.detail,
action: u.action, action: u.action,
@ -52,14 +54,15 @@ func (u uiErr) Msg(m string, args ...interface{}) uiErr {
} }
} }
type uiErrFn func(err error) uiErr // ErrFn function wrapper
type ErrFn func(err error) Err
// Create a UI error generator, this is needed to simplify // Create a UI error generator, this is needed to simplify
// the update of the detailed error message in several places // the update of the detailed error message in several places
// in MinIO code // in MinIO code
func newUIErrFn(msg, action, hint string) uiErrFn { func newErrFn(msg, action, hint string) ErrFn {
return func(err error) uiErr { return func(err error) Err {
u := uiErr{ u := Err{
msg: msg, msg: msg,
action: action, action: action,
hint: hint, hint: hint,
@ -71,35 +74,35 @@ func newUIErrFn(msg, action, hint string) uiErrFn {
} }
} }
// errorToUIError inspects the passed error and transforms it // ErrorToErr inspects the passed error and transforms it
// to the appropriate UI error. // to the appropriate UI error.
func errorToUIErr(err error) uiErr { func ErrorToErr(err error) Err {
// If this is already a uiErr, do nothing // If this is already a Err, do nothing
if e, ok := err.(uiErr); ok { if e, ok := err.(Err); ok {
return e return e
} }
// Show a generic message for known golang errors // Show a generic message for known golang errors
if errors.Is(err, syscall.EADDRINUSE) { if errors.Is(err, syscall.EADDRINUSE) {
return uiErrPortAlreadyInUse(err).Msg("Specified port is already in use") return ErrPortAlreadyInUse(err).Msg("Specified port is already in use")
} else if errors.Is(err, syscall.EACCES) { } else if errors.Is(err, syscall.EACCES) {
return uiErrPortAccess(err).Msg("Insufficient permissions to use specified port") return ErrPortAccess(err).Msg("Insufficient permissions to use specified port")
} else if os.IsPermission(err) { } else if os.IsPermission(err) {
return uiErrNoPermissionsToAccessDirFiles(err).Msg("Insufficient permissions to access path") return ErrNoPermissionsToAccessDirFiles(err).Msg("Insufficient permissions to access path")
} else if errors.Is(err, io.ErrUnexpectedEOF) { } else if errors.Is(err, io.ErrUnexpectedEOF) {
return uiErrUnexpectedDataContent(err) return ErrUnexpectedDataContent(err)
} else { } else {
// Failed to identify what type of error this, return a simple UI error // Failed to identify what type of error this, return a simple UI error
return uiErr{msg: err.Error()} return Err{msg: err.Error()}
} }
} }
// fmtError() converts a fatal error message to a more clear error // FmtError converts a fatal error message to a more clear error
// using some colors // using some colors
func fmtError(introMsg string, err error, jsonFlag bool) string { func FmtError(introMsg string, err error, jsonFlag bool) string {
renderedTxt := "" renderedTxt := ""
uiErr := errorToUIErr(err) uiErr := ErrorToErr(err)
// JSON print // JSON print
if jsonFlag { if jsonFlag {
// Message text in json should be simple // Message text in json should be simple
@ -111,18 +114,18 @@ func fmtError(introMsg string, err error, jsonFlag bool) string {
// Pretty print error message // Pretty print error message
introMsg += ": " introMsg += ": "
if uiErr.msg != "" { if uiErr.msg != "" {
introMsg += colorBold(uiErr.msg) introMsg += color.Bold(uiErr.msg)
} else { } else {
introMsg += colorBold(err.Error()) introMsg += color.Bold(err.Error())
} }
renderedTxt += colorRed(introMsg) + "\n" renderedTxt += color.Red(introMsg) + "\n"
// Add action message // Add action message
if uiErr.action != "" { if uiErr.action != "" {
renderedTxt += "> " + colorBgYellow(colorBlack(uiErr.action)) + "\n" renderedTxt += "> " + color.BgYellow(color.Black(uiErr.action)) + "\n"
} }
// Add hint // Add hint
if uiErr.hint != "" { if uiErr.hint != "" {
renderedTxt += colorBold("HINT:") + "\n" renderedTxt += color.Bold("HINT:") + "\n"
renderedTxt += " " + uiErr.hint renderedTxt += " " + uiErr.hint
} }
return renderedTxt return renderedTxt

@ -14,101 +14,102 @@
* limitations under the License. * limitations under the License.
*/ */
package cmd package config
// UI errors
var ( var (
uiErrInvalidConfig = newUIErrFn( ErrInvalidConfig = newErrFn(
"Invalid value found in the configuration file", "Invalid value found in the configuration file",
"Please ensure a valid value in the configuration file", "Please ensure a valid value in the configuration file",
"For more details, refer to https://docs.min.io/docs/minio-server-configuration-guide", "For more details, refer to https://docs.min.io/docs/minio-server-configuration-guide",
) )
uiErrInvalidBrowserValue = newUIErrFn( ErrInvalidBrowserValue = newErrFn(
"Invalid browser value", "Invalid browser value",
"Please check the passed value", "Please check the passed value",
"Browser can only accept `on` and `off` values. To disable web browser access, set this value to `off`", "Browser can only accept `on` and `off` values. To disable web browser access, set this value to `off`",
) )
uiErrInvalidDomainValue = newUIErrFn( ErrInvalidDomainValue = newErrFn(
"Invalid domain value", "Invalid domain value",
"Please check the passed value", "Please check the passed value",
"Domain can only accept DNS compatible values", "Domain can only accept DNS compatible values",
) )
uiErrInvalidErasureSetSize = newUIErrFn( ErrInvalidErasureSetSize = newErrFn(
"Invalid erasure set size", "Invalid erasure set size",
"Please check the passed value", "Please check the passed value",
"Erasure set can only accept any of [4, 6, 8, 10, 12, 14, 16] values", "Erasure set can only accept any of [4, 6, 8, 10, 12, 14, 16] values",
) )
uiErrInvalidWormValue = newUIErrFn( ErrInvalidWormValue = newErrFn(
"Invalid WORM value", "Invalid WORM value",
"Please check the passed value", "Please check the passed value",
"WORM can only accept `on` and `off` values. To enable WORM, set this value to `on`", "WORM can only accept `on` and `off` values. To enable WORM, set this value to `on`",
) )
uiErrInvalidCacheDrivesValue = newUIErrFn( ErrInvalidCacheDrivesValue = newErrFn(
"Invalid cache drive value", "Invalid cache drive value",
"Please check the value in this ENV variable", "Please check the value in this ENV variable",
"MINIO_CACHE_DRIVES: Mounted drives or directories are delimited by `;`", "MINIO_CACHE_DRIVES: Mounted drives or directories are delimited by `;`",
) )
uiErrInvalidCacheExcludesValue = newUIErrFn( ErrInvalidCacheExcludesValue = newErrFn(
"Invalid cache excludes value", "Invalid cache excludes value",
"Please check the passed value", "Please check the passed value",
"MINIO_CACHE_EXCLUDE: Cache exclusion patterns are delimited by `;`", "MINIO_CACHE_EXCLUDE: Cache exclusion patterns are delimited by `;`",
) )
uiErrInvalidCacheExpiryValue = newUIErrFn( ErrInvalidCacheExpiryValue = newErrFn(
"Invalid cache expiry value", "Invalid cache expiry value",
"Please check the passed value", "Please check the passed value",
"MINIO_CACHE_EXPIRY: Valid cache expiry duration is in days", "MINIO_CACHE_EXPIRY: Valid cache expiry duration is in days",
) )
uiErrInvalidCacheMaxUse = newUIErrFn( ErrInvalidCacheMaxUse = newErrFn(
"Invalid cache max-use value", "Invalid cache max-use value",
"Please check the passed value", "Please check the passed value",
"MINIO_CACHE_MAXUSE: Valid cache max-use value between 0-100", "MINIO_CACHE_MAXUSE: Valid cache max-use value between 0-100",
) )
uiErrInvalidCacheEncryptionKey = newUIErrFn( ErrInvalidCacheEncryptionKey = newErrFn(
"Invalid cache encryption master key value", "Invalid cache encryption master key value",
"Please check the passed value", "Please check the passed value",
"MINIO_CACHE_ENCRYPTION_MASTER_KEY: For more information, please refer to https://docs.min.io/docs/minio-disk-cache-guide", "MINIO_CACHE_ENCRYPTION_MASTER_KEY: For more information, please refer to https://docs.min.io/docs/minio-disk-cache-guide",
) )
uiErrInvalidCredentials = newUIErrFn( ErrInvalidCredentials = newErrFn(
"Invalid credentials", "Invalid credentials",
"Please provide correct credentials", "Please provide correct credentials",
`Access key length should be between minimum 3 characters in length. `Access key length should be between minimum 3 characters in length.
Secret key should be in between 8 and 40 characters`, Secret key should be in between 8 and 40 characters`,
) )
uiErrEnvCredentialsMissingGateway = newUIErrFn( ErrEnvCredentialsMissingGateway = newErrFn(
"Credentials missing", "Credentials missing",
"Please set your credentials in the environment", "Please set your credentials in the environment",
`In Gateway mode, access and secret keys should be specified via environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY respectively`, `In Gateway mode, access and secret keys should be specified via environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY respectively`,
) )
uiErrEnvCredentialsMissingDistributed = newUIErrFn( ErrEnvCredentialsMissingDistributed = newErrFn(
"Credentials missing", "Credentials missing",
"Please set your credentials in the environment", "Please set your credentials in the environment",
`In distributed server mode, access and secret keys should be specified via environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY respectively`, `In distributed server mode, access and secret keys should be specified via environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY respectively`,
) )
uiErrInvalidErasureEndpoints = newUIErrFn( ErrInvalidErasureEndpoints = newErrFn(
"Invalid endpoint(s) in erasure mode", "Invalid endpoint(s) in erasure mode",
"Please provide correct combination of local/remote paths", "Please provide correct combination of local/remote paths",
"For more information, please refer to https://docs.min.io/docs/minio-erasure-code-quickstart-guide", "For more information, please refer to https://docs.min.io/docs/minio-erasure-code-quickstart-guide",
) )
uiErrInvalidNumberOfErasureEndpoints = newUIErrFn( ErrInvalidNumberOfErasureEndpoints = newErrFn(
"Invalid total number of endpoints for erasure mode", "Invalid total number of endpoints for erasure mode",
"Please provide an even number of endpoints greater or equal to 4", "Please provide an even number of endpoints greater or equal to 4",
"For more information, please refer to https://docs.min.io/docs/minio-erasure-code-quickstart-guide", "For more information, please refer to https://docs.min.io/docs/minio-erasure-code-quickstart-guide",
) )
uiErrStorageClassValue = newUIErrFn( ErrStorageClassValue = newErrFn(
"Invalid storage class value", "Invalid storage class value",
"Please check the value", "Please check the 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. Objects are stored in Standard mode, if storage class is not defined in Put request `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. Objects are stored in Standard mode, if storage class is not defined in Put request
@ -116,13 +117,13 @@ MINIO_STORAGE_CLASS_RRS: Format "EC:<Default_Parity_Reduced_Redundancy_Class>" (
Refer to the link https://github.com/minio/minio/tree/master/docs/erasure/storage-class for more information`, Refer to the link https://github.com/minio/minio/tree/master/docs/erasure/storage-class for more information`,
) )
uiErrUnexpectedBackendVersion = newUIErrFn( ErrUnexpectedBackendVersion = newErrFn(
"Backend version seems to be too recent", "Backend version seems to be too recent",
"Please update to the latest MinIO version", "Please update to the latest MinIO version",
"", "",
) )
uiErrInvalidAddressFlag = newUIErrFn( ErrInvalidAddressFlag = newErrFn(
"--address input is invalid", "--address input is invalid",
"Please check --address parameter", "Please check --address parameter",
`--address binds to a specific ADDRESS:PORT, ADDRESS can be an IPv4/IPv6 address or hostname (default port is ':9000') `--address binds to a specific ADDRESS:PORT, ADDRESS can be an IPv4/IPv6 address or hostname (default port is ':9000')
@ -131,7 +132,7 @@ Refer to the link https://github.com/minio/minio/tree/master/docs/erasure/storag
--address '[fe80::da00:a6c8:e3ae:ddd7]:9000'`, --address '[fe80::da00:a6c8:e3ae:ddd7]:9000'`,
) )
uiErrInvalidFSEndpoint = newUIErrFn( ErrInvalidFSEndpoint = newErrFn(
"Invalid endpoint for standalone FS mode", "Invalid endpoint for standalone FS mode",
"Please check the FS endpoint", "Please check the FS endpoint",
`FS mode requires only one writable disk path `FS mode requires only one writable disk path
@ -139,91 +140,91 @@ Example 1:
$ minio server /data/minio/`, $ minio server /data/minio/`,
) )
uiErrUnableToWriteInBackend = newUIErrFn( ErrUnableToWriteInBackend = newErrFn(
"Unable to write to the backend", "Unable to write to the backend",
"Please ensure MinIO binary has write permissions for the backend", "Please ensure MinIO binary has write permissions for the backend",
`Verify if MinIO binary is running as the same user who has write permissions for the backend`, `Verify if MinIO binary is running as the same user who has write permissions for the backend`,
) )
uiErrPortAlreadyInUse = newUIErrFn( ErrPortAlreadyInUse = newErrFn(
"Port is already in use", "Port is already in use",
"Please ensure no other program uses the same address/port", "Please ensure no other program uses the same address/port",
"", "",
) )
uiErrPortAccess = newUIErrFn( ErrPortAccess = newErrFn(
"Unable to use specified port", "Unable to use specified port",
"Please ensure MinIO binary has 'cap_net_bind_service=+ep' permissions", "Please ensure MinIO binary has 'cap_net_bind_service=+ep' permissions",
`Use 'sudo setcap cap_net_bind_service=+ep /path/to/minio' to provide sufficient permissions`, `Use 'sudo setcap cap_net_bind_service=+ep /path/to/minio' to provide sufficient permissions`,
) )
uiErrNoPermissionsToAccessDirFiles = newUIErrFn( ErrNoPermissionsToAccessDirFiles = newErrFn(
"Missing permissions to access the specified path", "Missing permissions to access the specified path",
"Please ensure the specified path can be accessed", "Please ensure the specified path can be accessed",
"", "",
) )
uiErrSSLUnexpectedError = newUIErrFn( ErrSSLUnexpectedError = newErrFn(
"Invalid TLS certificate", "Invalid TLS certificate",
"Please check the content of your certificate data", "Please check the content of your certificate data",
`Only PEM (x.509) format is accepted as valid public & private certificates`, `Only PEM (x.509) format is accepted as valid public & private certificates`,
) )
uiErrSSLUnexpectedData = newUIErrFn( ErrSSLUnexpectedData = newErrFn(
"Invalid TLS certificate", "Invalid TLS certificate",
"Please check your certificate", "Please check your certificate",
"", "",
) )
uiErrSSLNoPassword = newUIErrFn( ErrSSLNoPassword = newErrFn(
"Missing TLS password", "Missing TLS password",
"Please set the password to environment variable `"+TLSPrivateKeyPassword+"` so that the private key can be decrypted", "Please set the password to environment variable `MINIO_CERT_PASSWD` so that the private key can be decrypted",
"", "",
) )
uiErrNoCertsAndHTTPSEndpoints = newUIErrFn( ErrNoCertsAndHTTPSEndpoints = newErrFn(
"HTTPS specified in endpoints, but no TLS certificate is found on the local machine", "HTTPS specified in endpoints, but no TLS certificate is found on the local machine",
"Please add TLS certificate or use HTTP endpoints only", "Please add TLS certificate or use HTTP endpoints only",
"Refer to https://docs.min.io/docs/how-to-secure-access-to-minio-server-with-tls for information about how to load a TLS certificate in your server", "Refer to https://docs.min.io/docs/how-to-secure-access-to-minio-server-with-tls for information about how to load a TLS certificate in your server",
) )
uiErrCertsAndHTTPEndpoints = newUIErrFn( ErrCertsAndHTTPEndpoints = newErrFn(
"HTTP specified in endpoints, but the server in the local machine is configured with a TLS certificate", "HTTP specified in endpoints, 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", "Please remove the certificate in the configuration directory or switch to HTTPS",
"", "",
) )
uiErrSSLWrongPassword = newUIErrFn( ErrSSLWrongPassword = newErrFn(
"Unable to decrypt the private key using the provided password", "Unable to decrypt the private key using the provided password",
"Please set the correct password in environment variable "+TLSPrivateKeyPassword, "Please set the correct password in environment variable `MINIO_CERT_PASSWD`",
"", "",
) )
uiErrUnexpectedDataContent = newUIErrFn( ErrUnexpectedDataContent = newErrFn(
"Unexpected data content", "Unexpected data content",
"Please contact MinIO at https://slack.min.io", "Please contact MinIO at https://slack.min.io",
"", "",
) )
uiErrUnexpectedError = newUIErrFn( ErrUnexpectedError = newErrFn(
"Unexpected error", "Unexpected error",
"Please contact MinIO at https://slack.min.io", "Please contact MinIO at https://slack.min.io",
"", "",
) )
uiErrInvalidCompressionIncludesValue = newUIErrFn( ErrInvalidCompressionIncludesValue = newErrFn(
"Invalid compression include value", "Invalid compression include value",
"Please check the passed value", "Please check the passed value",
"Compress extensions/mime-types are delimited by `,`. For eg, MINIO_COMPRESS_ATTR=\"A,B,C\"", "Compress extensions/mime-types are delimited by `,`. For eg, MINIO_COMPRESS_ATTR=\"A,B,C\"",
) )
uiErrInvalidGWSSEValue = newUIErrFn( ErrInvalidGWSSEValue = newErrFn(
"Invalid gateway SSE value", "Invalid gateway SSE value",
"Please check the passed value", "Please check the passed value",
"MINIO_GATEWAY_SSE: Gateway SSE accepts only C and S3 as valid values. Delimit by `;` to set more than one value", "MINIO_GATEWAY_SSE: Gateway SSE accepts only C and S3 as valid values. Delimit by `;` to set more than one value",
) )
uiErrInvalidGWSSEEnvValue = newUIErrFn( ErrInvalidGWSSEEnvValue = newErrFn(
"Invalid gateway SSE configuration", "Invalid gateway SSE configuration",
"", "",
"Refer to https://docs.min.io/docs/minio-kms-quickstart-guide.html for setting up SSE", "Refer to https://docs.min.io/docs/minio-kms-quickstart-guide.html for setting up SSE",

@ -0,0 +1,201 @@
/*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ldap
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"regexp"
"time"
"github.com/minio/minio/pkg/env"
ldap "gopkg.in/ldap.v3"
)
const (
defaultLDAPExpiry = time.Hour * 1
)
// Config contains AD/LDAP server connectivity information.
type Config struct {
IsEnabled bool `json:"enabled"`
// E.g. "ldap.minio.io:636"
ServerAddr string `json:"serverAddr"`
// STS credentials expiry duration
STSExpiryDuration string `json:"stsExpiryDuration"`
stsExpiryDuration time.Duration // contains converted value
RootCAs *x509.CertPool `json:"-"`
// Format string for usernames
UsernameFormat string `json:"usernameFormat"`
GroupSearchBaseDN string `json:"groupSearchBaseDN"`
GroupSearchFilter string `json:"groupSearchFilter"`
GroupNameAttribute string `json:"groupNameAttribute"`
}
// LDAP keys and envs.
const (
ServerAddr = "server_addr"
STSExpiry = "sts_expiry"
UsernameFormat = "username_format"
GroupSearchFilter = "group_search_filter"
GroupNameAttribute = "group_name_attribute"
GroupSearchBaseDN = "group_search_base_dn"
EnvServerAddr = "MINIO_IDENTITY_LDAP_SERVER_ADDR"
EnvSTSExpiry = "MINIO_IDENTITY_LDAP_STS_EXPIRY"
EnvUsernameFormat = "MINIO_IDENTITY_LDAP_USERNAME_FORMAT"
EnvGroupSearchFilter = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER"
EnvGroupNameAttribute = "MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE"
EnvGroupSearchBaseDN = "MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN"
)
// Connect connect to ldap server.
func (l *Config) Connect() (ldapConn *ldap.Conn, err error) {
if l == nil {
// Happens when LDAP is not configured.
return
}
return ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{RootCAs: l.RootCAs})
}
// GetExpiryDuration - return parsed expiry duration.
func (l Config) GetExpiryDuration() time.Duration {
return l.stsExpiryDuration
}
// Lookup - initializes LDAP config, overrides config, if any ENV values are set.
func Lookup(cfg Config, rootCAs *x509.CertPool) (l Config, err error) {
if cfg.ServerAddr == "" && cfg.IsEnabled {
return l, errors.New("ldap server cannot initialize with empty LDAP server")
}
l.RootCAs = rootCAs
ldapServer := env.Get(EnvServerAddr, cfg.ServerAddr)
if ldapServer == "" {
return l, nil
}
l.IsEnabled = true
l.ServerAddr = ldapServer
l.stsExpiryDuration = defaultLDAPExpiry
if v := env.Get(EnvSTSExpiry, cfg.STSExpiryDuration); v != "" {
expDur, err := time.ParseDuration(v)
if err != nil {
return l, errors.New("LDAP expiry time err:" + err.Error())
}
if expDur <= 0 {
return l, errors.New("LDAP expiry time has to be positive")
}
l.STSExpiryDuration = v
l.stsExpiryDuration = expDur
}
if v := env.Get(EnvUsernameFormat, cfg.UsernameFormat); v != "" {
subs, err := NewSubstituter("username", "test")
if err != nil {
return l, err
}
if _, err := subs.Substitute(v); err != nil {
return l, fmt.Errorf("Only username may be substituted in the username format: %s", err)
}
l.UsernameFormat = v
}
grpSearchFilter := env.Get(EnvGroupSearchFilter, cfg.GroupSearchFilter)
grpSearchNameAttr := env.Get(EnvGroupNameAttribute, cfg.GroupNameAttribute)
grpSearchBaseDN := env.Get(EnvGroupSearchBaseDN, cfg.GroupSearchBaseDN)
// Either all group params must be set or none must be set.
allNotSet := grpSearchFilter == "" && grpSearchNameAttr == "" && grpSearchBaseDN == ""
allSet := grpSearchFilter != "" && grpSearchNameAttr != "" && grpSearchBaseDN != ""
if !allNotSet && !allSet {
return l, errors.New("All group related parameters must be set")
}
if allSet {
subs, err := NewSubstituter("username", "test", "usernamedn", "test2")
if err != nil {
return l, err
}
if _, err := subs.Substitute(grpSearchFilter); err != nil {
return l, fmt.Errorf("Only username and usernamedn may be substituted in the group search filter string: %s", err)
}
l.GroupSearchFilter = grpSearchFilter
l.GroupNameAttribute = grpSearchNameAttr
subs, err = NewSubstituter("username", "test", "usernamedn", "test2")
if err != nil {
return l, err
}
if _, err := subs.Substitute(grpSearchBaseDN); err != nil {
return l, fmt.Errorf("Only username and usernamedn may be substituted in the base DN string: %s", err)
}
l.GroupSearchBaseDN = grpSearchBaseDN
}
return
}
// Substituter - This type is to allow restricted runtime
// substitutions of variables in LDAP configuration items during
// runtime.
type Substituter struct {
vals map[string]string
}
// NewSubstituter - sets up the substituter for usage, for e.g.:
//
// subber := NewSubstituter("username", "john")
func NewSubstituter(v ...string) (Substituter, error) {
if len(v)%2 != 0 {
return Substituter{}, errors.New("Need an even number of arguments")
}
vals := make(map[string]string)
for i := 0; i < len(v); i += 2 {
vals[v[i]] = v[i+1]
}
return Substituter{vals: vals}, nil
}
// Substitute - performs substitution on the given string `t`. Returns
// an error if there are any variables in the input that do not have
// values in the substituter. E.g.:
//
// subber.Substitute("uid=${username},cn=users,dc=example,dc=com")
//
// returns "uid=john,cn=users,dc=example,dc=com"
//
// whereas:
//
// subber.Substitute("uid=${usernamedn}")
//
// returns an error.
func (s *Substituter) Substitute(t string) (string, error) {
for k, v := range s.vals {
re := regexp.MustCompile(fmt.Sprintf(`\$\{%s\}`, k))
t = re.ReplaceAllLiteralString(t, v)
}
// Check if all requested substitutions have been made.
re := regexp.MustCompile(`\$\{.*\}`)
if re.MatchString(t) {
return "", errors.New("unsupported substitution requested")
}
return t, nil
}

@ -0,0 +1,65 @@
/*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ldap
import (
"fmt"
"testing"
)
func TestSubstituter(t *testing.T) {
tests := []struct {
KV []string
SubstitutableStr string
SubstitutedStr string
ErrExpected bool
}{
{
KV: []string{"username", "john"},
SubstitutableStr: "uid=${username},cn=users,dc=example,dc=com",
SubstitutedStr: "uid=john,cn=users,dc=example,dc=com",
ErrExpected: false,
},
{
KV: []string{"username", "john"},
SubstitutableStr: "uid=${usernamedn},cn=users,dc=example,dc=com",
ErrExpected: true,
},
{
KV: []string{"username"},
SubstitutableStr: "uid=${usernamedn},cn=users,dc=example,dc=com",
ErrExpected: true,
},
}
for i, test := range tests {
test := test
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
subber, err := NewSubstituter(test.KV...)
if err != nil && !test.ErrExpected {
t.Errorf("Unexpected failure %s", err)
}
gotStr, err := subber.Substitute(test.SubstitutableStr)
if err != nil && !test.ErrExpected {
t.Errorf("Unexpected failure %s", err)
}
if gotStr != test.SubstitutedStr {
t.Errorf("Expected %s, got %s", test.SubstitutedStr, gotStr)
}
})
}
}

@ -47,8 +47,6 @@ const (
cacheDataFile = "part.1" cacheDataFile = "part.1"
cacheMetaVersion = "1.0.0" cacheMetaVersion = "1.0.0"
cacheEnvDelimiter = ";"
// SSECacheEncrypted is the metadata key indicating that the object // SSECacheEncrypted is the metadata key indicating that the object
// is a cache entry encrypted with cache KMS master key in globalCacheKMS. // is a cache entry encrypted with cache KMS master key in globalCacheKMS.
SSECacheEncrypted = "X-Minio-Internal-Encrypted-Cache" SSECacheEncrypted = "X-Minio-Internal-Encrypted-Cache"

@ -13,7 +13,9 @@ import (
"time" "time"
"github.com/djherbis/atime" "github.com/djherbis/atime"
"github.com/minio/minio/cmd/config/cache"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/color"
"github.com/minio/minio/pkg/wildcard" "github.com/minio/minio/pkg/wildcard"
) )
@ -389,7 +391,7 @@ func (c *cacheObjects) hashIndex(bucket, object string) int {
// newCache initializes the cacheFSObjects for the "drives" specified in config.json // newCache initializes the cacheFSObjects for the "drives" specified in config.json
// or the global env overrides. // or the global env overrides.
func newCache(config CacheConfig) ([]*diskCache, bool, error) { func newCache(config cache.Config) ([]*diskCache, bool, error) {
var caches []*diskCache var caches []*diskCache
ctx := logger.SetReqInfo(context.Background(), &logger.ReqInfo{}) ctx := logger.SetReqInfo(context.Background(), &logger.ReqInfo{})
formats, migrating, err := loadAndValidateCacheFormat(ctx, config.Drives) formats, migrating, err := loadAndValidateCacheFormat(ctx, config.Drives)
@ -446,7 +448,7 @@ func checkAtimeSupport(dir string) (err error) {
return return
} }
func (c *cacheObjects) migrateCacheFromV1toV2(ctx context.Context) { func (c *cacheObjects) migrateCacheFromV1toV2(ctx context.Context) {
logStartupMessage(colorBlue("Cache migration initiated ....")) logStartupMessage(color.Blue("Cache migration initiated ...."))
var wg sync.WaitGroup var wg sync.WaitGroup
errs := make([]error, len(c.cache)) errs := make([]error, len(c.cache))
@ -482,7 +484,7 @@ func (c *cacheObjects) migrateCacheFromV1toV2(ctx context.Context) {
c.migMutex.Lock() c.migMutex.Lock()
defer c.migMutex.Unlock() defer c.migMutex.Unlock()
c.migrating = false c.migrating = false
logStartupMessage(colorBlue("Cache migration completed successfully.")) logStartupMessage(color.Blue("Cache migration completed successfully."))
} }
// PutObject - caches the uploaded object for single Put operations // PutObject - caches the uploaded object for single Put operations
@ -535,7 +537,7 @@ func (c *cacheObjects) PutObject(ctx context.Context, bucket, object string, r *
} }
// Returns cacheObjects for use by Server. // Returns cacheObjects for use by Server.
func newServerCacheObjects(ctx context.Context, config CacheConfig) (CacheObjectLayer, error) { func newServerCacheObjects(ctx context.Context, config cache.Config) (CacheObjectLayer, error) {
// list of disk caches for cache "drives" specified in config.json or MINIO_CACHE_DRIVES env var. // list of disk caches for cache "drives" specified in config.json or MINIO_CACHE_DRIVES env var.
cache, migrateSw, err := newCache(config) cache, migrateSw, err := newCache(config)
if err != nil { if err != nil {

@ -18,12 +18,13 @@ package cmd
import ( import (
"fmt" "fmt"
"os"
"strconv" "strconv"
"strings" "strings"
"github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio-go/v6/pkg/set"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/ellipses" "github.com/minio/minio/pkg/ellipses"
"github.com/minio/minio/pkg/env"
) )
// This file implements and supports ellipses pattern for // This file implements and supports ellipses pattern for
@ -77,13 +78,13 @@ func getSetIndexes(args []string, totalSizes []uint64) (setIndexes [][]uint64, e
} }
var customSetDriveCount uint64 var customSetDriveCount uint64
if v := os.Getenv("MINIO_ERASURE_SET_DRIVE_COUNT"); v != "" { if v := env.Get("MINIO_ERASURE_SET_DRIVE_COUNT", ""); v != "" {
customSetDriveCount, err = strconv.ParseUint(v, 10, 64) customSetDriveCount, err = strconv.ParseUint(v, 10, 64)
if err != nil { if err != nil {
return nil, uiErrInvalidErasureSetSize(err) return nil, config.ErrInvalidErasureSetSize(err)
} }
if !isValidSetSize(customSetDriveCount) { if !isValidSetSize(customSetDriveCount) {
return nil, uiErrInvalidErasureSetSize(nil) return nil, config.ErrInvalidErasureSetSize(nil)
} }
} }
@ -91,7 +92,7 @@ func getSetIndexes(args []string, totalSizes []uint64) (setIndexes [][]uint64, e
for _, totalSize := range totalSizes { for _, totalSize := range totalSizes {
// Check if totalSize has minimum range upto setSize // Check if totalSize has minimum range upto setSize
if totalSize < setSizes[0] || totalSize < customSetDriveCount { if totalSize < setSizes[0] || totalSize < customSetDriveCount {
return nil, uiErrInvalidNumberOfErasureEndpoints(nil) return nil, config.ErrInvalidNumberOfErasureEndpoints(nil)
} }
} }
@ -125,17 +126,17 @@ func getSetIndexes(args []string, totalSizes []uint64) (setIndexes [][]uint64, e
if customSetDriveCount > 0 { if customSetDriveCount > 0 {
msg := fmt.Sprintf("Invalid set drive count, leads to non-uniform distribution for the given number of disks. Possible values for custom set count are %d", possibleSetCounts(setSize)) msg := fmt.Sprintf("Invalid set drive count, leads to non-uniform distribution for the given number of disks. Possible values for custom set count are %d", possibleSetCounts(setSize))
if customSetDriveCount > setSize { if customSetDriveCount > setSize {
return nil, uiErrInvalidErasureSetSize(nil).Msg(msg) return nil, config.ErrInvalidErasureSetSize(nil).Msg(msg)
} }
if setSize%customSetDriveCount != 0 { if setSize%customSetDriveCount != 0 {
return nil, uiErrInvalidErasureSetSize(nil).Msg(msg) return nil, config.ErrInvalidErasureSetSize(nil).Msg(msg)
} }
setSize = customSetDriveCount setSize = customSetDriveCount
} }
// Check whether setSize is with the supported range. // Check whether setSize is with the supported range.
if !isValidSetSize(setSize) { if !isValidSetSize(setSize) {
return nil, uiErrInvalidNumberOfErasureEndpoints(nil) return nil, config.ErrInvalidNumberOfErasureEndpoints(nil)
} }
for i := range totalSizes { for i := range totalSizes {
@ -198,14 +199,14 @@ func parseEndpointSet(args ...string) (ep endpointSet, err error) {
for i, arg := range args { for i, arg := range args {
patterns, perr := ellipses.FindEllipsesPatterns(arg) patterns, perr := ellipses.FindEllipsesPatterns(arg)
if perr != nil { if perr != nil {
return endpointSet{}, uiErrInvalidErasureEndpoints(nil).Msg(perr.Error()) return endpointSet{}, config.ErrInvalidErasureEndpoints(nil).Msg(perr.Error())
} }
argPatterns[i] = patterns argPatterns[i] = patterns
} }
ep.setIndexes, err = getSetIndexes(args, getTotalSizes(argPatterns)) ep.setIndexes, err = getSetIndexes(args, getTotalSizes(argPatterns))
if err != nil { if err != nil {
return endpointSet{}, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) return endpointSet{}, config.ErrInvalidErasureEndpoints(nil).Msg(err.Error())
} }
ep.argPatterns = argPatterns ep.argPatterns = argPatterns
@ -254,7 +255,7 @@ func GetAllSets(args ...string) ([][]string, error) {
for _, sargs := range setArgs { for _, sargs := range setArgs {
for _, arg := range sargs { for _, arg := range sargs {
if uniqueArgs.Contains(arg) { if uniqueArgs.Contains(arg) {
return nil, uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("Input args (%s) has duplicate ellipses", args)) return nil, config.ErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("Input args (%s) has duplicate ellipses", args))
} }
uniqueArgs.Add(arg) uniqueArgs.Add(arg)
} }

@ -21,7 +21,6 @@ import (
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"os"
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -32,7 +31,9 @@ import (
humanize "github.com/dustin/go-humanize" humanize "github.com/dustin/go-humanize"
"github.com/minio/cli" "github.com/minio/cli"
"github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio-go/v6/pkg/set"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/env"
"github.com/minio/minio/pkg/mountinfo" "github.com/minio/minio/pkg/mountinfo"
) )
@ -377,14 +378,14 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
return serverAddr, endpoints, setupType, err return serverAddr, endpoints, setupType, err
} }
if endpoint.Type() != PathEndpointType { if endpoint.Type() != PathEndpointType {
return serverAddr, endpoints, setupType, uiErrInvalidFSEndpoint(nil).Msg("use path style endpoint for FS setup") return serverAddr, endpoints, setupType, config.ErrInvalidFSEndpoint(nil).Msg("use path style endpoint for FS setup")
} }
endpoints = append(endpoints, endpoint) endpoints = append(endpoints, endpoint)
setupType = FSSetupType setupType = FSSetupType
// Check for cross device mounts if any. // Check for cross device mounts if any.
if err = checkCrossDeviceMounts(endpoints); err != nil { if err = checkCrossDeviceMounts(endpoints); err != nil {
return serverAddr, endpoints, setupType, uiErrInvalidFSEndpoint(nil).Msg(err.Error()) return serverAddr, endpoints, setupType, config.ErrInvalidFSEndpoint(nil).Msg(err.Error())
} }
return serverAddr, endpoints, setupType, nil return serverAddr, endpoints, setupType, nil
} }
@ -395,12 +396,12 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
var eps EndpointList var eps EndpointList
eps, err = NewEndpointList(iargs...) eps, err = NewEndpointList(iargs...)
if err != nil { if err != nil {
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) return serverAddr, endpoints, setupType, config.ErrInvalidErasureEndpoints(nil).Msg(err.Error())
} }
// Check for cross device mounts if any. // Check for cross device mounts if any.
if err = checkCrossDeviceMounts(eps); err != nil { if err = checkCrossDeviceMounts(eps); err != nil {
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) return serverAddr, endpoints, setupType, config.ErrInvalidErasureEndpoints(nil).Msg(err.Error())
} }
for _, ep := range eps { for _, ep := range eps {
@ -417,7 +418,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
} }
if err := endpoints.UpdateIsLocal(); err != nil { if err := endpoints.UpdateIsLocal(); err != nil {
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) return serverAddr, endpoints, setupType, config.ErrInvalidErasureEndpoints(nil).Msg(err.Error())
} }
// Here all endpoints are URL style. // Here all endpoints are URL style.
@ -445,7 +446,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
// No local endpoint found. // No local endpoint found.
if localEndpointCount == 0 { if localEndpointCount == 0 {
return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg("no endpoint pointing to the local machine is found") return serverAddr, endpoints, setupType, config.ErrInvalidErasureEndpoints(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. // Check whether same path is not used in endpoints of a host on different port.
@ -461,7 +462,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
if IPSet, ok := pathIPMap[endpoint.Path]; ok { if IPSet, ok := pathIPMap[endpoint.Path]; ok {
if !IPSet.Intersection(hostIPSet).IsEmpty() { if !IPSet.Intersection(hostIPSet).IsEmpty() {
return serverAddr, endpoints, setupType, return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' can not be served by different port on same address", endpoint.Path)) config.ErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' can not be served by different port on same address", endpoint.Path))
} }
pathIPMap[endpoint.Path] = IPSet.Union(hostIPSet) pathIPMap[endpoint.Path] = IPSet.Union(hostIPSet)
} else { } else {
@ -479,7 +480,7 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
} }
if localPathSet.Contains(endpoint.Path) { if localPathSet.Contains(endpoint.Path) {
return serverAddr, endpoints, setupType, return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' cannot be served by different address on same server", endpoint.Path)) config.ErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' cannot be served by different address on same server", endpoint.Path))
} }
localPathSet.Add(endpoint.Path) localPathSet.Add(endpoint.Path)
} }
@ -490,10 +491,10 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
if !localPortSet.Contains(serverAddrPort) { if !localPortSet.Contains(serverAddrPort) {
if len(localPortSet) > 1 { if len(localPortSet) > 1 {
return serverAddr, endpoints, setupType, return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg("port number in server address must match with one of the port in local endpoints") config.ErrInvalidErasureEndpoints(nil).Msg("port number in server address must match with one of the port in local endpoints")
} }
return serverAddr, endpoints, setupType, return serverAddr, endpoints, setupType,
uiErrInvalidErasureEndpoints(nil).Msg("server address and local endpoint have different ports") config.ErrInvalidErasureEndpoints(nil).Msg("server address and local endpoint have different ports")
} }
} }
@ -571,9 +572,9 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
return serverAddr, endpoints, setupType, err return serverAddr, endpoints, setupType, err
} }
_, dok := os.LookupEnv("MINIO_DOMAIN") _, dok := env.Lookup("MINIO_DOMAIN")
_, eok := os.LookupEnv("MINIO_ETCD_ENDPOINTS") _, eok := env.Lookup("MINIO_ETCD_ENDPOINTS")
_, iok := os.LookupEnv("MINIO_PUBLIC_IPS") _, iok := env.Lookup("MINIO_PUBLIC_IPS")
if dok && eok && !iok { if dok && eok && !iok {
updateDomainIPs(uniqueArgs) updateDomainIPs(uniqueArgs)
} }

@ -18,11 +18,11 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"os"
"strconv" "strconv"
"strings" "strings"
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
"github.com/minio/minio/pkg/env"
) )
const ( const (
@ -76,30 +76,6 @@ const (
EnvVaultNamespace = "MINIO_SSE_VAULT_NAMESPACE" EnvVaultNamespace = "MINIO_SSE_VAULT_NAMESPACE"
) )
// Environment provides functions for accessing environment
// variables.
var Environment = environment{}
type environment struct{}
// Get retrieves the value of the environment variable named
// by the key. If the variable is present in the environment the
// value (which may be empty) is returned. Otherwise it returns
// the specified default value.
func (environment) Get(key, defaultValue string) string {
if v, ok := os.LookupEnv(key); ok {
return v
}
return defaultValue
}
// Lookup retrieves the value of the environment variable named
// by the key. If the variable is present in the environment the
// value (which may be empty) is returned and the boolean is true.
// Otherwise the returned value will be empty and the boolean will
// be false.
func (environment) Lookup(key string) (string, bool) { return os.LookupEnv(key) }
// LookupKMSConfig extracts the KMS configuration provided by environment // LookupKMSConfig extracts the KMS configuration provided by environment
// variables and merge them with the provided KMS configuration. The // variables and merge them with the provided KMS configuration. The
// merging follows the following rules: // merging follows the following rules:
@ -113,7 +89,7 @@ func (environment) Lookup(key string) (string, bool) { return os.LookupEnv(key)
// //
// It sets the global KMS configuration according to the merged configuration // It sets the global KMS configuration according to the merged configuration
// on success. // on success.
func (env environment) LookupKMSConfig(config crypto.KMSConfig) (err error) { func LookupKMSConfig(config crypto.KMSConfig) (err error) {
// Lookup Hashicorp-Vault configuration & overwrite config entry if ENV var is present // Lookup Hashicorp-Vault configuration & overwrite config entry if ENV var is present
config.Vault.Endpoint = env.Get(EnvVaultEndpoint, config.Vault.Endpoint) config.Vault.Endpoint = env.Get(EnvVaultEndpoint, config.Vault.Endpoint)
config.Vault.CAPath = env.Get(EnvVaultCAPath, config.Vault.CAPath) config.Vault.CAPath = env.Get(EnvVaultCAPath, config.Vault.CAPath)

@ -24,6 +24,7 @@ import (
"path" "path"
"time" "time"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/lock" "github.com/minio/minio/pkg/lock"
) )
@ -153,7 +154,7 @@ func formatFSMigrate(ctx context.Context, wlk *lock.LockedFile, fsPath string) e
return err return err
} }
if version != formatFSVersionV2 { if version != formatFSVersionV2 {
return uiErrUnexpectedBackendVersion(fmt.Errorf(`%s file: expected FS version: %s, found FS version: %s`, formatConfigFile, formatFSVersionV2, version)) return config.ErrUnexpectedBackendVersion(fmt.Errorf(`%s file: expected FS version: %s, found FS version: %s`, formatConfigFile, formatFSVersionV2, version))
} }
return nil return nil
} }

@ -32,6 +32,7 @@ import (
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/minio/minio-go/v6/pkg/s3utils" "github.com/minio/minio-go/v6/pkg/s3utils"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/lifecycle" "github.com/minio/minio/pkg/lifecycle"
"github.com/minio/minio/pkg/lock" "github.com/minio/minio/pkg/lock"
@ -116,7 +117,7 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
if err == errMinDiskSize { if err == errMinDiskSize {
return nil, err return nil, err
} }
return nil, uiErrUnableToWriteInBackend(err) return nil, config.ErrUnableToWriteInBackend(err)
} }
// Assign a new UUID for FS minio mode. Each server instance // Assign a new UUID for FS minio mode. Each server instance

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2017 MinIO, Inc. * MinIO Cloud Storage, (C) 2017-2019 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,11 +18,12 @@ package cmd
import ( import (
"net/http" "net/http"
"os"
"strings" "strings"
"github.com/minio/minio/cmd/config"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/env"
"github.com/minio/minio/pkg/hash" "github.com/minio/minio/pkg/hash"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
@ -365,14 +366,14 @@ func parseGatewaySSE(s string) (gatewaySSE, error) {
gwSlice = append(gwSlice, v) gwSlice = append(gwSlice, v)
continue continue
} }
return nil, uiErrInvalidGWSSEValue(nil).Msg("gateway SSE cannot be (%s) ", v) return nil, config.ErrInvalidGWSSEValue(nil).Msg("gateway SSE cannot be (%s) ", v)
} }
return gatewaySSE(gwSlice), nil return gatewaySSE(gwSlice), nil
} }
// handle gateway env vars // handle gateway env vars
func handleGatewayEnvVars() { func handleGatewayEnvVars() {
gwsseVal, ok := os.LookupEnv("MINIO_GATEWAY_SSE") gwsseVal, ok := env.Lookup("MINIO_GATEWAY_SSE")
if ok { if ok {
var err error var err error
GlobalGatewaySSE, err = parseGatewaySSE(gwsseVal) GlobalGatewaySSE, err = parseGatewaySSE(gwsseVal)

@ -27,14 +27,17 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/minio/cli" "github.com/minio/cli"
"github.com/minio/minio/cmd/config"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/certs" "github.com/minio/minio/pkg/certs"
"github.com/minio/minio/pkg/color"
"github.com/minio/minio/pkg/env"
) )
func init() { func init() {
logger.Init(GOPATH, GOROOT) logger.Init(GOPATH, GOROOT)
logger.RegisterUIError(fmtError) logger.RegisterError(config.FmtError)
} }
var ( var (
@ -141,7 +144,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// Validate if we have access, secret set through environment. // Validate if we have access, secret set through environment.
if !globalIsEnvCreds { if !globalIsEnvCreds {
logger.Fatal(uiErrEnvCredentialsMissingGateway(nil), "Unable to start gateway") logger.Fatal(config.ErrEnvCredentialsMissingGateway(nil), "Unable to start gateway")
} }
// Set system resources to maximum. // Set system resources to maximum.
@ -250,7 +253,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
loadLoggers() loadLoggers()
// This is only to uniquely identify each gateway deployments. // This is only to uniquely identify each gateway deployments.
globalDeploymentID = os.Getenv("MINIO_GATEWAY_DEPLOYMENT_ID") globalDeploymentID = env.Get("MINIO_GATEWAY_DEPLOYMENT_ID", mustGetUUID())
logger.SetDeploymentID(globalDeploymentID) logger.SetDeploymentID(globalDeploymentID)
var cacheConfig = globalServerConfig.GetCacheConfig() var cacheConfig = globalServerConfig.GetCacheConfig()
@ -298,7 +301,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// Print a warning message if gateway is not ready for production before the startup banner. // Print a warning message if gateway is not ready for production before the startup banner.
if !gw.Production() { if !gw.Production() {
logStartupMessage(colorYellow(" *** Warning: Not Ready for Production ***")) logStartupMessage(color.Yellow(" *** Warning: Not Ready for Production ***"))
} }
// Print gateway startup message. // Print gateway startup message.

@ -20,6 +20,8 @@ import (
"context" "context"
"fmt" "fmt"
"strings" "strings"
"github.com/minio/minio/pkg/color"
) )
// Prints the formatted startup message. // Prints the formatted startup message.
@ -55,15 +57,15 @@ func printGatewayCommonMsg(apiEndpoints []string) {
apiEndpointStr := strings.Join(apiEndpoints, " ") apiEndpointStr := strings.Join(apiEndpoints, " ")
// Colorize the message and print. // Colorize the message and print.
logStartupMessage(colorBlue("Endpoint: ") + colorBold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr))) logStartupMessage(color.Blue("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
if isTerminal() && !globalCLIContext.Anonymous { if color.IsTerminal() && !globalCLIContext.Anonymous {
logStartupMessage(colorBlue("AccessKey: ") + colorBold(fmt.Sprintf("%s ", cred.AccessKey))) logStartupMessage(color.Blue("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
logStartupMessage(colorBlue("SecretKey: ") + colorBold(fmt.Sprintf("%s ", cred.SecretKey))) logStartupMessage(color.Blue("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
} }
printEventNotifiers() printEventNotifiers()
if globalIsBrowserEnabled { if globalIsBrowserEnabled {
logStartupMessage(colorBlue("\nBrowser Access:")) logStartupMessage(color.Blue("\nBrowser Access:"))
logStartupMessage(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr)) logStartupMessage(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr))
} }
} }

@ -30,7 +30,6 @@ import (
"path" "path"
"strconv" "strconv"
"os"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -41,6 +40,7 @@ import (
miniogopolicy "github.com/minio/minio-go/v6/pkg/policy" miniogopolicy "github.com/minio/minio-go/v6/pkg/policy"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/env"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
"github.com/minio/minio/pkg/policy/condition" "github.com/minio/minio/pkg/policy/condition"
@ -158,7 +158,7 @@ EXAMPLES:
// Handler for 'minio gateway gcs' command line. // Handler for 'minio gateway gcs' command line.
func gcsGatewayMain(ctx *cli.Context) { func gcsGatewayMain(ctx *cli.Context) {
projectID := ctx.Args().First() projectID := ctx.Args().First()
if projectID == "" && os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") == "" { if projectID == "" && env.Get("GOOGLE_APPLICATION_CREDENTIALS", "") == "" {
logger.LogIf(context.Background(), errGCSProjectIDNotFound) logger.LogIf(context.Background(), errGCSProjectIDNotFound)
cli.ShowCommandHelpAndExit(ctx, "gcs", 1) cli.ShowCommandHelpAndExit(ctx, "gcs", 1)
} }
@ -190,7 +190,7 @@ func (g *GCS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
if g.projectID == "" { if g.projectID == "" {
// If project ID is not provided on command line, we figure it out // If project ID is not provided on command line, we figure it out
// from the credentials.json file. // from the credentials.json file.
g.projectID, err = gcsParseProjectID(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")) g.projectID, err = gcsParseProjectID(env.Get("GOOGLE_APPLICATION_CREDENTIALS", ""))
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -41,6 +41,7 @@ import (
minio "github.com/minio/minio/cmd" minio "github.com/minio/minio/cmd"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/env"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
) )
@ -126,32 +127,24 @@ func (g *HDFS) Name() string {
} }
func getKerberosClient() (*krb.Client, error) { func getKerberosClient() (*krb.Client, error) {
configPath := os.Getenv("KRB5_CONFIG") cfg, err := config.Load(env.Get("KRB5_CONFIG", "/etc/krb5.conf"))
if configPath == "" { if err != nil {
configPath = "/etc/krb5.conf" return nil, err
} }
cfg, err := config.Load(configPath) u, err := user.Current()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Determine the ccache location from the environment, // Determine the ccache location from the environment, falling back to the default location.
// falling back to the default location. ccachePath := env.Get("KRB5CCNAME", fmt.Sprintf("/tmp/krb5cc_%s", u.Uid))
ccachePath := os.Getenv("KRB5CCNAME")
if strings.Contains(ccachePath, ":") { if strings.Contains(ccachePath, ":") {
if strings.HasPrefix(ccachePath, "FILE:") { if strings.HasPrefix(ccachePath, "FILE:") {
ccachePath = strings.TrimPrefix(ccachePath, "FILE:") ccachePath = strings.TrimPrefix(ccachePath, "FILE:")
} else { } else {
return nil, fmt.Errorf("unable to use kerberos ccache: %s", ccachePath) return nil, fmt.Errorf("unable to use kerberos ccache: %s", ccachePath)
} }
} else if ccachePath == "" {
u, err := user.Current()
if err != nil {
return nil, err
}
ccachePath = fmt.Sprintf("/tmp/krb5cc_%s", u.Uid)
} }
ccache, err := credentials.LoadCCache(ccachePath) ccache, err := credentials.LoadCCache(ccachePath)
@ -192,20 +185,18 @@ func (g *HDFS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error
opts.Addresses = addresses opts.Addresses = addresses
} }
u, err := user.Current()
if err != nil {
return nil, fmt.Errorf("Unable to lookup local user: %s", err)
}
if opts.KerberosClient != nil { if opts.KerberosClient != nil {
opts.KerberosClient, err = getKerberosClient() opts.KerberosClient, err = getKerberosClient()
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to initialize kerberos client: %s", err) return nil, fmt.Errorf("Unable to initialize kerberos client: %s", err)
} }
} else { } else {
opts.User = os.Getenv("HADOOP_USER_NAME") opts.User = env.Get("HADOOP_USER_NAME", u.Username)
if opts.User == "" {
u, err := user.Current()
if err != nil {
return nil, fmt.Errorf("Unable to lookup local user: %s", err)
}
opts.User = u.Username
}
} }
clnt, err := hdfs.NewClient(opts) clnt, err := hdfs.NewClient(opts)

@ -18,16 +18,13 @@ package cmd
import ( import (
"crypto/x509" "crypto/x509"
"fmt"
"os" "os"
"time" "time"
isatty "github.com/mattn/go-isatty"
"github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio-go/v6/pkg/set"
etcd "github.com/coreos/etcd/clientv3" etcd "github.com/coreos/etcd/clientv3"
humanize "github.com/dustin/go-humanize" humanize "github.com/dustin/go-humanize"
"github.com/fatih/color"
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
@ -269,7 +266,7 @@ var (
standardExcludeCompressContentTypes = []string{"video/*", "audio/*", "application/zip", "application/x-gzip", "application/x-zip-compressed", " application/x-compress", "application/x-spoon"} standardExcludeCompressContentTypes = []string{"video/*", "audio/*", "application/zip", "application/x-gzip", "application/x-zip-compressed", " application/x-compress", "application/x-spoon"}
// Authorization validators list. // Authorization validators list.
globalIAMValidators *openid.Validators globalOpenIDValidators *openid.Validators
// OPA policy system. // OPA policy system.
globalPolicyOPA *iampolicy.Opa globalPolicyOPA *iampolicy.Opa
@ -288,64 +285,6 @@ var (
// Add new variable global values here. // Add new variable global values here.
) )
// global colors.
var (
// Check if we stderr, stdout are dumb terminals, we do not apply
// ansi coloring on dumb terminals.
isTerminal = func() bool {
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())
}
colorBold = func() func(a ...interface{}) string {
if isTerminal() {
return color.New(color.Bold).SprintFunc()
}
return fmt.Sprint
}()
colorRed = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.FgRed).SprintfFunc()
}
return fmt.Sprintf
}()
colorBlue = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.FgBlue).SprintfFunc()
}
return fmt.Sprintf
}()
colorYellow = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.FgYellow).SprintfFunc()
}
return fmt.Sprintf
}()
colorCyanBold = func() func(a ...interface{}) string {
if isTerminal() {
color.New(color.FgCyan, color.Bold).SprintFunc()
}
return fmt.Sprint
}()
colorYellowBold = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.FgYellow, color.Bold).SprintfFunc()
}
return fmt.Sprintf
}()
colorBgYellow = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.BgYellow).SprintfFunc()
}
return fmt.Sprintf
}()
colorBlack = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.FgBlack).SprintfFunc()
}
return fmt.Sprintf
}()
)
// Returns minio global information, as a key value map. // Returns minio global information, as a key value map.
// returned list of global values is not an exhaustive // returned list of global values is not an exhaustive
// list. Feel free to add new relevant fields. // list. Feel free to add new relevant fields.

@ -1,180 +0,0 @@
/*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log"
"os"
"regexp"
"time"
ldap "gopkg.in/ldap.v3"
)
const (
defaultLDAPExpiry = time.Hour * 1
)
// ldapServerConfig contains server connectivity information.
type ldapServerConfig struct {
IsEnabled bool `json:"enabled"`
// E.g. "ldap.minio.io:636"
ServerAddr string `json:"serverAddr"`
// STS credentials expiry duration
STSExpiryDuration string `json:"stsExpiryDuration"`
stsExpiryDuration time.Duration // contains converted value
rootCAs *x509.CertPool // contains custom CAs for ldaps server.
// Skips TLS verification (for testing, not
// recommended in production).
SkipTLSVerify bool `json:"skipTLSverify"`
// Format string for usernames
UsernameFormat string `json:"usernameFormat"`
GroupSearchBaseDN string `json:"groupSearchBaseDN"`
GroupSearchFilter string `json:"groupSearchFilter"`
GroupNameAttribute string `json:"groupNameAttribute"`
}
func (l *ldapServerConfig) Connect() (ldapConn *ldap.Conn, err error) {
if l == nil {
// Happens when LDAP is not configured.
return
}
if l.SkipTLSVerify {
ldapConn, err = ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{RootCAs: l.rootCAs, InsecureSkipVerify: true})
} else {
ldapConn, err = ldap.DialTLS("tcp", l.ServerAddr, &tls.Config{RootCAs: l.rootCAs})
}
return
}
// newLDAPConfigFromEnv loads configuration from the environment
func newLDAPConfigFromEnv(rootCAs *x509.CertPool) (l ldapServerConfig, err error) {
if ldapServer, ok := os.LookupEnv("MINIO_IDENTITY_LDAP_SERVER_ADDR"); ok {
l.IsEnabled = ok
l.ServerAddr = ldapServer
// Save root CAs
l.rootCAs = rootCAs
l.SkipTLSVerify = os.Getenv("MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY") == "true"
if v := os.Getenv("MINIO_IDENTITY_LDAP_STS_EXPIRY"); v != "" {
expDur, err := time.ParseDuration(v)
if err != nil {
return l, errors.New("LDAP expiry time err:" + err.Error())
}
if expDur <= 0 {
return l, errors.New("LDAP expiry time has to be positive")
}
l.STSExpiryDuration = v
l.stsExpiryDuration = expDur
} else {
l.stsExpiryDuration = defaultLDAPExpiry
}
if v := os.Getenv("MINIO_IDENTITY_LDAP_USERNAME_FORMAT"); v != "" {
subs := newSubstituter("username", "test")
if _, err := subs.substitute(v); err != nil {
return l, errors.New("Only username may be substituted in the username format")
}
l.UsernameFormat = v
}
grpSearchFilter := os.Getenv("MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER")
grpSearchNameAttr := os.Getenv("MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE")
grpSearchBaseDN := os.Getenv("MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN")
// Either all group params must be set or none must be set.
allNotSet := grpSearchFilter == "" && grpSearchNameAttr == "" && grpSearchBaseDN == ""
allSet := grpSearchFilter != "" && grpSearchNameAttr != "" && grpSearchBaseDN != ""
if !allNotSet && !allSet {
return l, errors.New("All group related parameters must be set")
}
if allSet {
subs := newSubstituter("username", "test", "usernamedn", "test2")
if _, err := subs.substitute(grpSearchFilter); err != nil {
return l, errors.New("Only username and usernamedn may be substituted in the group search filter string")
}
l.GroupSearchFilter = grpSearchFilter
l.GroupNameAttribute = grpSearchNameAttr
subs = newSubstituter("username", "test", "usernamedn", "test2")
if _, err := subs.substitute(grpSearchBaseDN); err != nil {
return l, errors.New("Only username and usernamedn may be substituted in the base DN string")
}
l.GroupSearchBaseDN = grpSearchBaseDN
}
}
return
}
// substituter - This type is to allow restricted runtime
// substitutions of variables in LDAP configuration items during
// runtime.
type substituter struct {
vals map[string]string
}
// newSubstituter - sets up the substituter for usage, for e.g.:
//
// subber := newSubstituter("username", "john")
func newSubstituter(v ...string) substituter {
if len(v)%2 != 0 {
log.Fatal("Need an even number of arguments")
}
vals := make(map[string]string)
for i := 0; i < len(v); i += 2 {
vals[v[i]] = v[i+1]
}
return substituter{vals: vals}
}
// substitute - performs substitution on the given string `t`. Returns
// an error if there are any variables in the input that do not have
// values in the substituter. E.g.:
//
// subber.substitute("uid=${username},cn=users,dc=example,dc=com")
//
// returns "uid=john,cn=users,dc=example,dc=com"
//
// whereas:
//
// subber.substitute("uid=${usernamedn}")
//
// returns an error.
func (s *substituter) substitute(t string) (string, error) {
for k, v := range s.vals {
re := regexp.MustCompile(fmt.Sprintf(`\$\{%s\}`, k))
t = re.ReplaceAllLiteralString(t, v)
}
// Check if all requested substitutions have been made.
re := regexp.MustCompile(`\$\{.*\}`)
if re.MatchString(t) {
return "", errors.New("unsupported substitution requested")
}
return t, nil
}

@ -25,6 +25,7 @@ import (
c "github.com/minio/mc/pkg/console" c "github.com/minio/mc/pkg/console"
"github.com/minio/minio/cmd/logger/message/log" "github.com/minio/minio/cmd/logger/message/log"
"github.com/minio/minio/pkg/color"
) )
// Console interface describes the methods that need to be implemented to satisfy the interface requirements. // Console interface describes the methods that need to be implemented to satisfy the interface requirements.
@ -89,8 +90,8 @@ func (f fatalMsg) quiet(msg string, args ...interface{}) {
var ( var (
logTag = "ERROR" logTag = "ERROR"
logBanner = ColorBgRed(ColorFgWhite(ColorBold(logTag))) + " " logBanner = color.BgRed(color.FgWhite(color.Bold(logTag))) + " "
emptyBanner = ColorBgRed(strings.Repeat(" ", len(logTag))) + " " emptyBanner = color.BgRed(strings.Repeat(" ", len(logTag))) + " "
bannerWidth = len(logTag) + 1 bannerWidth = len(logTag) + 1
) )

@ -139,9 +139,9 @@ func IsQuiet() bool {
return quietFlag return quietFlag
} }
// RegisterUIError registers the specified rendering function. This latter // RegisterError registers the specified rendering function. This latter
// will be called for a pretty rendering of fatal errors. // will be called for a pretty rendering of fatal errors.
func RegisterUIError(f func(string, error, bool) string) { func RegisterError(f func(string, error, bool) string) {
errorFmtFunc = f errorFmtFunc = f
} }

@ -24,6 +24,7 @@ import (
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/cmd/logger/message/log" "github.com/minio/minio/cmd/logger/message/log"
"github.com/minio/minio/pkg/color"
) )
// Target implements loggerTarget to send log // Target implements loggerTarget to send log
@ -103,7 +104,7 @@ func (c *Target) Send(e interface{}) error {
tagString = "\n " + tagString tagString = "\n " + tagString
} }
var msg = logger.ColorFgRed(logger.ColorBold(entry.Trace.Message)) var msg = color.FgRed(color.Bold(entry.Trace.Message))
var output = fmt.Sprintf("\n%s\n%s%s%s%s%s%s\nError: %s%s\n%s", var output = fmt.Sprintf("\n%s\n%s%s%s%s%s%s\nError: %s%s\n%s",
apiString, timeString, deploymentID, requestID, remoteHost, host, userAgent, apiString, timeString, deploymentID, requestID, remoteHost, host, userAgent,
msg, tagString, strings.Join(trace, "\n")) msg, tagString, strings.Join(trace, "\n"))

@ -18,45 +18,9 @@ package logger
import ( import (
"fmt" "fmt"
"os"
"regexp" "regexp"
"github.com/fatih/color" "github.com/minio/minio/pkg/color"
isatty "github.com/mattn/go-isatty"
)
// Global colors.
var (
// Check if we stderr, stdout are dumb terminals, we do not apply
// ansi coloring on dumb terminals.
isTerminal = func() bool {
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())
}
ColorBold = func() func(a ...interface{}) string {
if isTerminal() {
return color.New(color.Bold).SprintFunc()
}
return fmt.Sprint
}()
ColorFgRed = func() func(a ...interface{}) string {
if isTerminal() {
return color.New(color.FgRed).SprintFunc()
}
return fmt.Sprint
}()
ColorBgRed = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.BgRed).SprintfFunc()
}
return fmt.Sprintf
}()
ColorFgWhite = func() func(format string, a ...interface{}) string {
if isTerminal() {
return color.New(color.FgWhite).SprintfFunc()
}
return fmt.Sprintf
}()
) )
var ansiRE = regexp.MustCompile("(\x1b[^m]*m)") var ansiRE = regexp.MustCompile("(\x1b[^m]*m)")
@ -68,19 +32,19 @@ func ansiEscape(format string, args ...interface{}) {
} }
func ansiMoveRight(n int) { func ansiMoveRight(n int) {
if isTerminal() { if color.IsTerminal() {
ansiEscape("[%dC", n) ansiEscape("[%dC", n)
} }
} }
func ansiSaveAttributes() { func ansiSaveAttributes() {
if isTerminal() { if color.IsTerminal() {
ansiEscape("7") ansiEscape("7")
} }
} }
func ansiRestoreAttributes() { func ansiRestoreAttributes() {
if isTerminal() { if color.IsTerminal() {
ansiEscape("8") ansiEscape("8")
} }

@ -27,6 +27,7 @@ import (
"syscall" "syscall"
"github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio-go/v6/pkg/set"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
) )
@ -334,7 +335,7 @@ func sameLocalAddrs(addr1, addr2 string) (bool, error) {
func CheckLocalServerAddr(serverAddr string) error { func CheckLocalServerAddr(serverAddr string) error {
host, port, err := net.SplitHostPort(serverAddr) host, port, err := net.SplitHostPort(serverAddr)
if err != nil { if err != nil {
return uiErrInvalidAddressFlag(err) return config.ErrInvalidAddressFlag(err)
} }
// Strip off IPv6 zone information. // Strip off IPv6 zone information.
@ -345,9 +346,9 @@ func CheckLocalServerAddr(serverAddr string) error {
// Check whether port is a valid port number. // Check whether port is a valid port number.
p, err := strconv.Atoi(port) p, err := strconv.Atoi(port)
if err != nil { if err != nil {
return uiErrInvalidAddressFlag(err).Msg("invalid port number") return config.ErrInvalidAddressFlag(err).Msg("invalid port number")
} else if p < 1 || p > 65535 { } else if p < 1 || p > 65535 {
return uiErrInvalidAddressFlag(nil).Msg("port number must be between 1 to 65535") return config.ErrInvalidAddressFlag(nil).Msg("port number must be between 1 to 65535")
} }
// 0.0.0.0 is a wildcard address and refers to local network // 0.0.0.0 is a wildcard address and refers to local network
@ -359,7 +360,7 @@ func CheckLocalServerAddr(serverAddr string) error {
return err return err
} }
if !isLocalHost { if !isLocalHost {
return uiErrInvalidAddressFlag(nil).Msg("host in server address should be this server") return config.ErrInvalidAddressFlag(nil).Msg("host in server address should be this server")
} }
} }

@ -28,14 +28,17 @@ import (
"github.com/minio/cli" "github.com/minio/cli"
"github.com/minio/dsync/v2" "github.com/minio/dsync/v2"
"github.com/minio/minio/cmd/config"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/certs" "github.com/minio/minio/pkg/certs"
"github.com/minio/minio/pkg/color"
"github.com/minio/minio/pkg/env"
) )
func init() { func init() {
logger.Init(GOPATH, GOROOT) logger.Init(GOPATH, GOROOT)
logger.RegisterUIError(fmtError) logger.RegisterError(config.FmtError)
gob.Register(VerifyFileError("")) gob.Register(VerifyFileError(""))
gob.Register(DeleteFileError("")) gob.Register(DeleteFileError(""))
} }
@ -133,7 +136,7 @@ EXAMPLES:
// Checks if endpoints are either available through environment // Checks if endpoints are either available through environment
// or command line, returns false if both fails. // or command line, returns false if both fails.
func endpointsPresent(ctx *cli.Context) bool { func endpointsPresent(ctx *cli.Context) bool {
_, ok := os.LookupEnv("MINIO_ENDPOINTS") _, ok := env.Lookup("MINIO_ENDPOINTS")
if !ok { if !ok {
ok = ctx.Args().Present() ok = ctx.Args().Present()
} }
@ -150,12 +153,12 @@ func serverHandleCmdArgs(ctx *cli.Context) {
var err error var err error
if len(ctx.Args()) > serverCommandLineArgsMax { if len(ctx.Args()) > serverCommandLineArgsMax {
uErr := uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("Invalid total number of endpoints (%d) passed, supported upto 32 unique arguments", uErr := config.ErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("Invalid total number of endpoints (%d) passed, supported upto 32 unique arguments",
len(ctx.Args()))) len(ctx.Args())))
logger.FatalIf(uErr, "Unable to validate passed endpoints") logger.FatalIf(uErr, "Unable to validate passed endpoints")
} }
endpoints := strings.Fields(os.Getenv("MINIO_ENDPOINTS")) endpoints := strings.Fields(env.Get("MINIO_ENDPOINTS", ""))
if len(endpoints) > 0 { if len(endpoints) > 0 {
globalMinioAddr, globalEndpoints, setupType, globalXLSetCount, globalXLSetDriveCount, err = createServerEndpoints(globalCLIContext.Addr, endpoints...) globalMinioAddr, globalEndpoints, setupType, globalXLSetCount, globalXLSetDriveCount, err = createServerEndpoints(globalCLIContext.Addr, endpoints...)
} else { } else {
@ -184,7 +187,7 @@ func serverHandleEnvVars() {
// Handle common environment variables. // Handle common environment variables.
handleCommonEnvVars() handleCommonEnvVars()
if serverRegion := os.Getenv("MINIO_REGION"); serverRegion != "" { if serverRegion := env.Get("MINIO_REGION", ""); serverRegion != "" {
// region Envs are set globally. // region Envs are set globally.
globalIsEnvRegion = true globalIsEnvRegion = true
globalServerRegion = serverRegion globalServerRegion = serverRegion
@ -222,10 +225,10 @@ func serverMain(ctx *cli.Context) {
// Is distributed setup, error out if no certificates are found for HTTPS endpoints. // Is distributed setup, error out if no certificates are found for HTTPS endpoints.
if globalIsDistXL { if globalIsDistXL {
if globalEndpoints.IsHTTPS() && !globalIsSSL { if globalEndpoints.IsHTTPS() && !globalIsSSL {
logger.Fatal(uiErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server") logger.Fatal(config.ErrNoCertsAndHTTPSEndpoints(nil), "Unable to start the server")
} }
if !globalEndpoints.IsHTTPS() && globalIsSSL { if !globalEndpoints.IsHTTPS() && globalIsSSL {
logger.Fatal(uiErrCertsAndHTTPEndpoints(nil), "Unable to start the server") logger.Fatal(config.ErrCertsAndHTTPEndpoints(nil), "Unable to start the server")
} }
} }
@ -235,7 +238,7 @@ func serverMain(ctx *cli.Context) {
} }
if globalIsDiskCacheEnabled { if globalIsDiskCacheEnabled {
logger.StartupMessage(colorRed(colorBold("Disk caching is allowed only for gateway deployments"))) logger.StartupMessage(color.Red(color.Bold("Disk caching is allowed only for gateway deployments")))
} }
// FIXME: This code should be removed in future releases and we should have mandatory // FIXME: This code should be removed in future releases and we should have mandatory
@ -245,14 +248,14 @@ func serverMain(ctx *cli.Context) {
// Check for backward compatibility and newer style. // Check for backward compatibility and newer style.
if !globalIsEnvCreds && globalIsDistXL { if !globalIsEnvCreds && globalIsDistXL {
// Try to load old config file if any, for backward compatibility. // Try to load old config file if any, for backward compatibility.
var config = &serverConfig{} var cfg = &serverConfig{}
if _, err = Load(getConfigFile(), config); err == nil { if _, err = Load(getConfigFile(), cfg); err == nil {
globalActiveCred = config.Credential globalActiveCred = cfg.Credential
} }
if os.IsNotExist(err) { if os.IsNotExist(err) {
if _, err = Load(getConfigFile()+".deprecated", config); err == nil { if _, err = Load(getConfigFile()+".deprecated", cfg); err == nil {
globalActiveCred = config.Credential globalActiveCred = cfg.Credential
} }
} }
@ -262,7 +265,8 @@ func serverMain(ctx *cli.Context) {
logger.Info(`Supplying credentials from your 'config.json' is **DEPRECATED**, Access key and Secret key in distributed server mode is expected to be specified with environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY. This approach will become mandatory in future releases, please migrate to this approach soon.`) logger.Info(`Supplying credentials from your 'config.json' is **DEPRECATED**, Access key and Secret key in distributed server mode is expected to be specified with environment variables MINIO_ACCESS_KEY and MINIO_SECRET_KEY. This approach will become mandatory in future releases, please migrate to this approach soon.`)
} else { } else {
// Credential is not available anywhere by both means, we cannot start distributed setup anymore, fail eagerly. // Credential is not available anywhere by both means, we cannot start distributed setup anymore, fail eagerly.
logger.Fatal(uiErrEnvCredentialsMissingDistributed(nil), "Unable to initialize the server in distributed mode") logger.Fatal(config.ErrEnvCredentialsMissingDistributed(nil),
"Unable to initialize the server in distributed mode")
} }
} }
} }
@ -293,7 +297,7 @@ func serverMain(ctx *cli.Context) {
var handler http.Handler var handler http.Handler
handler, err = configureServerHandler(globalEndpoints) handler, err = configureServerHandler(globalEndpoints)
if err != nil { if err != nil {
logger.Fatal(uiErrUnexpectedError(err), "Unable to configure one of server's RPC services") logger.Fatal(config.ErrUnexpectedError(err), "Unable to configure one of server's RPC services")
} }
var getCert certs.GetCertificateFunc var getCert certs.GetCertificateFunc

@ -25,6 +25,7 @@ import (
"strings" "strings"
humanize "github.com/dustin/go-humanize" humanize "github.com/dustin/go-humanize"
color "github.com/minio/minio/pkg/color"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
) )
@ -121,18 +122,18 @@ func printServerCommonMsg(apiEndpoints []string) {
apiEndpointStr := strings.Join(apiEndpoints, " ") apiEndpointStr := strings.Join(apiEndpoints, " ")
// Colorize the message and print. // Colorize the message and print.
logStartupMessage(colorBlue("Endpoint: ") + colorBold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr))) logStartupMessage(color.Blue("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
if isTerminal() && !globalCLIContext.Anonymous { if color.IsTerminal() && !globalCLIContext.Anonymous {
logStartupMessage(colorBlue("AccessKey: ") + colorBold(fmt.Sprintf("%s ", cred.AccessKey))) logStartupMessage(color.Blue("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
logStartupMessage(colorBlue("SecretKey: ") + colorBold(fmt.Sprintf("%s ", cred.SecretKey))) logStartupMessage(color.Blue("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
if region != "" { if region != "" {
logStartupMessage(colorBlue("Region: ") + colorBold(fmt.Sprintf(getFormatStr(len(region), 3), region))) logStartupMessage(color.Blue("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
} }
} }
printEventNotifiers() printEventNotifiers()
if globalIsBrowserEnabled { if globalIsBrowserEnabled {
logStartupMessage(colorBlue("\nBrowser Access:")) logStartupMessage(color.Blue("\nBrowser Access:"))
logStartupMessage(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr)) logStartupMessage(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr))
} }
} }
@ -144,9 +145,9 @@ func printEventNotifiers() {
return return
} }
arnMsg := colorBlue("SQS ARNs: ") arnMsg := color.Blue("SQS ARNs: ")
for _, arn := range arns { for _, arn := range arns {
arnMsg += colorBold(fmt.Sprintf(getFormatStr(len(arn), 1), arn)) arnMsg += color.Bold(fmt.Sprintf(getFormatStr(len(arn), 1), arn))
} }
logStartupMessage(arnMsg) logStartupMessage(arnMsg)
@ -159,13 +160,15 @@ func printCLIAccessMsg(endPoint string, alias string) {
cred := globalServerConfig.GetCredential() cred := globalServerConfig.GetCredential()
// Configure 'mc', following block prints platform specific information for minio client. // Configure 'mc', following block prints platform specific information for minio client.
if isTerminal() { if color.IsTerminal() {
logStartupMessage(colorBlue("\nCommand-line Access: ") + mcQuickStartGuide) logStartupMessage(color.Blue("\nCommand-line Access: ") + mcQuickStartGuide)
if runtime.GOOS == globalWindowsOSName { if runtime.GOOS == globalWindowsOSName {
mcMessage := fmt.Sprintf("$ mc.exe config host add %s %s %s %s", alias, endPoint, cred.AccessKey, cred.SecretKey) mcMessage := fmt.Sprintf("$ mc.exe config host add %s %s %s %s", alias,
endPoint, cred.AccessKey, cred.SecretKey)
logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage)) logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
} else { } else {
mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s", alias, endPoint, cred.AccessKey, cred.SecretKey) mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s", alias,
endPoint, cred.AccessKey, cred.SecretKey)
logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage)) logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
} }
} }
@ -173,12 +176,12 @@ func printCLIAccessMsg(endPoint string, alias string) {
// Prints startup message for Object API acces, prints link to our SDK documentation. // Prints startup message for Object API acces, prints link to our SDK documentation.
func printObjectAPIMsg() { func printObjectAPIMsg() {
logStartupMessage(colorBlue("\nObject API (Amazon S3 compatible):")) logStartupMessage(color.Blue("\nObject API (Amazon S3 compatible):"))
logStartupMessage(colorBlue(" Go: ") + fmt.Sprintf(getFormatStr(len(goQuickStartGuide), 8), goQuickStartGuide)) logStartupMessage(color.Blue(" Go: ") + fmt.Sprintf(getFormatStr(len(goQuickStartGuide), 8), goQuickStartGuide))
logStartupMessage(colorBlue(" Java: ") + fmt.Sprintf(getFormatStr(len(javaQuickStartGuide), 6), javaQuickStartGuide)) logStartupMessage(color.Blue(" Java: ") + fmt.Sprintf(getFormatStr(len(javaQuickStartGuide), 6), javaQuickStartGuide))
logStartupMessage(colorBlue(" Python: ") + fmt.Sprintf(getFormatStr(len(pyQuickStartGuide), 4), pyQuickStartGuide)) logStartupMessage(color.Blue(" Python: ") + fmt.Sprintf(getFormatStr(len(pyQuickStartGuide), 4), pyQuickStartGuide))
logStartupMessage(colorBlue(" JavaScript: ") + jsQuickStartGuide) logStartupMessage(color.Blue(" JavaScript: ") + jsQuickStartGuide)
logStartupMessage(colorBlue(" .NET: ") + fmt.Sprintf(getFormatStr(len(dotnetQuickStartGuide), 6), dotnetQuickStartGuide)) logStartupMessage(color.Blue(" .NET: ") + fmt.Sprintf(getFormatStr(len(dotnetQuickStartGuide), 6), dotnetQuickStartGuide))
} }
// Get formatted disk/storage info message. // Get formatted disk/storage info message.
@ -186,7 +189,7 @@ func getStorageInfoMsg(storageInfo StorageInfo) string {
var msg string var msg string
if storageInfo.Backend.Type == BackendErasure { if storageInfo.Backend.Type == BackendErasure {
diskInfo := fmt.Sprintf(" %d Online, %d Offline. ", storageInfo.Backend.OnlineDisks, storageInfo.Backend.OfflineDisks) diskInfo := fmt.Sprintf(" %d Online, %d Offline. ", storageInfo.Backend.OnlineDisks, storageInfo.Backend.OfflineDisks)
msg += colorBlue("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo) msg += color.Blue("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
} }
return msg return msg
} }
@ -199,7 +202,7 @@ func printStorageInfo(storageInfo StorageInfo) {
} }
func printCacheStorageInfo(storageInfo CacheStorageInfo) { func printCacheStorageInfo(storageInfo CacheStorageInfo) {
msg := fmt.Sprintf("%s %s Free, %s Total", colorBlue("Cache Capacity:"), msg := fmt.Sprintf("%s %s Free, %s Total", color.Blue("Cache Capacity:"),
humanize.IBytes(uint64(storageInfo.Free)), humanize.IBytes(uint64(storageInfo.Free)),
humanize.IBytes(uint64(storageInfo.Total))) humanize.IBytes(uint64(storageInfo.Total)))
logStartupMessage(msg) logStartupMessage(msg)
@ -207,14 +210,14 @@ func printCacheStorageInfo(storageInfo CacheStorageInfo) {
// Prints certificate expiry date warning // Prints certificate expiry date warning
func getCertificateChainMsg(certs []*x509.Certificate) string { func getCertificateChainMsg(certs []*x509.Certificate) string {
msg := colorBlue("\nCertificate expiry info:\n") msg := color.Blue("\nCertificate expiry info:\n")
totalCerts := len(certs) totalCerts := len(certs)
var expiringCerts int var expiringCerts int
for i := totalCerts - 1; i >= 0; i-- { for i := totalCerts - 1; i >= 0; i-- {
cert := certs[i] cert := certs[i]
if cert.NotAfter.Before(UTCNow().Add(globalMinioCertExpireWarnDays)) { if cert.NotAfter.Before(UTCNow().Add(globalMinioCertExpireWarnDays)) {
expiringCerts++ expiringCerts++
msg += fmt.Sprintf(colorBold("#%d %s will expire on %s\n"), expiringCerts, cert.Subject.CommonName, cert.NotAfter) msg += fmt.Sprintf(color.Bold("#%d %s will expire on %s\n"), expiringCerts, cert.Subject.CommonName, cert.NotAfter)
} }
} }
if expiringCerts > 0 { if expiringCerts > 0 {

@ -25,6 +25,8 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/minio/minio/pkg/color"
) )
// Tests if we generate storage info. // Tests if we generate storage info.
@ -53,8 +55,8 @@ func TestCertificateExpiryInfo(t *testing.T) {
}, },
} }
expectedMsg := colorBlue("\nCertificate expiry info:\n") + expectedMsg := color.Blue("\nCertificate expiry info:\n") +
colorBold(fmt.Sprintf("#1 Test cert will expire on %s\n", expiredDate)) color.Bold(fmt.Sprintf("#1 Test cert will expire on %s\n", expiredDate))
// When // When
msg := getCertificateChainMsg(fakeCerts) msg := getCertificateChainMsg(fakeCerts)

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

@ -29,6 +29,7 @@ import (
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/minio/minio/cmd/config"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
) )
@ -573,7 +574,8 @@ func registerStorageRESTHandlers(router *mux.Router, endpoints EndpointList) {
} }
storage, err := newPosix(endpoint.Path) storage, err := newPosix(endpoint.Path)
if err != nil { if err != nil {
logger.Fatal(uiErrUnableToWriteInBackend(err), "Unable to initialize posix backend") logger.Fatal(config.ErrUnableToWriteInBackend(err),
"Unable to initialize posix backend")
} }
server := &storageRESTServer{storage, mustGetUUID()} server := &storageRESTServer{storage, mustGetUUID()}

@ -24,6 +24,7 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
xldap "github.com/minio/minio/cmd/config/ldap"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
@ -263,12 +264,12 @@ func (sts *stsAPIHandlers) AssumeRoleWithJWT(w http.ResponseWriter, r *http.Requ
ctx = newContext(r, w, action) ctx = newContext(r, w, action)
defer logger.AuditLog(w, r, action, nil) defer logger.AuditLog(w, r, action, nil)
if globalIAMValidators == nil { if globalOpenIDValidators == nil {
writeSTSErrorResponse(ctx, w, ErrSTSNotInitialized, errServerNotInitialized) writeSTSErrorResponse(ctx, w, ErrSTSNotInitialized, errServerNotInitialized)
return return
} }
v, err := globalIAMValidators.Get("jwt") v, err := globalOpenIDValidators.Get("jwt")
if err != nil { if err != nil {
writeSTSErrorResponse(ctx, w, ErrSTSInvalidParameterValue, err) writeSTSErrorResponse(ctx, w, ErrSTSInvalidParameterValue, err)
return return
@ -471,10 +472,10 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
return return
} }
usernameSubs := newSubstituter("username", ldapUsername) usernameSubs, _ := xldap.NewSubstituter("username", ldapUsername)
// We ignore error below as we already validated the username // We ignore error below as we already validated the username
// format string at startup. // format string at startup.
usernameDN, _ := usernameSubs.substitute(globalServerConfig.LDAPServerConfig.UsernameFormat) usernameDN, _ := usernameSubs.Substitute(globalServerConfig.LDAPServerConfig.UsernameFormat)
// Bind with user credentials to validate the password // Bind with user credentials to validate the password
err = ldapConn.Bind(usernameDN, ldapPassword) err = ldapConn.Bind(usernameDN, ldapPassword)
if err != nil { if err != nil {
@ -486,14 +487,14 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
if globalServerConfig.LDAPServerConfig.GroupSearchFilter != "" { if globalServerConfig.LDAPServerConfig.GroupSearchFilter != "" {
// Verified user credentials. Now we find the groups they are // Verified user credentials. Now we find the groups they are
// a member of. // a member of.
searchSubs := newSubstituter( searchSubs, _ := xldap.NewSubstituter(
"username", ldapUsername, "username", ldapUsername,
"usernamedn", usernameDN, "usernamedn", usernameDN,
) )
// We ignore error below as we already validated the search string // We ignore error below as we already validated the search string
// at startup. // at startup.
groupSearchFilter, _ := searchSubs.substitute(globalServerConfig.LDAPServerConfig.GroupSearchFilter) groupSearchFilter, _ := searchSubs.Substitute(globalServerConfig.LDAPServerConfig.GroupSearchFilter)
baseDN, _ := searchSubs.substitute(globalServerConfig.LDAPServerConfig.GroupSearchBaseDN) baseDN, _ := searchSubs.Substitute(globalServerConfig.LDAPServerConfig.GroupSearchBaseDN)
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
baseDN, baseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
@ -513,7 +514,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
groups = append(groups, entry.Attributes[0].Values...) groups = append(groups, entry.Attributes[0].Values...)
} }
} }
expiryDur := globalServerConfig.LDAPServerConfig.stsExpiryDuration expiryDur := globalServerConfig.LDAPServerConfig.GetExpiryDuration()
m := map[string]interface{}{ m := map[string]interface{}{
"exp": UTCNow().Add(expiryDur).Unix(), "exp": UTCNow().Add(expiryDur).Unix(),
"ldapUser": ldapUsername, "ldapUser": ldapUsername,

@ -25,6 +25,7 @@ import (
"github.com/cheggaaa/pb" "github.com/cheggaaa/pb"
humanize "github.com/dustin/go-humanize" humanize "github.com/dustin/go-humanize"
"github.com/minio/minio/pkg/color"
) )
// prepareUpdateMessage - prepares the update message, only if a // prepareUpdateMessage - prepares the update message, only if a
@ -54,8 +55,8 @@ func colorizeUpdateMessage(updateString string, newerThan string) string {
line2Length := len(fmt.Sprintf(msgLine2Fmt, updateString)) line2Length := len(fmt.Sprintf(msgLine2Fmt, updateString))
// Populate lines with color coding. // Populate lines with color coding.
line1InColor := fmt.Sprintf(msgLine1Fmt, colorYellowBold(newerThan)) line1InColor := fmt.Sprintf(msgLine1Fmt, color.YellowBold(newerThan))
line2InColor := fmt.Sprintf(msgLine2Fmt, colorCyanBold(updateString)) line2InColor := fmt.Sprintf(msgLine2Fmt, color.CyanBold(updateString))
// calculate the rectangular box size. // calculate the rectangular box size.
maxContentWidth := int(math.Max(float64(line1Length), float64(line2Length))) maxContentWidth := int(math.Max(float64(line1Length), float64(line2Length)))
@ -89,10 +90,10 @@ func colorizeUpdateMessage(updateString string, newerThan string) string {
} }
lines := []string{ lines := []string{
colorYellowBold(topLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + topRightChar), color.YellowBold(topLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + topRightChar),
vertBarChar + line1InColor + strings.Repeat(" ", maxContentWidth-line1Length) + vertBarChar, vertBarChar + line1InColor + strings.Repeat(" ", maxContentWidth-line1Length) + vertBarChar,
vertBarChar + line2InColor + strings.Repeat(" ", maxContentWidth-line2Length) + vertBarChar, vertBarChar + line2InColor + strings.Repeat(" ", maxContentWidth-line2Length) + vertBarChar,
colorYellowBold(bottomLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + bottomRightChar), color.YellowBold(bottomLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + bottomRightChar),
} }
return "\n" + strings.Join(lines, "\n") + "\n" return "\n" + strings.Join(lines, "\n") + "\n"
} }

@ -21,6 +21,8 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/minio/minio/pkg/color"
) )
// Tests update notifier string builder. // Tests update notifier string builder.
@ -68,8 +70,8 @@ func TestPrepareUpdateMessage(t *testing.T) {
for i, testCase := range testCases { for i, testCase := range testCases {
output := prepareUpdateMessage(testCase.dlURL, testCase.older) output := prepareUpdateMessage(testCase.dlURL, testCase.older)
line1 := fmt.Sprintf("%s %s", plainMsg, colorYellowBold(testCase.expectedSubStr)) line1 := fmt.Sprintf("%s %s", plainMsg, color.YellowBold(testCase.expectedSubStr))
line2 := fmt.Sprintf("Update: %s", colorCyanBold(testCase.dlURL)) line2 := fmt.Sprintf("Update: %s", color.CyanBold(testCase.dlURL))
// Uncomment below to see message appearance: // Uncomment below to see message appearance:
// fmt.Println(output) // fmt.Println(output)
switch { switch {

@ -35,6 +35,7 @@ import (
"github.com/inconshreveable/go-update" "github.com/inconshreveable/go-update"
xhttp "github.com/minio/minio/cmd/http" xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/env"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
_ "github.com/minio/sha256-simd" // Needed for sha256 hash verifier. _ "github.com/minio/sha256-simd" // Needed for sha256 hash verifier.
) )
@ -144,7 +145,7 @@ func IsDocker() bool {
func IsDCOS() bool { func IsDCOS() bool {
// http://mesos.apache.org/documentation/latest/docker-containerizer/ // http://mesos.apache.org/documentation/latest/docker-containerizer/
// Mesos docker containerizer sets this value // Mesos docker containerizer sets this value
return os.Getenv("MESOS_CONTAINER_NAME") != "" return env.Get("MESOS_CONTAINER_NAME", "") != ""
} }
// IsKubernetes returns true if minio is running in kubernetes. // IsKubernetes returns true if minio is running in kubernetes.
@ -153,7 +154,7 @@ func IsKubernetes() bool {
// indeed running inside a kubernetes pod // indeed running inside a kubernetes pod
// is KUBERNETES_SERVICE_HOST but in future // is KUBERNETES_SERVICE_HOST but in future
// we might need to enhance this. // we might need to enhance this.
return os.Getenv("KUBERNETES_SERVICE_HOST") != "" return env.Get("KUBERNETES_SERVICE_HOST", "") != ""
} }
// IsBOSH returns true if minio is deployed from a bosh package // IsBOSH returns true if minio is deployed from a bosh package
@ -247,7 +248,7 @@ func getUserAgent(mode string) string {
uaAppend(" MinIO/", ReleaseTag) uaAppend(" MinIO/", ReleaseTag)
uaAppend(" MinIO/", CommitID) uaAppend(" MinIO/", CommitID)
if IsDCOS() { if IsDCOS() {
universePkgVersion := os.Getenv("MARATHON_APP_LABEL_DCOS_PACKAGE_VERSION") universePkgVersion := env.Get("MARATHON_APP_LABEL_DCOS_PACKAGE_VERSION", "")
// On DC/OS environment try to the get universe package version. // On DC/OS environment try to the get universe package version.
if universePkgVersion != "" { if universePkgVersion != "" {
uaAppend(" MinIO/universe-", universePkgVersion) uaAppend(" MinIO/universe-", universePkgVersion)
@ -262,7 +263,7 @@ func getUserAgent(mode string) string {
} }
} }
pcfTileVersion := os.Getenv("MINIO_PCF_TILE_VERSION") pcfTileVersion := env.Get("MINIO_PCF_TILE_VERSION", "")
if pcfTileVersion != "" { if pcfTileVersion != "" {
uaAppend(" MinIO/pcf-tile-", pcfTileVersion) uaAppend(" MinIO/pcf-tile-", pcfTileVersion)
} }

@ -0,0 +1,85 @@
package color
import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/mattn/go-isatty"
)
// global colors.
var (
// Check if we stderr, stdout are dumb terminals, we do not apply
// ansi coloring on dumb terminals.
IsTerminal = func() bool {
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())
}
Bold = func() func(a ...interface{}) string {
if IsTerminal() {
return color.New(color.Bold).SprintFunc()
}
return fmt.Sprint
}()
Red = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgRed).SprintfFunc()
}
return fmt.Sprintf
}()
Blue = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgBlue).SprintfFunc()
}
return fmt.Sprintf
}()
Yellow = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgYellow).SprintfFunc()
}
return fmt.Sprintf
}()
CyanBold = func() func(a ...interface{}) string {
if IsTerminal() {
color.New(color.FgCyan, color.Bold).SprintFunc()
}
return fmt.Sprint
}()
YellowBold = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgYellow, color.Bold).SprintfFunc()
}
return fmt.Sprintf
}()
BgYellow = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.BgYellow).SprintfFunc()
}
return fmt.Sprintf
}()
Black = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgBlack).SprintfFunc()
}
return fmt.Sprintf
}()
FgRed = func() func(a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgRed).SprintFunc()
}
return fmt.Sprint
}()
BgRed = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.BgRed).SprintfFunc()
}
return fmt.Sprintf
}()
FgWhite = func() func(format string, a ...interface{}) string {
if IsTerminal() {
return color.New(color.FgWhite).SprintfFunc()
}
return fmt.Sprintf
}()
)

21
pkg/env/env.go vendored

@ -0,0 +1,21 @@
package env
import "os"
// Get retrieves the value of the environment variable named
// by the key. If the variable is present in the environment the
// value (which may be empty) is returned. Otherwise it returns
// the specified default value.
func Get(key, defaultValue string) string {
if v, ok := os.LookupEnv(key); ok {
return v
}
return defaultValue
}
// Lookup retrieves the value of the environment variable named
// by the key. If the variable is present in the environment the
// value (which may be empty) is returned and the boolean is true.
// Otherwise the returned value will be empty and the boolean will
// be false.
func Lookup(key string) (string, bool) { return os.LookupEnv(key) }
Loading…
Cancel
Save