github.com/cobalt77/jfrog-client-go@v0.14.5/artifactory/services/utils/deleteutils.go (about)

     1  package utils
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/cobalt77/jfrog-client-go/utils"
     9  	"github.com/cobalt77/jfrog-client-go/utils/errorutils"
    10  	"github.com/cobalt77/jfrog-client-go/utils/io/content"
    11  )
    12  
    13  func WildcardToDirsPath(deletePattern, searchResult string) (string, error) {
    14  	if !strings.HasSuffix(deletePattern, "/") {
    15  		return "", errors.New("Delete pattern must end with \"/\"")
    16  	}
    17  
    18  	regexpPattern := "^" + strings.Replace(deletePattern, "*", "([^/]*|.*)", -1)
    19  	r, err := regexp.Compile(regexpPattern)
    20  	errorutils.CheckError(err)
    21  	if err != nil {
    22  		return "", err
    23  	}
    24  
    25  	groups := r.FindStringSubmatch(searchResult)
    26  	if len(groups) > 0 {
    27  		return groups[0], nil
    28  	}
    29  	return "", nil
    30  }
    31  
    32  // Write all the dirs to be deleted into 'resultWriter'.
    33  // However, skip dirs with files(s) that should not be deleted.
    34  // In order to accomplish this, we check if the dirs are a prefix of any artifact, witch means the folder contains the artifact and should not be deleted.
    35  // Optimization: In order not to scan for each dir the entire artifact reader and see if it is a prefix or not, we rely on the fact that the dirs and artifacts are sorted.
    36  // We have two sorted readers in ascending order, we will start scanning from the beginning of the lists and compare whether the folder is a prefix of the current artifact,
    37  // in case this is true the dir should not be deleted and we can move on to the next dir, otherwise we have to continue to the next dir or artifact.
    38  // To know this, we will choose to move on with the lexicographic largest between the two.
    39  //
    40  // candidateDirsReaders - Sorted list of dirs to be deleted.
    41  // filesNotToBeDeleteReader - Sorted files that should not be deleted.
    42  // resultWriter - The filtered list of dirs to be deleted.
    43  func WriteCandidateDirsToBeDeleted(candidateDirsReaders []*content.ContentReader, filesNotToBeDeleteReader *content.ContentReader, resultWriter *content.ContentWriter) (err error) {
    44  	dirsToBeDeletedReader, err := MergeSortedFiles(candidateDirsReaders, true)
    45  	if err != nil {
    46  		return
    47  	}
    48  	defer dirsToBeDeletedReader.Close()
    49  	var candidateDirToBeDeletedPath string
    50  	var itemNotToBeDeletedLocation string
    51  	var candidateDirToBeDeleted, artifactNotToBeDeleted *ResultItem
    52  	for {
    53  		// Fetch the next 'candidateDirToBeDeleted'.
    54  		if candidateDirToBeDeleted == nil {
    55  			candidateDirToBeDeleted = new(ResultItem)
    56  			if err = dirsToBeDeletedReader.NextRecord(candidateDirToBeDeleted); err != nil {
    57  				break
    58  			}
    59  			if candidateDirToBeDeleted.Name == "." {
    60  				continue
    61  			}
    62  			candidateDirToBeDeletedPath = strings.ToLower(candidateDirToBeDeleted.GetItemRelativePath())
    63  		}
    64  		// Fetch the next 'artifactNotToBeDelete'.
    65  		if artifactNotToBeDeleted == nil {
    66  			artifactNotToBeDeleted = new(ResultItem)
    67  			if err = filesNotToBeDeleteReader.NextRecord(artifactNotToBeDeleted); err != nil {
    68  				// No artifacts left, write remaining dirs to be deleted to result file.
    69  				resultWriter.Write(*candidateDirToBeDeleted)
    70  				writeRemainCandidate(resultWriter, dirsToBeDeletedReader)
    71  				break
    72  			}
    73  			itemNotToBeDeletedLocation = strings.ToLower(artifactNotToBeDeleted.GetItemRelativeLocation())
    74  		}
    75  		// Found an 'artifact not to be deleted' in 'dir to be deleted', therefore skip writing the dir to the result file.
    76  		if strings.HasPrefix(itemNotToBeDeletedLocation, candidateDirToBeDeletedPath) {
    77  			candidateDirToBeDeleted = nil
    78  			continue
    79  		}
    80  		// 'artifactNotToBeDeletePath' & 'candidateDirToBeDeletedPath' are both sorted. As a result 'candidateDirToBeDeleted' cant be a prefix for any of the remaining artifacts.
    81  		if itemNotToBeDeletedLocation > candidateDirToBeDeletedPath {
    82  			resultWriter.Write(*candidateDirToBeDeleted)
    83  			candidateDirToBeDeleted = nil
    84  			continue
    85  		}
    86  		artifactNotToBeDeleted = nil
    87  	}
    88  	err = filesNotToBeDeleteReader.GetError()
    89  	filesNotToBeDeleteReader.Reset()
    90  	return
    91  }
    92  
    93  func writeRemainCandidate(cw *content.ContentWriter, mergeResult *content.ContentReader) {
    94  	for toBeDeleted := new(ResultItem); mergeResult.NextRecord(toBeDeleted) == nil; toBeDeleted = new(ResultItem) {
    95  		cw.Write(*toBeDeleted)
    96  	}
    97  }
    98  
    99  func FilterCandidateToBeDeleted(deleteCandidates *content.ContentReader, resultWriter *content.ContentWriter) ([]*content.ContentReader, error) {
   100  	paths := make(map[string]ResultItem)
   101  	pathsKeys := make([]string, 0, utils.MaxBufferSize)
   102  	dirsToBeDeleted := []*content.ContentReader{}
   103  	for candidate := new(ResultItem); deleteCandidates.NextRecord(candidate) == nil; candidate = new(ResultItem) {
   104  		// Save all dirs candidate in a diffrent temp file.
   105  		if candidate.Type == "folder" {
   106  			if candidate.Name == "." {
   107  				continue
   108  			}
   109  			pathsKeys = append(pathsKeys, candidate.GetItemRelativePath())
   110  			paths[candidate.GetItemRelativePath()] = *candidate
   111  			if len(pathsKeys) == utils.MaxBufferSize {
   112  				sortedCandidateDirsFile, err := SortAndSaveBufferToFile(paths, pathsKeys, true)
   113  				if err != nil {
   114  					return nil, err
   115  				}
   116  				dirsToBeDeleted = append(dirsToBeDeleted, sortedCandidateDirsFile)
   117  				// Init buffer.
   118  				paths = make(map[string]ResultItem)
   119  				pathsKeys = make([]string, 0, utils.MaxBufferSize)
   120  			}
   121  		} else {
   122  			// Write none dir results.
   123  			resultWriter.Write(*candidate)
   124  		}
   125  	}
   126  	if err := deleteCandidates.GetError(); err != nil {
   127  		return nil, err
   128  	}
   129  	deleteCandidates.Reset()
   130  	if len(pathsKeys) > 0 {
   131  		sortedFile, err := SortAndSaveBufferToFile(paths, pathsKeys, true)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  		dirsToBeDeleted = append(dirsToBeDeleted, sortedFile)
   136  	}
   137  	return dirsToBeDeleted, nil
   138  }