github.com/cobalt77/jfrog-client-go@v0.14.5/artifactory/services/delete.go (about) 1 package services 2 3 import ( 4 "errors" 5 "net/http" 6 "strconv" 7 8 rthttpclient "github.com/cobalt77/jfrog-client-go/artifactory/httpclient" 9 "github.com/cobalt77/jfrog-client-go/artifactory/services/utils" 10 "github.com/cobalt77/jfrog-client-go/auth" 11 clientutils "github.com/cobalt77/jfrog-client-go/utils" 12 "github.com/cobalt77/jfrog-client-go/utils/errorutils" 13 "github.com/cobalt77/jfrog-client-go/utils/io/content" 14 "github.com/cobalt77/jfrog-client-go/utils/log" 15 "github.com/jfrog/gofrog/parallel" 16 ) 17 18 type DeleteService struct { 19 client *rthttpclient.ArtifactoryHttpClient 20 ArtDetails auth.ServiceDetails 21 DryRun bool 22 Threads int 23 } 24 25 func NewDeleteService(client *rthttpclient.ArtifactoryHttpClient) *DeleteService { 26 return &DeleteService{client: client} 27 } 28 29 func (ds *DeleteService) GetArtifactoryDetails() auth.ServiceDetails { 30 return ds.ArtDetails 31 } 32 33 func (ds *DeleteService) SetArtifactoryDetails(rt auth.ServiceDetails) { 34 ds.ArtDetails = rt 35 } 36 37 func (ds *DeleteService) IsDryRun() bool { 38 return ds.DryRun 39 } 40 41 func (ds *DeleteService) GetThreads() int { 42 return ds.Threads 43 } 44 45 func (ds *DeleteService) SetThreads(threads int) { 46 ds.Threads = threads 47 } 48 49 func (ds *DeleteService) GetJfrogHttpClient() (*rthttpclient.ArtifactoryHttpClient, error) { 50 return ds.client, nil 51 } 52 53 func (ds *DeleteService) GetPathsToDelete(deleteParams DeleteParams) (resultItems *content.ContentReader, err error) { 54 log.Info("Searching artifacts...") 55 var tempResultItems, toBeDeletedDirs *content.ContentReader 56 switch deleteParams.GetSpecType() { 57 case utils.AQL: 58 resultItems, err = utils.SearchBySpecWithAql(deleteParams.GetFile(), ds, utils.NONE) 59 if err != nil { 60 return 61 } 62 case utils.WILDCARD: 63 deleteParams.SetIncludeDirs(true) 64 tempResultItems, err = utils.SearchBySpecWithPattern(deleteParams.GetFile(), ds, utils.NONE) 65 if err != nil { 66 return 67 } 68 defer tempResultItems.Close() 69 toBeDeletedDirs, err = removeNotToBeDeletedDirs(deleteParams.GetFile(), ds, tempResultItems) 70 if err != nil { 71 return 72 } 73 // The 'removeNotToBeDeletedDirs' should filter out any folders that should not be deleted, if no action is needed, nil will be return. 74 // As a result, we should keep the flow with tempResultItems reader instead. 75 if toBeDeletedDirs == nil { 76 toBeDeletedDirs = tempResultItems 77 } 78 defer toBeDeletedDirs.Close() 79 resultItems, err = utils.ReduceTopChainDirResult(toBeDeletedDirs) 80 if err != nil { 81 return 82 } 83 case utils.BUILD: 84 resultItems, err = utils.SearchBySpecWithBuild(deleteParams.GetFile(), ds) 85 } 86 length, err := resultItems.Length() 87 if err != nil { 88 return 89 } 90 utils.LogSearchResults(length) 91 return 92 } 93 94 type fileDeleteHandlerFunc func(utils.ResultItem) parallel.TaskFunc 95 96 func (ds *DeleteService) createFileHandlerFunc(result *utils.Result) fileDeleteHandlerFunc { 97 return func(resultItem utils.ResultItem) parallel.TaskFunc { 98 return func(threadId int) error { 99 result.TotalCount[threadId]++ 100 logMsgPrefix := clientutils.GetLogMsgPrefix(threadId, ds.DryRun) 101 deletePath, e := utils.BuildArtifactoryUrl(ds.GetArtifactoryDetails().GetUrl(), resultItem.GetItemRelativePath(), make(map[string]string)) 102 if e != nil { 103 return e 104 } 105 log.Info(logMsgPrefix+"Deleting", resultItem.GetItemRelativePath()) 106 if ds.DryRun { 107 return nil 108 } 109 httpClientsDetails := ds.GetArtifactoryDetails().CreateHttpClientDetails() 110 resp, body, err := ds.client.SendDelete(deletePath, nil, &httpClientsDetails) 111 if err != nil { 112 log.Error(err) 113 return err 114 } 115 if resp.StatusCode != http.StatusNoContent { 116 err = errors.New("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)) 117 log.Error(errorutils.CheckError(err)) 118 return err 119 } 120 121 result.SuccessCount[threadId]++ 122 return nil 123 } 124 } 125 } 126 127 func (ds *DeleteService) DeleteFiles(deleteItems *content.ContentReader) (int, error) { 128 producerConsumer := parallel.NewBounedRunner(ds.GetThreads(), false) 129 errorsQueue := clientutils.NewErrorsQueue(1) 130 result := *utils.NewResult(ds.Threads) 131 go func() { 132 defer producerConsumer.Done() 133 for deleteItem := new(utils.ResultItem); deleteItems.NextRecord(deleteItem) == nil; deleteItem = new(utils.ResultItem) { 134 fileDeleteHandlerFunc := ds.createFileHandlerFunc(&result) 135 producerConsumer.AddTaskWithError(fileDeleteHandlerFunc(*deleteItem), errorsQueue.AddError) 136 } 137 if err := deleteItems.GetError(); err != nil { 138 errorsQueue.AddError(err) 139 } 140 deleteItems.Reset() 141 }() 142 return ds.performTasks(producerConsumer, errorsQueue, result) 143 } 144 145 func (ds *DeleteService) performTasks(consumer parallel.Runner, errorsQueue *clientutils.ErrorsQueue, result utils.Result) (totalDeleted int, err error) { 146 consumer.Run() 147 err = errorsQueue.GetError() 148 149 totalDeleted = utils.SumIntArray(result.SuccessCount) 150 log.Debug("Deleted", strconv.Itoa(totalDeleted), "artifacts.") 151 return 152 } 153 154 type DeleteConfiguration struct { 155 ArtDetails auth.ServiceDetails 156 DryRun bool 157 } 158 159 func (conf *DeleteConfiguration) GetArtifactoryDetails() auth.ServiceDetails { 160 return conf.ArtDetails 161 } 162 163 func (conf *DeleteConfiguration) SetArtifactoryDetails(art auth.ServiceDetails) { 164 conf.ArtDetails = art 165 } 166 167 func (conf *DeleteConfiguration) IsDryRun() bool { 168 return conf.DryRun 169 } 170 171 type DeleteParams struct { 172 *utils.ArtifactoryCommonParams 173 } 174 175 func (ds *DeleteParams) GetFile() *utils.ArtifactoryCommonParams { 176 return ds.ArtifactoryCommonParams 177 } 178 179 func (ds *DeleteParams) SetIncludeDirs(includeDirs bool) { 180 ds.IncludeDirs = includeDirs 181 } 182 183 func NewDeleteParams() DeleteParams { 184 return DeleteParams{ArtifactoryCommonParams: &utils.ArtifactoryCommonParams{}} 185 } 186 187 // This function receives as an argument a reader within the list of files and dirs to be deleted from Artifactory. 188 // In case the search params used to create this list included excludeProps, we might need to remove some directories from this list. 189 // These directories must be removed, because they include files, which should not be deleted, because of the excludeProps params. 190 // These directories must not be deleted from Artifactory. 191 // In case of no excludeProps filed in the file spec, nil will be return so all deleteCandidates will get deleted. 192 func removeNotToBeDeletedDirs(specFile *utils.ArtifactoryCommonParams, ds *DeleteService, deleteCandidates *content.ContentReader) (*content.ContentReader, error) { 193 length, err := deleteCandidates.Length() 194 if err != nil || specFile.ExcludeProps == "" || length == 0 { 195 return nil, err 196 } 197 // Send AQL to get all artifacts that includes the exclude props. 198 resultWriter, err := content.NewContentWriter(content.DefaultKey, true, false) 199 if err != nil { 200 return nil, err 201 } 202 bufferFiles, err := utils.FilterCandidateToBeDeleted(deleteCandidates, resultWriter) 203 if len(bufferFiles) > 0 { 204 defer func() { 205 for _, file := range bufferFiles { 206 file.Close() 207 } 208 }() 209 artifactNotToBeDeleteReader, err := getSortedArtifactsToNotDelete(specFile, ds) 210 if err != nil { 211 return nil, err 212 } 213 defer artifactNotToBeDeleteReader.Close() 214 if err = utils.WriteCandidateDirsToBeDeleted(bufferFiles, artifactNotToBeDeleteReader, resultWriter); err != nil { 215 return nil, err 216 } 217 } 218 if err = resultWriter.Close(); err != nil { 219 return nil, err 220 } 221 return content.NewContentReader(resultWriter.GetFilePath(), content.DefaultKey), err 222 } 223 224 func getSortedArtifactsToNotDelete(specFile *utils.ArtifactoryCommonParams, ds *DeleteService) (*content.ContentReader, error) { 225 specFile.Props = specFile.ExcludeProps 226 specFile.SortOrder = "asc" 227 specFile.SortBy = []string{"repo", "path", "name"} 228 specFile.ExcludeProps = "" 229 return utils.SearchBySpecWithPattern(specFile, ds, utils.NONE) 230 }