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 }