github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/transferfiles/transferfileprogress.go (about) 1 package transferfiles 2 3 import ( 4 "time" 5 6 "github.com/gookit/color" 7 "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/state" 8 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" 9 coreLog "github.com/jfrog/jfrog-cli-core/v2/utils/log" 10 "github.com/jfrog/jfrog-cli-core/v2/utils/progressbar" 11 "github.com/jfrog/jfrog-client-go/utils/errorutils" 12 "github.com/vbauerster/mpb/v7" 13 ) 14 15 const phase1HeadLine = "Phase 1: Transferring all files in the repository" 16 17 // TransferProgressMng provides progress indication for the jf rt transfer-files command. 18 // Transferring one repository's data at a time. 19 type TransferProgressMng struct { 20 // Determine whether the progress bar should be displayed 21 shouldDisplay bool 22 // Task bar with the total repositories transfer progress 23 totalRepositories *progressbar.TasksWithHeadlineProg 24 // A bar showing the running time 25 runningTime *progressbar.TasksProgressBar 26 // A bar showing the remaining storage to transfer in the process 27 totalSize *progressbar.TasksProgressBar 28 // A bar showing the number of working transfer threads 29 workingThreads *progressbar.TasksProgressBar 30 // A bar showing the speed of the data transfer 31 speedBar *progressbar.TasksProgressBar 32 // A bar showing the estimated remaining time for the transfer 33 timeEstBar *progressbar.TasksProgressBar 34 // A bar showing the number of visited folders 35 visitedFoldersBar *progressbar.TasksProgressBar 36 // A bar showing the number of delayed artifacts in the process 37 delayedBar *progressbar.TasksProgressBar 38 // A bar showing the number of transfer failures in the process 39 errorBar *progressbar.TasksProgressBar 40 // Current repo progress bars 41 currentRepoHeadline *mpb.Bar 42 emptyLine *mpb.Bar 43 phases []*progressbar.TasksWithHeadlineProg 44 // Progress bar manager 45 barsMng *progressbar.ProgressBarMng 46 // Transfer progress bar manager 47 transferMng *progressbar.TransferProgressMng 48 // In case of an emergency stop the transfer's progress bar will be aborted and the 'stopLine' bar will be display. 49 stopLine *mpb.Bar 50 // Progress bar labels 51 filesStatus *int 52 transferState *state.TransferStateManager 53 windows bool 54 } 55 56 // NewTransferProgressMng creates TransferProgressMng object. 57 // If the progress bar shouldn't be displayed returns nil. 58 func initTransferProgressMng(allSourceLocalRepos []string, tdc *TransferFilesCommand, fileStatus int) error { 59 // Init the transfer progress bar manager 60 trmng, shouldDisplay, err := progressbar.InitTransferProgressBarMng(tdc.stateManager, allSourceLocalRepos) 61 if !shouldDisplay || err != nil { 62 return err 63 } 64 transfer := TransferProgressMng{barsMng: trmng.GetBarMng(), shouldDisplay: true, transferMng: trmng} 65 transfer.transferState = tdc.stateManager 66 transfer.filesStatus = &fileStatus 67 transfer.windows = coreutils.IsWindows() 68 transfer.transferMng.StopCurrentRepoProgressBars(false) 69 // Init Progress Bars 70 transfer.totalRepositories = transfer.transferMng.NewRepositoriesProgressBar() 71 transfer.totalSize = transfer.transferMng.NewGeneralProgBar() 72 transfer.workingThreads = transfer.transferMng.NewWorkingThreadsProg() 73 transfer.runningTime = transfer.transferMng.NewRunningTimeProgressBar() 74 transfer.speedBar = transfer.transferMng.NewSpeedProgBar() 75 transfer.timeEstBar = transfer.transferMng.NewTimeEstBar() 76 // Init global error count for the process 77 transfer.errorBar = transfer.transferMng.NewErrorBar() 78 tdc.progressbar = &transfer 79 return nil 80 } 81 82 // NewRepository adds new repository's progress details. 83 // Aborting previous repository if exists. 84 func (t *TransferProgressMng) NewRepository(name string) { 85 // Abort previous repository before creating the new one 86 if t.currentRepoHeadline != nil { 87 t.RemoveRepository() 88 } 89 t.emptyLine = t.barsMng.NewHeadlineBar("") 90 t.currentRepoHeadline = t.barsMng.NewHeadlineBarWithSpinner("Current repository: " + color.Green.Render(name)) 91 t.visitedFoldersBar = t.transferMng.NewVisitedFoldersBar() 92 t.delayedBar = t.transferMng.NewDelayedBar() 93 t.transferMng.StopCurrentRepoProgressBars(false) 94 } 95 96 // Quit terminate the TransferProgressMng process. 97 func (t *TransferProgressMng) Quit() error { 98 t.transferMng.StopCurrentRepoProgressBars(true) 99 t.transferMng.StopGlobalProgressBars() 100 if t.ShouldDisplay() { 101 t.abortMetricsBars() 102 if t.currentRepoHeadline != nil { 103 t.RemoveRepository() 104 } 105 if t.totalRepositories != nil { 106 t.barsMng.QuitTasksWithHeadlineProgressBar(t.totalRepositories) 107 } 108 // Wait a few refresh rates to make sure all aborts have finished. 109 time.Sleep(progressbar.ProgressRefreshRate * 3) 110 // Wait for all go routines to finish before quiting 111 t.transferMng.WaitForReposGoRoutineToFinish() 112 t.barsMng.GetBarsWg().Wait() 113 } else if t.stopLine != nil { 114 t.stopLine.Abort(true) 115 t.stopLine = nil 116 } 117 118 // Close log file 119 if t.barsMng.GetLogFile() != nil { 120 err := coreLog.CloseLogFile(t.barsMng.GetLogFile()) 121 if err != nil { 122 return err 123 } 124 // Set back the default logger 125 coreLog.SetDefaultLogger() 126 } 127 return nil 128 } 129 130 func (t *TransferProgressMng) ShouldDisplay() bool { 131 return t.shouldDisplay 132 } 133 134 // IncrementPhase increments completed tasks count for a specific phase by 1. 135 func (t *TransferProgressMng) IncrementPhase(id int) error { 136 if len(t.phases) == 0 { 137 // Progress bar was terminated 138 return nil 139 } 140 if id < 0 || id > len(t.phases)-1 { 141 return errorutils.CheckErrorf("IncrementPhase: invalid phase id %d", id) 142 } 143 if t.phases[id].GetTasksProgressBar().GetTotal() == 0 { 144 return nil 145 } 146 if t.ShouldDisplay() { 147 t.barsMng.Increment(t.phases[id]) 148 } 149 return nil 150 } 151 152 // IncrementPhaseBy increments completed tasks count for a specific phase by n. 153 func (t *TransferProgressMng) IncrementPhaseBy(id int, n int) error { 154 if len(t.phases) == 0 { 155 // Progress bar was terminated 156 return nil 157 } 158 if id < 0 || id > len(t.phases)-1 { 159 return errorutils.CheckErrorf("IncrementPhaseBy: invalid phase id %d", id) 160 } 161 if t.phases[id].GetTasksProgressBar().GetTotal() == 0 { 162 return nil 163 } 164 if t.phases[id].GetTasksProgressBar().GetTotal() < t.phases[id].GetTasksProgressBar().GetTasksCount()+int64(n) { 165 t.barsMng.IncBy(n, t.phases[id]) 166 return t.DonePhase(id) 167 } 168 if t.ShouldDisplay() { 169 t.barsMng.IncBy(n, t.phases[id]) 170 } 171 return nil 172 } 173 174 func (t *TransferProgressMng) DonePhase(id int) error { 175 if len(t.phases) == 0 { 176 // Progress bar was terminated 177 return nil 178 } 179 if id < 0 || id > len(t.phases)-1 { 180 return errorutils.CheckErrorf("DonePhase: invalid phase id %d", id) 181 } 182 t.barsMng.DoneTask(t.phases[id]) 183 return nil 184 } 185 186 func (t *TransferProgressMng) AddPhase1(skip bool) { 187 if skip { 188 t.phases = append(t.phases, t.barsMng.NewTasksWithHeadlineProgressBar(0, phase1HeadLine, false, t.windows, "")) 189 } else { 190 bar2 := t.transferMng.NewPhase1ProgressBar() 191 t.phases = append(t.phases, bar2) 192 } 193 } 194 195 func (t *TransferProgressMng) AddPhase2() { 196 bar := t.transferMng.NewPhase2ProgressBar() 197 t.phases = append(t.phases, bar) 198 } 199 200 func (t *TransferProgressMng) AddPhase3() { 201 bar := t.transferMng.NewPhase3ProgressBar() 202 t.phases = append(t.phases, bar) 203 } 204 205 func (t *TransferProgressMng) RemoveRepository() { 206 if t.currentRepoHeadline == nil { 207 return 208 } 209 // Abort all current repository's bars 210 t.currentRepoHeadline.Abort(true) 211 t.currentRepoHeadline = nil 212 t.visitedFoldersBar.GetBar().Abort(true) 213 t.delayedBar.GetBar().Abort(true) 214 t.emptyLine.Abort(true) 215 t.emptyLine = nil 216 // Abort all phases bars 217 for i := 0; i < len(t.phases); i++ { 218 t.transferMng.QuitDoubleHeadLineProgWithBar(t.phases[i]) 219 } 220 t.transferMng.StopCurrentRepoProgressBars(true) 221 t.transferMng.WaitForPhasesGoRoutinesToFinish() 222 t.phases = nil 223 224 // Wait a refresh rate to make sure all aborts have finished 225 time.Sleep(progressbar.ProgressRefreshRate) 226 } 227 228 func (t *TransferProgressMng) incNumberOfVisitedFolders() { 229 if t.ShouldDisplay() { 230 t.visitedFoldersBar.SetGeneralProgressTotal(t.visitedFoldersBar.GetTotal() + 1) 231 } 232 } 233 234 func (t *TransferProgressMng) changeNumberOfDelayedFiles(n int) { 235 if t.ShouldDisplay() { 236 diff := int64(n) 237 t.delayedBar.SetGeneralProgressTotal(t.delayedBar.GetTotal() + diff) 238 } 239 } 240 241 func (t *TransferProgressMng) changeNumberOfFailuresBy(n int) { 242 if t.ShouldDisplay() { 243 diff := int64(n) 244 t.errorBar.SetGeneralProgressTotal(t.errorBar.GetTotal() + diff) 245 } 246 } 247 248 func (t *TransferProgressMng) StopGracefully() { 249 if !t.ShouldDisplay() { 250 return 251 } 252 t.shouldDisplay = false 253 // Wait a refresh rate to make sure all 'increase' operations have finished before aborting all bars 254 time.Sleep(progressbar.ProgressRefreshRate) 255 t.transferMng.StopGlobalProgressBars() 256 t.abortMetricsBars() 257 t.RemoveRepository() 258 t.transferMng.WaitForReposGoRoutineToFinish() 259 t.barsMng.QuitTasksWithHeadlineProgressBar(t.totalRepositories) 260 t.totalRepositories = nil 261 t.stopLine = t.barsMng.NewHeadlineBarWithSpinner(coreutils.RemoveEmojisIfNonSupportedTerminal("🛑 Gracefully stopping files transfer")) 262 } 263 264 func (t *TransferProgressMng) abortMetricsBars() { 265 for _, barPtr := range []*progressbar.TasksProgressBar{t.runningTime, t.workingThreads, t.visitedFoldersBar, t.delayedBar, t.errorBar, t.speedBar, t.timeEstBar, t.totalSize} { 266 if barPtr != nil { 267 barPtr.GetBar().Abort(true) 268 } 269 } 270 }