github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/server-startup-msg.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"fmt"
    22  	"net"
    23  	"net/url"
    24  	"strings"
    25  
    26  	"github.com/minio/madmin-go/v3"
    27  	"github.com/minio/minio/internal/color"
    28  	"github.com/minio/minio/internal/logger"
    29  	xnet "github.com/minio/pkg/v2/net"
    30  )
    31  
    32  // generates format string depending on the string length and padding.
    33  func getFormatStr(strLen int, padding int) string {
    34  	formatStr := fmt.Sprintf("%ds", strLen+padding)
    35  	return "%" + formatStr
    36  }
    37  
    38  // Prints the formatted startup message.
    39  func printStartupMessage(apiEndpoints []string, err error) {
    40  	logger.Info(color.Bold(MinioBannerName))
    41  	if err != nil {
    42  		if globalConsoleSys != nil {
    43  			globalConsoleSys.Send(GlobalContext, fmt.Sprintf("Server startup failed with '%v', some features may be missing", err))
    44  		}
    45  	}
    46  
    47  	if !globalSubnetConfig.Registered() {
    48  		var builder strings.Builder
    49  		startupBanner(&builder)
    50  		logger.Info(builder.String())
    51  	}
    52  
    53  	strippedAPIEndpoints := stripStandardPorts(apiEndpoints, globalMinioHost)
    54  
    55  	// Prints credential, region and browser access.
    56  	printServerCommonMsg(strippedAPIEndpoints)
    57  
    58  	// Prints `mc` cli configuration message chooses
    59  	// first endpoint as default.
    60  	printCLIAccessMsg(strippedAPIEndpoints[0], "myminio")
    61  
    62  	// Prints documentation message.
    63  	printObjectAPIMsg()
    64  }
    65  
    66  // Returns true if input is IPv6
    67  func isIPv6(host string) bool {
    68  	h, _, err := net.SplitHostPort(host)
    69  	if err != nil {
    70  		h = host
    71  	}
    72  	ip := net.ParseIP(h)
    73  	return ip.To16() != nil && ip.To4() == nil
    74  }
    75  
    76  // strip api endpoints list with standard ports such as
    77  // port "80" and "443" before displaying on the startup
    78  // banner.  Returns a new list of API endpoints.
    79  func stripStandardPorts(apiEndpoints []string, host string) (newAPIEndpoints []string) {
    80  	if len(apiEndpoints) == 1 {
    81  		return apiEndpoints
    82  	}
    83  	newAPIEndpoints = make([]string, len(apiEndpoints))
    84  	// Check all API endpoints for standard ports and strip them.
    85  	for i, apiEndpoint := range apiEndpoints {
    86  		_, err := xnet.ParseHTTPURL(apiEndpoint)
    87  		if err != nil {
    88  			continue
    89  		}
    90  		u, err := url.Parse(apiEndpoint)
    91  		if err != nil {
    92  			continue
    93  		}
    94  		if host == "" && isIPv6(u.Hostname()) {
    95  			// Skip all IPv6 endpoints
    96  			continue
    97  		}
    98  		if u.Port() == "80" && u.Scheme == "http" || u.Port() == "443" && u.Scheme == "https" {
    99  			u.Host = u.Hostname()
   100  		}
   101  		newAPIEndpoints[i] = u.String()
   102  	}
   103  	return newAPIEndpoints
   104  }
   105  
   106  // Prints common server startup message. Prints credential, region and browser access.
   107  func printServerCommonMsg(apiEndpoints []string) {
   108  	// Get saved credentials.
   109  	cred := globalActiveCred
   110  
   111  	// Get saved region.
   112  	region := globalSite.Region
   113  
   114  	apiEndpointStr := strings.TrimSpace(strings.Join(apiEndpoints, "  "))
   115  	// Colorize the message and print.
   116  	logger.Info(color.Blue("API: ") + color.Bold(fmt.Sprintf("%s ", apiEndpointStr)))
   117  	if color.IsTerminal() && (!globalServerCtxt.Anonymous && !globalServerCtxt.JSON && globalAPIConfig.permitRootAccess()) {
   118  		logger.Info(color.Blue("   RootUser: ") + color.Bold("%s ", cred.AccessKey))
   119  		logger.Info(color.Blue("   RootPass: ") + color.Bold("%s \n", cred.SecretKey))
   120  		if region != "" {
   121  			logger.Info(color.Blue("   Region: ") + color.Bold("%s", fmt.Sprintf(getFormatStr(len(region), 2), region)))
   122  		}
   123  	}
   124  
   125  	if globalBrowserEnabled {
   126  		consoleEndpointStr := strings.Join(stripStandardPorts(getConsoleEndpoints(), globalMinioConsoleHost), " ")
   127  		logger.Info(color.Blue("WebUI: ") + color.Bold(fmt.Sprintf("%s ", consoleEndpointStr)))
   128  		if color.IsTerminal() && (!globalServerCtxt.Anonymous && !globalServerCtxt.JSON && globalAPIConfig.permitRootAccess()) {
   129  			logger.Info(color.Blue("   RootUser: ") + color.Bold("%s ", cred.AccessKey))
   130  			logger.Info(color.Blue("   RootPass: ") + color.Bold("%s ", cred.SecretKey))
   131  		}
   132  	}
   133  
   134  	printEventNotifiers()
   135  	printLambdaTargets()
   136  }
   137  
   138  // Prints startup message for Object API access, prints link to our SDK documentation.
   139  func printObjectAPIMsg() {
   140  	logger.Info(color.Blue("\nDocs: ") + "https://min.io/docs/minio/linux/index.html")
   141  }
   142  
   143  func printLambdaTargets() {
   144  	if globalLambdaTargetList == nil || globalLambdaTargetList.Empty() {
   145  		return
   146  	}
   147  
   148  	arnMsg := color.Blue("Object Lambda ARNs: ")
   149  	for _, arn := range globalLambdaTargetList.List(globalSite.Region) {
   150  		arnMsg += color.Bold(fmt.Sprintf("%s ", arn))
   151  	}
   152  	logger.Info(arnMsg + "\n")
   153  }
   154  
   155  // Prints bucket notification configurations.
   156  func printEventNotifiers() {
   157  	if globalNotificationSys == nil {
   158  		return
   159  	}
   160  
   161  	arns := globalEventNotifier.GetARNList(true)
   162  	if len(arns) == 0 {
   163  		return
   164  	}
   165  
   166  	arnMsg := color.Blue("SQS ARNs: ")
   167  	for _, arn := range arns {
   168  		arnMsg += color.Bold(fmt.Sprintf("%s ", arn))
   169  	}
   170  
   171  	logger.Info(arnMsg + "\n")
   172  }
   173  
   174  // Prints startup message for command line access. Prints link to our documentation
   175  // and custom platform specific message.
   176  func printCLIAccessMsg(endPoint string, alias string) {
   177  	// Get saved credentials.
   178  	cred := globalActiveCred
   179  
   180  	const mcQuickStartGuide = "https://min.io/docs/minio/linux/reference/minio-mc.html#quickstart"
   181  
   182  	// Configure 'mc', following block prints platform specific information for minio client.
   183  	if color.IsTerminal() && (!globalServerCtxt.Anonymous && globalAPIConfig.permitRootAccess()) {
   184  		logger.Info(color.Blue("\nCLI: ") + mcQuickStartGuide)
   185  		mcMessage := fmt.Sprintf("$ mc alias set '%s' '%s' '%s' '%s'", alias,
   186  			endPoint, cred.AccessKey, cred.SecretKey)
   187  		logger.Info(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
   188  	}
   189  }
   190  
   191  // Get formatted disk/storage info message.
   192  func getStorageInfoMsg(storageInfo StorageInfo) string {
   193  	var msg string
   194  	var mcMessage string
   195  	onlineDisks, offlineDisks := getOnlineOfflineDisksStats(storageInfo.Disks)
   196  	if storageInfo.Backend.Type == madmin.Erasure {
   197  		if offlineDisks.Sum() > 0 {
   198  			mcMessage = "Use `mc admin info` to look for latest server/drive info\n"
   199  		}
   200  
   201  		diskInfo := fmt.Sprintf(" %d Online, %d Offline. ", onlineDisks.Sum(), offlineDisks.Sum())
   202  		msg += color.Blue("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
   203  		if len(mcMessage) > 0 {
   204  			msg = fmt.Sprintf("%s %s", mcMessage, msg)
   205  		}
   206  	}
   207  	return msg
   208  }
   209  
   210  // Prints startup message of storage capacity and erasure information.
   211  func printStorageInfo(storageInfo StorageInfo) {
   212  	if msg := getStorageInfoMsg(storageInfo); msg != "" {
   213  		logger.Info(msg)
   214  	}
   215  }