zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/cli/client/search_functions.go (about) 1 //go:build search 2 // +build search 3 4 package client 5 6 import ( 7 "context" 8 "fmt" 9 "math" 10 "strings" 11 "sync" 12 "time" 13 14 zerr "zotregistry.io/zot/errors" 15 zcommon "zotregistry.io/zot/pkg/common" 16 ) 17 18 const CveDBRetryInterval = 3 19 20 func SearchAllImages(config SearchConfig) error { 21 username, password := getUsernameAndPassword(config.User) 22 imageErr := make(chan stringResult) 23 ctx, cancel := context.WithCancel(context.Background()) 24 25 var wg sync.WaitGroup 26 27 wg.Add(1) 28 29 go config.SearchService.getAllImages(ctx, config, username, password, imageErr, &wg) 30 wg.Add(1) 31 32 errCh := make(chan error, 1) 33 34 go collectResults(config, &wg, imageErr, cancel, printImageTableHeader, errCh) 35 wg.Wait() 36 select { 37 case err := <-errCh: 38 return err 39 default: 40 return nil 41 } 42 } 43 44 func SearchAllImagesGQL(config SearchConfig) error { 45 username, password := getUsernameAndPassword(config.User) 46 ctx, cancel := context.WithCancel(context.Background()) 47 48 defer cancel() 49 50 imageList, err := config.SearchService.getImagesGQL(ctx, config, username, password, "") 51 if err != nil { 52 return err 53 } 54 55 imageListData := []imageStruct{} 56 57 for _, image := range imageList.Results { 58 imageListData = append(imageListData, imageStruct(image)) 59 } 60 61 return printImageResult(config, imageListData) 62 } 63 64 func SearchImageByName(config SearchConfig, image string) error { 65 username, password := getUsernameAndPassword(config.User) 66 imageErr := make(chan stringResult) 67 ctx, cancel := context.WithCancel(context.Background()) 68 69 var wg sync.WaitGroup 70 71 wg.Add(1) 72 73 go config.SearchService.getImageByName(ctx, config, username, password, 74 image, imageErr, &wg) 75 wg.Add(1) 76 77 errCh := make(chan error, 1) 78 go collectResults(config, &wg, imageErr, cancel, printImageTableHeader, errCh) 79 80 wg.Wait() 81 82 select { 83 case err := <-errCh: 84 if strings.Contains(err.Error(), "NAME_UNKNOWN") { 85 return zerr.ErrEmptyRepoList 86 } 87 88 return err 89 default: 90 return nil 91 } 92 } 93 94 func SearchImageByNameGQL(config SearchConfig, imageName string) error { 95 username, password := getUsernameAndPassword(config.User) 96 ctx, cancel := context.WithCancel(context.Background()) 97 98 defer cancel() 99 100 repo, tag := zcommon.GetImageDirAndTag(imageName) 101 102 imageList, err := config.SearchService.getImagesGQL(ctx, config, username, password, repo) 103 if err != nil { 104 return err 105 } 106 107 imageListData := []imageStruct{} 108 109 for _, image := range imageList.Results { 110 if tag == "" || image.Tag == tag { 111 imageListData = append(imageListData, imageStruct(image)) 112 } 113 } 114 115 return printImageResult(config, imageListData) 116 } 117 118 func SearchImagesByDigest(config SearchConfig, digest string) error { 119 username, password := getUsernameAndPassword(config.User) 120 imageErr := make(chan stringResult) 121 ctx, cancel := context.WithCancel(context.Background()) 122 123 var wg sync.WaitGroup 124 125 wg.Add(1) 126 127 go config.SearchService.getImagesByDigest(ctx, config, username, password, 128 digest, imageErr, &wg) 129 wg.Add(1) 130 131 errCh := make(chan error, 1) 132 go collectResults(config, &wg, imageErr, cancel, printImageTableHeader, errCh) 133 134 wg.Wait() 135 136 select { 137 case err := <-errCh: 138 return err 139 default: 140 return nil 141 } 142 } 143 144 func SearchDerivedImageListGQL(config SearchConfig, derivedImage string) error { 145 username, password := getUsernameAndPassword(config.User) 146 ctx, cancel := context.WithCancel(context.Background()) 147 148 defer cancel() 149 150 imageList, err := config.SearchService.getDerivedImageListGQL(ctx, config, username, 151 password, derivedImage) 152 if err != nil { 153 return err 154 } 155 156 imageListData := []imageStruct{} 157 158 for _, image := range imageList.DerivedImageList.Results { 159 imageListData = append(imageListData, imageStruct(image)) 160 } 161 162 return printImageResult(config, imageListData) 163 } 164 165 func SearchBaseImageListGQL(config SearchConfig, baseImage string) error { 166 username, password := getUsernameAndPassword(config.User) 167 ctx, cancel := context.WithCancel(context.Background()) 168 169 defer cancel() 170 171 imageList, err := config.SearchService.getBaseImageListGQL(ctx, config, username, 172 password, baseImage) 173 if err != nil { 174 return err 175 } 176 177 imageListData := []imageStruct{} 178 179 for _, image := range imageList.BaseImageList.Results { 180 imageListData = append(imageListData, imageStruct(image)) 181 } 182 183 return printImageResult(config, imageListData) 184 } 185 186 func SearchImagesForDigestGQL(config SearchConfig, digest string) error { 187 username, password := getUsernameAndPassword(config.User) 188 ctx, cancel := context.WithCancel(context.Background()) 189 190 defer cancel() 191 192 imageList, err := config.SearchService.getImagesForDigestGQL(ctx, config, username, password, digest) 193 if err != nil { 194 return err 195 } 196 197 imageListData := []imageStruct{} 198 199 for _, image := range imageList.Results { 200 imageListData = append(imageListData, imageStruct(image)) 201 } 202 203 if err := printImageResult(config, imageListData); err != nil { 204 return err 205 } 206 207 return nil 208 } 209 210 func SearchCVEForImageGQL(config SearchConfig, image, searchedCveID string) error { 211 username, password := getUsernameAndPassword(config.User) 212 ctx, cancel := context.WithCancel(context.Background()) 213 214 defer cancel() 215 216 var cveList *cveResult 217 218 err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error { 219 var err error 220 221 cveList, err = config.SearchService.getCveByImageGQL(ctx, config, username, password, image, searchedCveID) 222 if err != nil { 223 if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) { 224 cancel() 225 226 return err 227 } 228 229 fmt.Fprintf(config.ResultWriter, 230 "[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds())) 231 } 232 233 return err 234 }, maxRetries, CveDBRetryInterval*time.Second) 235 if err != nil { 236 return err 237 } 238 239 if len(cveList.Data.CVEListForImage.CVEList) == 0 { 240 fmt.Fprint(config.ResultWriter, "No CVEs found for image\n") 241 242 return nil 243 } 244 245 var builder strings.Builder 246 247 if config.OutputFormat == defaultOutputFormat || config.OutputFormat == "" { 248 printCVETableHeader(&builder) 249 fmt.Fprint(config.ResultWriter, builder.String()) 250 } 251 252 out, err := cveList.string(config.OutputFormat) 253 if err != nil { 254 return err 255 } 256 257 fmt.Fprint(config.ResultWriter, out) 258 259 return nil 260 } 261 262 func SearchImagesByCVEIDGQL(config SearchConfig, repo, cveid string) error { 263 username, password := getUsernameAndPassword(config.User) 264 ctx, cancel := context.WithCancel(context.Background()) 265 266 defer cancel() 267 268 var imageList *zcommon.ImagesForCve 269 270 err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error { 271 var err error 272 273 imageList, err = config.SearchService.getTagsForCVEGQL(ctx, config, username, password, 274 repo, cveid) 275 if err != nil { 276 if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) { 277 cancel() 278 279 return err 280 } 281 282 fmt.Fprintf(config.ResultWriter, 283 "[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds())) 284 } 285 286 return err 287 }, maxRetries, CveDBRetryInterval*time.Second) 288 if err != nil { 289 return err 290 } 291 292 imageListData := []imageStruct{} 293 294 for _, image := range imageList.Results { 295 imageListData = append(imageListData, imageStruct(image)) 296 } 297 298 return printImageResult(config, imageListData) 299 } 300 301 func SearchFixedTagsGQL(config SearchConfig, repo, cveid string) error { 302 username, password := getUsernameAndPassword(config.User) 303 ctx, cancel := context.WithCancel(context.Background()) 304 305 defer cancel() 306 307 var fixedTags *zcommon.ImageListWithCVEFixedResponse 308 309 err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error { 310 var err error 311 312 fixedTags, err = config.SearchService.getFixedTagsForCVEGQL(ctx, config, username, password, 313 repo, cveid) 314 if err != nil { 315 if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) { 316 cancel() 317 318 return err 319 } 320 321 fmt.Fprintf(config.ResultWriter, 322 "[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds())) 323 } 324 325 return err 326 }, maxRetries, CveDBRetryInterval*time.Second) 327 if err != nil { 328 return err 329 } 330 331 imageList := make([]imageStruct, 0, len(fixedTags.Results)) 332 333 for _, image := range fixedTags.Results { 334 imageList = append(imageList, imageStruct(image)) 335 } 336 337 return printImageResult(config, imageList) 338 } 339 340 func GlobalSearchGQL(config SearchConfig, query string) error { 341 username, password := getUsernameAndPassword(config.User) 342 ctx, cancel := context.WithCancel(context.Background()) 343 344 defer cancel() 345 346 globalSearchResult, err := config.SearchService.globalSearchGQL(ctx, config, username, password, query) 347 if err != nil { 348 return err 349 } 350 351 imagesList := []imageStruct{} 352 353 for _, image := range globalSearchResult.Images { 354 imagesList = append(imagesList, imageStruct(image)) 355 } 356 357 reposList := []repoStruct{} 358 359 for _, repo := range globalSearchResult.Repos { 360 reposList = append(reposList, repoStruct(repo)) 361 } 362 363 if err := printImageResult(config, imagesList); err != nil { 364 return err 365 } 366 367 return printRepoResults(config, reposList) 368 } 369 370 func SearchReferrersGQL(config SearchConfig, subject string) error { 371 username, password := getUsernameAndPassword(config.User) 372 373 repo, ref, refIsTag, err := zcommon.GetRepoReference(subject) 374 if err != nil { 375 return err 376 } 377 378 digest := ref 379 380 if refIsTag { 381 digest, err = fetchImageDigest(repo, ref, username, password, config) 382 if err != nil { 383 return err 384 } 385 } 386 387 response, err := config.SearchService.getReferrersGQL(context.Background(), config, username, password, repo, digest) 388 if err != nil { 389 return err 390 } 391 392 referrersList := referrersResult(response.Referrers) 393 394 maxArtifactTypeLen := math.MinInt 395 396 for _, referrer := range referrersList { 397 if maxArtifactTypeLen < len(referrer.ArtifactType) { 398 maxArtifactTypeLen = len(referrer.ArtifactType) 399 } 400 } 401 402 printReferrersTableHeader(config, config.ResultWriter, maxArtifactTypeLen) 403 404 return printReferrersResult(config, referrersList, maxArtifactTypeLen) 405 } 406 407 func SearchReferrers(config SearchConfig, subject string) error { 408 username, password := getUsernameAndPassword(config.User) 409 410 repo, ref, refIsTag, err := zcommon.GetRepoReference(subject) 411 if err != nil { 412 return err 413 } 414 415 digest := ref 416 417 if refIsTag { 418 digest, err = fetchImageDigest(repo, ref, username, password, config) 419 if err != nil { 420 return err 421 } 422 } 423 424 referrersList, err := config.SearchService.getReferrers(context.Background(), config, username, password, 425 repo, digest) 426 if err != nil { 427 return err 428 } 429 430 maxArtifactTypeLen := math.MinInt 431 432 for _, referrer := range referrersList { 433 if maxArtifactTypeLen < len(referrer.ArtifactType) { 434 maxArtifactTypeLen = len(referrer.ArtifactType) 435 } 436 } 437 438 printReferrersTableHeader(config, config.ResultWriter, maxArtifactTypeLen) 439 440 return printReferrersResult(config, referrersList, maxArtifactTypeLen) 441 } 442 443 func SearchRepos(config SearchConfig) error { 444 username, password := getUsernameAndPassword(config.User) 445 repoErr := make(chan stringResult) 446 ctx, cancel := context.WithCancel(context.Background()) 447 448 var wg sync.WaitGroup 449 450 wg.Add(1) 451 452 go config.SearchService.getRepos(ctx, config, username, password, repoErr, &wg) 453 wg.Add(1) 454 455 errCh := make(chan error, 1) 456 457 go collectResults(config, &wg, repoErr, cancel, printImageTableHeader, errCh) 458 wg.Wait() 459 select { 460 case err := <-errCh: 461 return err 462 default: 463 return nil 464 } 465 }