github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/transferfiles/fileserror.go (about)

     1  package transferfiles
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/jfrog/gofrog/parallel"
    10  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/api"
    11  	"github.com/jfrog/jfrog-client-go/utils/log"
    12  )
    13  
    14  type errorFileHandlerFunc func() parallel.TaskFunc
    15  
    16  // Manages the phase of handling upload failures that were collected during previous runs and phases.
    17  type errorsRetryPhase struct {
    18  	phaseBase
    19  	errorsFilesToHandle []string
    20  }
    21  
    22  func (e *errorsRetryPhase) getPhaseName() string {
    23  	return "Retry Transfer Errors Phase"
    24  }
    25  
    26  func (e *errorsRetryPhase) run() error {
    27  	return e.handlePreviousUploadFailures()
    28  }
    29  
    30  // Consumes errors files with upload failures from cache and tries to transfer these files again.
    31  // Does so by creating and uploading by chunks, and polling on status.
    32  // Consumed errors files are deleted, new failures are written to new files.
    33  func (e *errorsRetryPhase) handlePreviousUploadFailures() error {
    34  	errorsFilesToHandle := e.errorsFilesToHandle
    35  	if len(errorsFilesToHandle) == 0 {
    36  		return nil
    37  	}
    38  	log.Info("Starting to handle previous upload failures...")
    39  	e.transferManager = newTransferManager(e.phaseBase, getDelayUploadComparisonFunctions(e.repoSummary.PackageType))
    40  	action := func(pcWrapper *producerConsumerWrapper, uploadChunkChan chan UploadedChunk, delayHelper delayUploadHelper, errorsChannelMng *ErrorsChannelMng) error {
    41  		errFileHandler := e.createErrorFilesHandleFunc(pcWrapper, uploadChunkChan, delayHelper, errorsChannelMng)
    42  		_, err := pcWrapper.chunkBuilderProducerConsumer.AddTaskWithError(errFileHandler(), pcWrapper.errorsQueue.AddError)
    43  		return err
    44  	}
    45  	delayAction := func(phase phaseBase, addedDelayFiles []string) error {
    46  		return consumeAllDelayFiles(phase)
    47  	}
    48  
    49  	if err := e.transferManager.doTransferWithProducerConsumer(action, delayAction); err != nil {
    50  		return err
    51  	}
    52  
    53  	log.Info("Done handling previous upload failures.")
    54  	return deleteAllFiles(errorsFilesToHandle)
    55  }
    56  
    57  func convertUploadStatusToFileRepresentation(statuses []ExtendedFileUploadStatusResponse) (files []api.FileRepresentation) {
    58  	for _, status := range statuses {
    59  		files = append(files, status.FileRepresentation)
    60  	}
    61  	return
    62  }
    63  
    64  func (e *errorsRetryPhase) handleErrorsFile(errFilePath string, pcWrapper *producerConsumerWrapper, uploadChunkChan chan UploadedChunk, delayHelper delayUploadHelper, errorsChannelMng *ErrorsChannelMng) error {
    65  	if ShouldStop(&e.phaseBase, &delayHelper, errorsChannelMng) {
    66  		return nil
    67  	}
    68  	log.Debug("Handling errors file: '", errFilePath, "'")
    69  
    70  	// Read and parse the file
    71  	failedFiles, err := readErrorFile(errFilePath)
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	if e.progressBar != nil {
    77  		// Since we're about to handle the transfer retry of the failed files,
    78  		// we should now decrement the failures counter view.
    79  		e.progressBar.changeNumberOfFailuresBy(-1 * len(failedFiles.Errors))
    80  		err = e.stateManager.ChangeTransferFailureCountBy(uint64(len(failedFiles.Errors)), false)
    81  		if err != nil {
    82  			return err
    83  		}
    84  	}
    85  
    86  	// Upload
    87  	_, err = uploadByChunks(convertUploadStatusToFileRepresentation(failedFiles.Errors), uploadChunkChan, e.phaseBase, delayHelper, errorsChannelMng, pcWrapper)
    88  	return err
    89  }
    90  
    91  func (e *errorsRetryPhase) createErrorFilesHandleFunc(pcWrapper *producerConsumerWrapper, uploadChunkChan chan UploadedChunk, delayHelper delayUploadHelper, errorsChannelMng *ErrorsChannelMng) errorFileHandlerFunc {
    92  	return func() parallel.TaskFunc {
    93  		return func(int) error {
    94  			var errList []string
    95  			var err error
    96  			for _, errFile := range e.errorsFilesToHandle {
    97  				err = e.handleErrorsFile(errFile, pcWrapper, uploadChunkChan, delayHelper, errorsChannelMng)
    98  				if err != nil {
    99  					errList = append(errList, fmt.Sprintf("handleErrorsFile for %s failed with error: \n%s", errFile, err.Error()))
   100  				}
   101  			}
   102  			if len(errList) > 0 {
   103  				err = errors.New(strings.Join(errList, "\n"))
   104  			}
   105  			return err
   106  		}
   107  	}
   108  }
   109  
   110  func (e *errorsRetryPhase) shouldSkipPhase() (bool, error) {
   111  	var err error
   112  	// check if error file exist for this repo
   113  	e.errorsFilesToHandle, err = getErrorsFiles([]string{e.repoKey}, true)
   114  	if err != nil {
   115  		return true, err
   116  	}
   117  	return len(e.errorsFilesToHandle) < 1, nil
   118  }
   119  
   120  func (e *errorsRetryPhase) phaseStarted() error {
   121  	e.startTime = time.Now()
   122  	return nil
   123  }
   124  
   125  func (e *errorsRetryPhase) initProgressBar() error {
   126  	if e.progressBar == nil {
   127  		return nil
   128  	}
   129  
   130  	// Init progress with the number of tasks of errors file handling (fixing previous upload failures)
   131  	filesCount := 0
   132  	var storage int64 = 0
   133  	for _, path := range e.errorsFilesToHandle {
   134  		failedFiles, err := readErrorFile(path)
   135  		if err != nil {
   136  			return err
   137  		}
   138  		for _, singleFailedFile := range failedFiles.Errors {
   139  			storage += singleFailedFile.SizeBytes
   140  		}
   141  		filesCount += len(failedFiles.Errors)
   142  	}
   143  	// The progress bar will also be responsible to display the number of delayed items for this repository.
   144  	// Those delayed artifacts will be handled at the end of this phase in case they exist.
   145  	delayFiles, err := getDelayFiles([]string{e.repoKey})
   146  	if err != nil {
   147  		return err
   148  	}
   149  	delayCount, delayStorage, err := countDelayFilesContent(delayFiles)
   150  	if err != nil {
   151  		return err
   152  	}
   153  	err = e.stateManager.SetTotalSizeAndFilesPhase3(int64(filesCount)+int64(delayCount), storage+delayStorage)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	e.progressBar.AddPhase3()
   158  	return nil
   159  }
   160  
   161  func (e *errorsRetryPhase) phaseDone() error {
   162  	if e.progressBar != nil {
   163  		return e.progressBar.DonePhase(e.phaseId)
   164  	}
   165  	return nil
   166  }