github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/transferfiles/status.go (about) 1 package transferfiles 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/api" 11 "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/state" 12 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" 13 "github.com/jfrog/jfrog-cli-core/v2/utils/progressbar" 14 "github.com/jfrog/jfrog-client-go/utils/errorutils" 15 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 16 "github.com/jfrog/jfrog-client-go/utils/log" 17 ) 18 19 const sizeUnits = "KMGTPE" 20 21 func ShowStatus() error { 22 var output strings.Builder 23 stateManager, err := state.NewTransferStateManager(true) 24 if err != nil { 25 return err 26 } 27 28 isRunning, err := stateManager.InitStartTimestamp() 29 if err != nil { 30 return err 31 } 32 if !isRunning { 33 addString(&output, "๐ด", "Status", "Not running", 0) 34 log.Output(output.String()) 35 return nil 36 } 37 isStopping, err := isStopping() 38 if err != nil { 39 return err 40 } 41 if isStopping { 42 addString(&output, "๐ก", "Status", "Stopping", 0) 43 log.Output(output.String()) 44 return nil 45 } 46 47 addOverallStatus(stateManager, &output, stateManager.GetRunningTimeString()) 48 if stateManager.CurrentRepoKey != "" { 49 transferState, exists, err := state.LoadTransferState(stateManager.CurrentRepoKey, false) 50 if err != nil { 51 return err 52 } 53 if !exists { 54 return errorutils.CheckErrorf("could not find the state file of repository '%s'. Aborting", stateManager.CurrentRepoKey) 55 } 56 stateManager.TransferState = transferState 57 output.WriteString("\n") 58 setRepositoryStatus(stateManager, &output) 59 } 60 addStaleChunks(stateManager, &output) 61 log.Output(output.String()) 62 return nil 63 } 64 65 func isStopping() (bool, error) { 66 transferDir, err := coreutils.GetJfrogTransferDir() 67 if err != nil { 68 return false, err 69 } 70 71 return fileutils.IsFileExists(filepath.Join(transferDir, StopFileName), false) 72 } 73 74 func addOverallStatus(stateManager *state.TransferStateManager, output *strings.Builder, runningTime string) { 75 addTitle(output, "Overall Transfer Status") 76 addString(output, coreutils.RemoveEmojisIfNonSupportedTerminal("๐ข"), "Status", "Running", 3) 77 addString(output, "๐", "Running for", runningTime, 3) 78 addString(output, "๐ ", "Storage", sizeToString(stateManager.OverallTransfer.TransferredSizeBytes)+" / "+sizeToString(stateManager.OverallTransfer.TotalSizeBytes)+calcPercentageInt64(stateManager.OverallTransfer.TransferredSizeBytes, stateManager.OverallTransfer.TotalSizeBytes), 3) 79 addString(output, "๐ฆ", "Repositories", fmt.Sprintf("%d / %d", stateManager.TotalRepositories.TransferredUnits, stateManager.TotalRepositories.TotalUnits)+calcPercentageInt64(stateManager.TotalRepositories.TransferredUnits, stateManager.TotalRepositories.TotalUnits), 2) 80 addString(output, "๐งต", "Working threads", strconv.Itoa(stateManager.WorkingThreads), 2) 81 addString(output, "โก", "Transfer speed", stateManager.GetSpeedString(), 2) 82 addString(output, "โ", "Estimated time remaining", stateManager.GetEstimatedRemainingTimeString(), 1) 83 failureTxt := strconv.FormatUint(stateManager.TransferFailures, 10) 84 if stateManager.TransferFailures > 0 { 85 failureTxt += " (" + progressbar.RetryFailureContentNote + ")" 86 } 87 addString(output, "โ", "Transfer failures", failureTxt, 2) 88 } 89 90 func calcPercentageInt64(transferred, total int64) string { 91 if transferred == 0 || total == 0 { 92 return "" 93 } 94 return fmt.Sprintf(" (%.1f%%)", float64(transferred)/float64(total)*100) 95 } 96 97 func setRepositoryStatus(stateManager *state.TransferStateManager, output *strings.Builder) { 98 addTitle(output, "Current Repository Status") 99 addString(output, "๐ท ", "Name", stateManager.CurrentRepoKey, 3) 100 currentRepo := stateManager.CurrentRepo 101 switch stateManager.CurrentRepoPhase { 102 case api.Phase1, api.Phase3: 103 if stateManager.CurrentRepoPhase == api.Phase1 { 104 addString(output, "๐ข", "Phase", "Transferring all files in the repository (1/3)", 3) 105 } else { 106 addString(output, "๐ข", "Phase", "Retrying transfer failures and transfer delayed files (3/3)", 3) 107 } 108 addString(output, "๐ ", "Storage", sizeToString(currentRepo.Phase1Info.TransferredSizeBytes)+" / "+sizeToString(currentRepo.Phase1Info.TotalSizeBytes)+calcPercentageInt64(currentRepo.Phase1Info.TransferredSizeBytes, currentRepo.Phase1Info.TotalSizeBytes), 3) 109 addString(output, "๐", "Files", fmt.Sprintf("%d / %d", currentRepo.Phase1Info.TransferredUnits, currentRepo.Phase1Info.TotalUnits)+calcPercentageInt64(currentRepo.Phase1Info.TransferredUnits, currentRepo.Phase1Info.TotalUnits), 3) 110 case api.Phase2: 111 addString(output, "๐ข", "Phase", "Transferring newly created and modified files (2/3)", 3) 112 } 113 if stateManager.CurrentRepoPhase == api.Phase1 { 114 addString(output, "๐", "Visited folders", strconv.FormatUint(stateManager.VisitedFolders, 10), 2) 115 } 116 delayedTxt := strconv.FormatUint(stateManager.DelayedFiles, 10) 117 if stateManager.DelayedFiles > 0 { 118 delayedTxt += " (" + progressbar.DelayedFilesContentNote + ")" 119 } 120 addString(output, "โ", "Delayed files", delayedTxt, 2) 121 } 122 123 func addStaleChunks(stateManager *state.TransferStateManager, output *strings.Builder) { 124 if len(stateManager.StaleChunks) == 0 { 125 return 126 } 127 output.WriteString("\n") 128 addTitle(output, "File Chunks in Transit for More than 30 Minutes") 129 130 for _, nodeStaleChunks := range stateManager.StaleChunks { 131 addString(output, "๐ท๏ธ ", "Node ID", nodeStaleChunks.NodeID, 1) 132 for _, staleChunks := range nodeStaleChunks.Chunks { 133 addString(output, " ๐ท๏ธ ", "Chunk ID", staleChunks.ChunkID, 1) 134 sent := time.Unix(staleChunks.Sent, 0) 135 runningSecs := int64(time.Since(sent).Seconds()) 136 addString(output, " โฑ๏ธ ", "Sent", sent.Format(time.DateTime)+" ("+state.SecondsToLiteralTime(runningSecs, "")+")", 1) 137 for _, file := range staleChunks.Files { 138 output.WriteString("\t\t๐ " + file + "\n") 139 } 140 } 141 } 142 } 143 144 func addTitle(output *strings.Builder, title string) { 145 output.WriteString(coreutils.PrintBoldTitle(title + "\n")) 146 } 147 148 func addString(output *strings.Builder, emoji, key, value string, tabsCount int) { 149 indentation := strings.Repeat("\t", tabsCount) 150 if indentation == "" { 151 indentation = " " 152 } 153 if len(emoji) > 0 { 154 if coreutils.IsWindows() { 155 emoji = "โ" 156 } 157 emoji += " " 158 } 159 key = emoji + key + ":" 160 // PrintBold removes emojis if they are unsupported. After they are removed, the string is also trimmed, so we should avoid adding trailing spaces to the key. 161 output.WriteString(coreutils.PrintBold(key)) 162 output.WriteString(indentation + value + "\n") 163 } 164 165 func sizeToString(sizeInBytes int64) string { 166 var divider int64 = 1024 167 sizeUnitIndex := 0 168 for ; sizeUnitIndex < len(sizeUnits)-1 && (sizeInBytes >= divider<<10); sizeUnitIndex++ { 169 divider <<= 10 170 } 171 return fmt.Sprintf("%.1f %ciB", float64(sizeInBytes)/float64(divider), sizeUnits[sizeUnitIndex]) 172 }