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  }