2021-04-18 15:41:13 -04:00
// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2016-07-13 02:21:18 -04:00
2016-08-18 19:23:42 -04:00
package cmd
2016-07-13 02:21:18 -04:00
import (
"fmt"
2018-12-18 11:26:30 -05:00
"net"
2021-08-05 18:01:19 -04:00
"net/url"
2016-07-13 02:21:18 -04:00
"runtime"
"strings"
2016-10-05 15:48:07 -04:00
humanize "github.com/dustin/go-humanize"
2021-05-06 11:52:02 -04:00
"github.com/minio/madmin-go"
2021-06-01 17:59:40 -04:00
color "github.com/minio/minio/internal/color"
"github.com/minio/minio/internal/logger"
2021-06-14 17:54:37 -04:00
xnet "github.com/minio/pkg/net"
2016-07-13 02:21:18 -04:00
)
// generates format string depending on the string length and padding.
func getFormatStr ( strLen int , padding int ) string {
formatStr := fmt . Sprintf ( "%ds" , strLen + padding )
return "%" + formatStr
}
2020-05-28 16:03:04 -04:00
func mustGetStorageInfo ( objAPI ObjectLayer ) StorageInfo {
2021-01-04 12:42:09 -05:00
storageInfo , _ := objAPI . StorageInfo ( GlobalContext )
2020-05-28 16:03:04 -04:00
return storageInfo
}
2016-07-13 02:21:18 -04:00
// Prints the formatted startup message.
2020-10-09 12:59:52 -04:00
func printStartupMessage ( apiEndpoints [ ] string , err error ) {
if err != nil {
2022-03-03 16:21:16 -05:00
if globalConsoleSys != nil {
globalConsoleSys . Send ( fmt . Sprintf ( "Server startup failed with '%v', some features may be missing" , err ) , string ( logger . All ) )
}
2020-10-09 12:59:52 -04:00
}
2017-04-12 12:22:35 -04:00
2022-06-27 06:58:25 -04:00
if len ( globalSubnetConfig . APIKey ) == 0 && err == nil {
2022-06-28 19:37:40 -04:00
var builder strings . Builder
startupBanner ( & builder )
logger . Info ( "\n" + builder . String ( ) )
2022-06-27 06:58:25 -04:00
}
2021-06-21 02:04:47 -04:00
strippedAPIEndpoints := stripStandardPorts ( apiEndpoints , globalMinioHost )
2018-03-28 17:14:06 -04:00
// If cache layer is enabled, print cache capacity.
2019-11-09 12:27:23 -05:00
cachedObjAPI := newCachedObjectLayerFn ( )
if cachedObjAPI != nil {
2020-04-09 12:30:02 -04:00
printCacheStorageInfo ( cachedObjAPI . StorageInfo ( GlobalContext ) )
2018-03-28 17:14:06 -04:00
}
2019-11-09 12:27:23 -05:00
2017-12-22 06:28:13 -05:00
// Object layer is initialized then print StorageInfo.
2019-11-09 12:27:23 -05:00
objAPI := newObjectLayerFn ( )
2017-12-22 06:28:13 -05:00
if objAPI != nil {
2020-05-28 16:03:04 -04:00
printStorageInfo ( mustGetStorageInfo ( objAPI ) )
2017-12-22 06:28:13 -05:00
}
2017-01-10 19:43:48 -05:00
// Prints credential, region and browser access.
2017-05-31 12:21:28 -04:00
printServerCommonMsg ( strippedAPIEndpoints )
2017-01-10 19:43:48 -05:00
// Prints `mc` cli configuration message chooses
// first endpoint as default.
2017-06-09 22:50:51 -04:00
printCLIAccessMsg ( strippedAPIEndpoints [ 0 ] , "myminio" )
2017-01-10 19:43:48 -05:00
// Prints documentation message.
2016-07-13 02:21:18 -04:00
printObjectAPIMsg ( )
}
2021-08-05 18:01:19 -04:00
// Returns true if input is IPv6
func isIPv6 ( host string ) bool {
2018-12-18 11:26:30 -05:00
h , _ , err := net . SplitHostPort ( host )
if err != nil {
h = host
}
ip := net . ParseIP ( h )
2021-08-05 18:01:19 -04:00
return ip . To16 ( ) != nil && ip . To4 ( ) == nil
2018-12-18 11:26:30 -05:00
}
2017-05-31 12:21:28 -04:00
// strip api endpoints list with standard ports such as
// port "80" and "443" before displaying on the startup
// banner. Returns a new list of API endpoints.
2021-06-21 02:04:47 -04:00
func stripStandardPorts ( apiEndpoints [ ] string , host string ) ( newAPIEndpoints [ ] string ) {
2021-08-05 18:01:19 -04:00
if len ( apiEndpoints ) == 1 {
return apiEndpoints
2021-07-23 18:22:25 -04:00
}
2017-05-31 12:21:28 -04:00
newAPIEndpoints = make ( [ ] string , len ( apiEndpoints ) )
// Check all API endpoints for standard ports and strip them.
for i , apiEndpoint := range apiEndpoints {
2021-08-05 18:01:19 -04:00
_ , err := xnet . ParseHTTPURL ( apiEndpoint )
if err != nil {
continue
}
u , err := url . Parse ( apiEndpoint )
2017-05-31 12:21:28 -04:00
if err != nil {
continue
}
2021-08-05 18:01:19 -04:00
if host == "" && isIPv6 ( u . Hostname ( ) ) {
// Skip all IPv6 endpoints
2018-12-18 11:26:30 -05:00
continue
}
2021-08-05 18:01:19 -04:00
if u . Port ( ) == "80" && u . Scheme == "http" || u . Port ( ) == "443" && u . Scheme == "https" {
u . Host = u . Hostname ( )
}
2018-12-18 11:26:30 -05:00
newAPIEndpoints [ i ] = u . String ( )
2017-05-31 12:21:28 -04:00
}
return newAPIEndpoints
}
2016-07-13 02:21:18 -04:00
// Prints common server startup message. Prints credential, region and browser access.
2017-01-10 19:43:48 -05:00
func printServerCommonMsg ( apiEndpoints [ ] string ) {
2016-07-13 02:21:18 -04:00
// Get saved credentials.
2019-10-23 01:59:13 -04:00
cred := globalActiveCred
2016-07-13 02:21:18 -04:00
// Get saved region.
2021-11-25 16:06:25 -05:00
region := globalSite . Region
2016-07-13 02:21:18 -04:00
2017-01-10 19:43:48 -05:00
apiEndpointStr := strings . Join ( apiEndpoints , " " )
2017-05-31 12:21:28 -04:00
2016-07-13 02:21:18 -04:00
// Colorize the message and print.
2022-03-03 16:21:16 -05:00
logger . Info ( color . Blue ( "API: " ) + color . Bold ( fmt . Sprintf ( "%s " , apiEndpointStr ) ) )
if color . IsTerminal ( ) && ( ! globalCLIContext . Anonymous && ! globalCLIContext . JSON ) {
logger . Info ( color . Blue ( "RootUser: " ) + color . Bold ( fmt . Sprintf ( "%s " , cred . AccessKey ) ) )
logger . Info ( color . Blue ( "RootPass: " ) + color . Bold ( fmt . Sprintf ( "%s " , cred . SecretKey ) ) )
2018-10-16 16:19:12 -04:00
if region != "" {
2022-03-03 16:21:16 -05:00
logger . Info ( color . Blue ( "Region: " ) + color . Bold ( fmt . Sprintf ( getFormatStr ( len ( region ) , 2 ) , region ) ) )
2018-10-16 16:19:12 -04:00
}
2017-05-15 21:17:02 -04:00
}
2016-09-14 04:11:03 -04:00
printEventNotifiers ( )
2021-04-29 22:01:43 -04:00
2022-06-27 06:58:25 -04:00
if globalMinioConsolePortAuto && globalBrowserEnabled {
logger . Info ( color . RedBold ( "\nWARNING: Console endpoint is listening on a dynamic port (%s), please use --console-address \":PORT\" to choose a static port." ,
globalMinioConsolePort ) )
}
2021-04-29 22:01:43 -04:00
if globalBrowserEnabled {
2021-06-21 02:04:47 -04:00
consoleEndpointStr := strings . Join ( stripStandardPorts ( getConsoleEndpoints ( ) , globalMinioConsoleHost ) , " " )
2022-06-27 06:58:25 -04:00
logger . Info ( color . Blue ( "Console: " ) + color . Bold ( fmt . Sprintf ( "%s " , consoleEndpointStr ) ) )
2022-03-03 16:21:16 -05:00
if color . IsTerminal ( ) && ( ! globalCLIContext . Anonymous && ! globalCLIContext . JSON ) {
logger . Info ( color . Blue ( "RootUser: " ) + color . Bold ( fmt . Sprintf ( "%s " , cred . AccessKey ) ) )
logger . Info ( color . Blue ( "RootPass: " ) + color . Bold ( fmt . Sprintf ( "%s " , cred . SecretKey ) ) )
2021-06-17 23:27:04 -04:00
}
2021-04-29 22:01:43 -04:00
}
2016-09-13 14:00:40 -04:00
}
2021-06-17 23:27:04 -04:00
// Prints startup message for Object API acces, prints link to our SDK documentation.
func printObjectAPIMsg ( ) {
2022-03-03 16:21:16 -05:00
logger . Info ( color . Blue ( "\nDocumentation: " ) + "https://docs.min.io" )
2021-06-17 23:27:04 -04:00
}
2016-09-13 14:00:40 -04:00
// Prints bucket notification configurations.
func printEventNotifiers ( ) {
2019-11-13 20:38:05 -05:00
if globalNotificationSys == nil {
return
}
2020-04-21 12:38:32 -04:00
arns := globalNotificationSys . GetARNList ( true )
2018-03-15 16:03:41 -04:00
if len ( arns ) == 0 {
2017-05-15 21:17:02 -04:00
return
2016-09-13 14:00:40 -04:00
}
2018-03-15 16:03:41 -04:00
2019-10-04 13:35:33 -04:00
arnMsg := color . Blue ( "SQS ARNs: " )
2018-03-15 16:03:41 -04:00
for _ , arn := range arns {
2021-01-06 13:38:07 -05:00
arnMsg += color . Bold ( fmt . Sprintf ( "%s " , arn ) )
2016-09-10 05:23:28 -04:00
}
2018-03-15 16:03:41 -04:00
2022-03-03 16:21:16 -05:00
logger . Info ( arnMsg )
2016-07-13 02:21:18 -04:00
}
// Prints startup message for command line access. Prints link to our documentation
// and custom platform specific message.
2017-06-09 22:50:51 -04:00
func printCLIAccessMsg ( endPoint string , alias string ) {
2016-07-13 02:21:18 -04:00
// Get saved credentials.
2019-10-23 01:59:13 -04:00
cred := globalActiveCred
2016-07-13 02:21:18 -04:00
2021-06-17 23:27:04 -04:00
const mcQuickStartGuide = "https://docs.min.io/docs/minio-client-quickstart-guide"
2016-07-13 02:21:18 -04:00
// Configure 'mc', following block prints platform specific information for minio client.
2020-01-06 11:42:47 -05:00
if color . IsTerminal ( ) && ! globalCLIContext . Anonymous {
2022-03-03 16:21:16 -05:00
logger . Info ( color . Blue ( "\nCommand-line: " ) + mcQuickStartGuide )
2018-10-16 16:19:12 -04:00
if runtime . GOOS == globalWindowsOSName {
2020-08-17 20:39:55 -04:00
mcMessage := fmt . Sprintf ( "$ mc.exe alias set %s %s %s %s" , alias ,
2019-10-04 13:35:33 -04:00
endPoint , cred . AccessKey , cred . SecretKey )
2022-03-03 16:21:16 -05:00
logger . Info ( fmt . Sprintf ( getFormatStr ( len ( mcMessage ) , 3 ) , mcMessage ) )
2018-10-16 16:19:12 -04:00
} else {
2020-08-17 20:39:55 -04:00
mcMessage := fmt . Sprintf ( "$ mc alias set %s %s %s %s" , alias ,
2019-10-04 13:35:33 -04:00
endPoint , cred . AccessKey , cred . SecretKey )
2022-03-03 16:21:16 -05:00
logger . Info ( fmt . Sprintf ( getFormatStr ( len ( mcMessage ) , 3 ) , mcMessage ) )
2018-10-16 16:19:12 -04:00
}
2016-07-13 02:21:18 -04:00
}
}
2016-10-05 15:48:07 -04:00
// Get formatted disk/storage info message.
2016-10-10 02:03:10 -04:00
func getStorageInfoMsg ( storageInfo StorageInfo ) string {
2018-05-23 20:30:25 -04:00
var msg string
2020-03-17 18:25:00 -04:00
var mcMessage string
2020-12-21 12:35:19 -05:00
onlineDisks , offlineDisks := getOnlineOfflineDisksStats ( storageInfo . Disks )
2021-03-04 17:36:23 -05:00
if storageInfo . Backend . Type == madmin . Erasure {
2020-12-21 12:35:19 -05:00
if offlineDisks . Sum ( ) > 0 {
2020-04-12 21:08:27 -04:00
mcMessage = "Use `mc admin info` to look for latest server/disk info\n"
2020-03-17 18:25:00 -04:00
}
2020-03-21 13:02:20 -04:00
2020-12-21 12:35:19 -05:00
diskInfo := fmt . Sprintf ( " %d Online, %d Offline. " , onlineDisks . Sum ( ) , offlineDisks . Sum ( ) )
2019-10-04 13:35:33 -04:00
msg += color . Blue ( "Status:" ) + fmt . Sprintf ( getFormatStr ( len ( diskInfo ) , 8 ) , diskInfo )
2020-03-21 13:02:20 -04:00
if len ( mcMessage ) > 0 {
msg = fmt . Sprintf ( "%s %s" , mcMessage , msg )
}
2016-10-05 15:48:07 -04:00
}
return msg
}
// Prints startup message of storage capacity and erasure information.
2016-10-10 02:03:10 -04:00
func printStorageInfo ( storageInfo StorageInfo ) {
2018-05-23 20:30:25 -04:00
if msg := getStorageInfoMsg ( storageInfo ) ; msg != "" {
2022-03-03 16:21:16 -05:00
logger . Info ( msg )
2018-05-23 20:30:25 -04:00
}
2016-10-05 15:48:07 -04:00
}
2016-10-14 07:48:08 -04:00
2018-05-30 14:30:14 -04:00
func printCacheStorageInfo ( storageInfo CacheStorageInfo ) {
2019-10-04 13:35:33 -04:00
msg := fmt . Sprintf ( "%s %s Free, %s Total" , color . Blue ( "Cache Capacity:" ) ,
2020-08-24 15:11:20 -04:00
humanize . IBytes ( storageInfo . Free ) ,
humanize . IBytes ( storageInfo . Total ) )
2022-03-03 16:21:16 -05:00
logger . Info ( msg )
2018-03-28 17:14:06 -04:00
}