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  }