github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/commands/generic/download.go (about) 1 package generic 2 3 import ( 4 "errors" 5 "github.com/jfrog/jfrog-cli-go/artifactory/spec" 6 "github.com/jfrog/jfrog-cli-go/artifactory/utils" 7 "github.com/jfrog/jfrog-cli-go/utils/cliutils" 8 "github.com/jfrog/jfrog-cli-go/utils/progressbar" 9 "github.com/jfrog/jfrog-client-go/artifactory/buildinfo" 10 "github.com/jfrog/jfrog-client-go/artifactory/services" 11 clientutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" 12 "github.com/jfrog/jfrog-client-go/utils/errorutils" 13 ioUtils "github.com/jfrog/jfrog-client-go/utils/io" 14 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 15 "github.com/jfrog/jfrog-client-go/utils/log" 16 "os" 17 "path/filepath" 18 "strconv" 19 "strings" 20 ) 21 22 type DownloadCommand struct { 23 buildConfiguration *utils.BuildConfiguration 24 GenericCommand 25 configuration *utils.DownloadConfiguration 26 logFile *os.File 27 } 28 29 func NewDownloadCommand() *DownloadCommand { 30 return &DownloadCommand{GenericCommand: *NewGenericCommand()} 31 } 32 33 func (dc *DownloadCommand) LogFile() *os.File { 34 return dc.logFile 35 } 36 37 func (dc *DownloadCommand) SetBuildConfiguration(buildConfiguration *utils.BuildConfiguration) *DownloadCommand { 38 dc.buildConfiguration = buildConfiguration 39 return dc 40 } 41 42 func (dc *DownloadCommand) Configuration() *utils.DownloadConfiguration { 43 return dc.configuration 44 } 45 46 func (dc *DownloadCommand) SetConfiguration(configuration *utils.DownloadConfiguration) *DownloadCommand { 47 dc.configuration = configuration 48 return dc 49 } 50 51 func (dc *DownloadCommand) CommandName() string { 52 return "rt_download" 53 } 54 55 func (dc *DownloadCommand) Run() error { 56 if dc.SyncDeletesPath() != "" && !dc.Quiet() && !cliutils.InteractiveConfirm("Sync-deletes may delete some files in your local file system. Are you sure you want to continue?\n"+ 57 "You can avoid this confirmation message by adding --quiet to the command.") { 58 return nil 59 } 60 // Initialize Progress bar, set logger to a log file 61 var err error 62 var progressBar ioUtils.Progress 63 progressBar, dc.logFile, err = progressbar.InitProgressBarIfPossible() 64 if err != nil { 65 return err 66 } 67 if progressBar != nil { 68 defer progressBar.Quit() 69 } 70 71 // Create Service Manager: 72 servicesManager, err := utils.CreateDownloadServiceManager(dc.rtDetails, dc.configuration.Threads, dc.DryRun(), progressBar) 73 if err != nil { 74 return err 75 } 76 77 // Build Info Collection: 78 isCollectBuildInfo := len(dc.buildConfiguration.BuildName) > 0 && len(dc.buildConfiguration.BuildNumber) > 0 79 if isCollectBuildInfo && !dc.DryRun() { 80 if err = utils.SaveBuildGeneralDetails(dc.buildConfiguration.BuildName, dc.buildConfiguration.BuildNumber); err != nil { 81 return err 82 } 83 } 84 85 var errorOccurred = false 86 var downloadParamsArray []services.DownloadParams 87 // Create DownloadParams for all File-Spec groups. 88 for i := 0; i < len(dc.Spec().Files); i++ { 89 downParams, err := getDownloadParams(dc.Spec().Get(i), dc.configuration) 90 if err != nil { 91 errorOccurred = true 92 log.Error(err) 93 continue 94 } 95 downloadParamsArray = append(downloadParamsArray, downParams) 96 } 97 // Perform download. 98 filesInfo, totalExpected, err := servicesManager.DownloadFiles(downloadParamsArray...) 99 if err != nil { 100 errorOccurred = true 101 log.Error(err) 102 } 103 104 dc.result.SetSuccessCount(len(filesInfo)) 105 dc.result.SetFailCount(totalExpected - len(filesInfo)) 106 // Check for errors. 107 if errorOccurred { 108 return errors.New("Download finished with errors, please review the logs.") 109 } 110 if dc.DryRun() { 111 dc.result.SetSuccessCount(totalExpected) 112 dc.result.SetFailCount(0) 113 return err 114 } else if dc.SyncDeletesPath() != "" { 115 absSyncDeletesPath, err := filepath.Abs(dc.SyncDeletesPath()) 116 if err != nil { 117 return errorutils.CheckError(err) 118 } 119 if _, err = os.Stat(absSyncDeletesPath); err == nil { 120 walkFn := createSyncDeletesWalkFunction(filesInfo) 121 err = fileutils.Walk(dc.SyncDeletesPath(), walkFn, false) 122 if err != nil { 123 return errorutils.CheckError(err) 124 } 125 } else if os.IsNotExist(err) { 126 log.Info("Sync-deletes path", absSyncDeletesPath, "does not exists.") 127 } 128 } 129 log.Debug("Downloaded", strconv.Itoa(len(filesInfo)), "artifacts.") 130 131 // Build Info 132 if isCollectBuildInfo { 133 buildDependencies := convertFileInfoToBuildDependencies(filesInfo) 134 populateFunc := func(partial *buildinfo.Partial) { 135 partial.Dependencies = buildDependencies 136 partial.ModuleId = dc.buildConfiguration.Module 137 } 138 err = utils.SavePartialBuildInfo(dc.buildConfiguration.BuildName, dc.buildConfiguration.BuildNumber, populateFunc) 139 } 140 141 return err 142 } 143 144 func convertFileInfoToBuildDependencies(filesInfo []clientutils.FileInfo) []buildinfo.Dependency { 145 buildDependencies := make([]buildinfo.Dependency, len(filesInfo)) 146 for i, fileInfo := range filesInfo { 147 dependency := buildinfo.Dependency{Checksum: &buildinfo.Checksum{}} 148 dependency.Md5 = fileInfo.Md5 149 dependency.Sha1 = fileInfo.Sha1 150 // Artifact name in build info as the name in artifactory 151 filename, _ := fileutils.GetFileAndDirFromPath(fileInfo.ArtifactoryPath) 152 dependency.Id = filename 153 buildDependencies[i] = dependency 154 } 155 return buildDependencies 156 } 157 158 func getDownloadParams(f *spec.File, configuration *utils.DownloadConfiguration) (downParams services.DownloadParams, err error) { 159 downParams = services.NewDownloadParams() 160 downParams.ArtifactoryCommonParams = f.ToArtifactoryCommonParams() 161 downParams.Symlink = configuration.Symlink 162 downParams.ValidateSymlink = configuration.ValidateSymlink 163 downParams.MinSplitSize = configuration.MinSplitSize 164 downParams.SplitCount = configuration.SplitCount 165 166 downParams.Recursive, err = f.IsRecursive(true) 167 if err != nil { 168 return 169 } 170 171 downParams.IncludeDirs, err = f.IsIncludeDirs(false) 172 if err != nil { 173 return 174 } 175 176 downParams.Flat, err = f.IsFlat(false) 177 if err != nil { 178 return 179 } 180 181 downParams.Explode, err = f.IsExplode(false) 182 if err != nil { 183 return 184 } 185 186 return 187 } 188 189 func createSyncDeletesWalkFunction(downloadedFiles []clientutils.FileInfo) fileutils.WalkFunc { 190 return func(path string, info os.FileInfo, err error) error { 191 // Convert path to absolute path 192 path, err = filepath.Abs(path) 193 if errorutils.CheckError(err) != nil { 194 return err 195 } 196 // Go over the downloaded files list 197 for _, file := range downloadedFiles { 198 // If the current path is a prefix of a downloaded file - we won't delete it. 199 fileAbsPath, err := filepath.Abs(file.LocalPath) 200 if errorutils.CheckError(err) != nil { 201 return err 202 } 203 if strings.HasPrefix(fileAbsPath, path) { 204 return nil 205 } 206 } 207 // The current path is not a prefix of any downloaded file so it should be deleted 208 log.Info("Deleting:", path) 209 if info.IsDir() { 210 // If current path is a dir - remove all content and return SkipDir to stop walking this path 211 err = os.RemoveAll(path) 212 if err == nil { 213 return fileutils.SkipDir 214 } 215 } else { 216 // Path is a file 217 err = os.Remove(path) 218 } 219 220 return errorutils.CheckError(err) 221 } 222 }