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 }