zot-wo-auth/pkg/cli/client/search_functions.go
Alexei Dodon f58597ade9
refactor: Reduce zli binary size (#1805)
Signed-off-by: Alexei Dodon <adodon@cisco.com>
2023-09-15 15:17:01 -07:00

466 lines
11 KiB
Go

//go:build search
// +build search
package client
import (
"context"
"fmt"
"math"
"strings"
"sync"
"time"
zerr "zotregistry.io/zot/errors"
zcommon "zotregistry.io/zot/pkg/common"
)
const CveDBRetryInterval = 3
func SearchAllImages(config searchConfig) error {
username, password := getUsernameAndPassword(config.user)
imageErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go config.searchService.getAllImages(ctx, config, username, password, imageErr, &wg)
wg.Add(1)
errCh := make(chan error, 1)
go collectResults(config, &wg, imageErr, cancel, printImageTableHeader, errCh)
wg.Wait()
select {
case err := <-errCh:
return err
default:
return nil
}
}
func SearchAllImagesGQL(config searchConfig) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
imageList, err := config.searchService.getImagesGQL(ctx, config, username, password, "")
if err != nil {
return err
}
imageListData := []imageStruct{}
for _, image := range imageList.Results {
imageListData = append(imageListData, imageStruct(image))
}
return printImageResult(config, imageListData)
}
func SearchImageByName(config searchConfig, image string) error {
username, password := getUsernameAndPassword(config.user)
imageErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go config.searchService.getImageByName(ctx, config, username, password,
image, imageErr, &wg)
wg.Add(1)
errCh := make(chan error, 1)
go collectResults(config, &wg, imageErr, cancel, printImageTableHeader, errCh)
wg.Wait()
select {
case err := <-errCh:
if strings.Contains(err.Error(), "NAME_UNKNOWN") {
return zerr.ErrEmptyRepoList
}
return err
default:
return nil
}
}
func SearchImageByNameGQL(config searchConfig, imageName string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
repo, tag := zcommon.GetImageDirAndTag(imageName)
imageList, err := config.searchService.getImagesGQL(ctx, config, username, password, repo)
if err != nil {
return err
}
imageListData := []imageStruct{}
for _, image := range imageList.Results {
if tag == "" || image.Tag == tag {
imageListData = append(imageListData, imageStruct(image))
}
}
return printImageResult(config, imageListData)
}
func SearchImagesByDigest(config searchConfig, digest string) error {
username, password := getUsernameAndPassword(config.user)
imageErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go config.searchService.getImagesByDigest(ctx, config, username, password,
digest, imageErr, &wg)
wg.Add(1)
errCh := make(chan error, 1)
go collectResults(config, &wg, imageErr, cancel, printImageTableHeader, errCh)
wg.Wait()
select {
case err := <-errCh:
return err
default:
return nil
}
}
func SearchDerivedImageListGQL(config searchConfig, derivedImage string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
imageList, err := config.searchService.getDerivedImageListGQL(ctx, config, username,
password, derivedImage)
if err != nil {
return err
}
imageListData := []imageStruct{}
for _, image := range imageList.DerivedImageList.Results {
imageListData = append(imageListData, imageStruct(image))
}
return printImageResult(config, imageListData)
}
func SearchBaseImageListGQL(config searchConfig, baseImage string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
imageList, err := config.searchService.getBaseImageListGQL(ctx, config, username,
password, baseImage)
if err != nil {
return err
}
imageListData := []imageStruct{}
for _, image := range imageList.BaseImageList.Results {
imageListData = append(imageListData, imageStruct(image))
}
return printImageResult(config, imageListData)
}
func SearchImagesForDigestGQL(config searchConfig, digest string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
imageList, err := config.searchService.getImagesForDigestGQL(ctx, config, username, password, digest)
if err != nil {
return err
}
imageListData := []imageStruct{}
for _, image := range imageList.Results {
imageListData = append(imageListData, imageStruct(image))
}
if err := printImageResult(config, imageListData); err != nil {
return err
}
return nil
}
func SearchCVEForImageGQL(config searchConfig, image, searchedCveID string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var cveList *cveResult
err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error {
var err error
cveList, err = config.searchService.getCveByImageGQL(ctx, config, username, password, image, searchedCveID)
if err != nil {
if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) {
cancel()
return err
}
fmt.Fprintf(config.resultWriter,
"[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds()))
}
return err
}, maxRetries, CveDBRetryInterval*time.Second)
if err != nil {
return err
}
if len(cveList.Data.CVEListForImage.CVEList) == 0 {
fmt.Fprint(config.resultWriter, "No CVEs found for image\n")
return nil
}
var builder strings.Builder
if config.outputFormat == defaultOutputFormat || config.outputFormat == "" {
printCVETableHeader(&builder)
fmt.Fprint(config.resultWriter, builder.String())
}
out, err := cveList.string(config.outputFormat)
if err != nil {
return err
}
fmt.Fprint(config.resultWriter, out)
return nil
}
func SearchImagesByCVEIDGQL(config searchConfig, repo, cveid string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var imageList *zcommon.ImagesForCve
err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error {
var err error
imageList, err = config.searchService.getTagsForCVEGQL(ctx, config, username, password,
repo, cveid)
if err != nil {
if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) {
cancel()
return err
}
fmt.Fprintf(config.resultWriter,
"[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds()))
}
return err
}, maxRetries, CveDBRetryInterval*time.Second)
if err != nil {
return err
}
imageListData := []imageStruct{}
for _, image := range imageList.Results {
imageListData = append(imageListData, imageStruct(image))
}
return printImageResult(config, imageListData)
}
func SearchFixedTagsGQL(config searchConfig, repo, cveid string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var fixedTags *zcommon.ImageListWithCVEFixedResponse
err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error {
var err error
fixedTags, err = config.searchService.getFixedTagsForCVEGQL(ctx, config, username, password,
repo, cveid)
if err != nil {
if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) {
cancel()
return err
}
fmt.Fprintf(config.resultWriter,
"[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds()))
}
return err
}, maxRetries, CveDBRetryInterval*time.Second)
if err != nil {
return err
}
imageList := make([]imageStruct, 0, len(fixedTags.Results))
for _, image := range fixedTags.Results {
imageList = append(imageList, imageStruct(image))
}
return printImageResult(config, imageList)
}
func GlobalSearchGQL(config searchConfig, query string) error {
username, password := getUsernameAndPassword(config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
globalSearchResult, err := config.searchService.globalSearchGQL(ctx, config, username, password, query)
if err != nil {
return err
}
imagesList := []imageStruct{}
for _, image := range globalSearchResult.Images {
imagesList = append(imagesList, imageStruct(image))
}
reposList := []repoStruct{}
for _, repo := range globalSearchResult.Repos {
reposList = append(reposList, repoStruct(repo))
}
if err := printImageResult(config, imagesList); err != nil {
return err
}
return printRepoResults(config, reposList)
}
func SearchReferrersGQL(config searchConfig, subject string) error {
username, password := getUsernameAndPassword(config.user)
repo, ref, refIsTag, err := zcommon.GetRepoReference(subject)
if err != nil {
return err
}
digest := ref
if refIsTag {
digest, err = fetchImageDigest(repo, ref, username, password, config)
if err != nil {
return err
}
}
response, err := config.searchService.getReferrersGQL(context.Background(), config, username, password, repo, digest)
if err != nil {
return err
}
referrersList := referrersResult(response.Referrers)
maxArtifactTypeLen := math.MinInt
for _, referrer := range referrersList {
if maxArtifactTypeLen < len(referrer.ArtifactType) {
maxArtifactTypeLen = len(referrer.ArtifactType)
}
}
printReferrersTableHeader(config, config.resultWriter, maxArtifactTypeLen)
return printReferrersResult(config, referrersList, maxArtifactTypeLen)
}
func SearchReferrers(config searchConfig, subject string) error {
username, password := getUsernameAndPassword(config.user)
repo, ref, refIsTag, err := zcommon.GetRepoReference(subject)
if err != nil {
return err
}
digest := ref
if refIsTag {
digest, err = fetchImageDigest(repo, ref, username, password, config)
if err != nil {
return err
}
}
referrersList, err := config.searchService.getReferrers(context.Background(), config, username, password,
repo, digest)
if err != nil {
return err
}
maxArtifactTypeLen := math.MinInt
for _, referrer := range referrersList {
if maxArtifactTypeLen < len(referrer.ArtifactType) {
maxArtifactTypeLen = len(referrer.ArtifactType)
}
}
printReferrersTableHeader(config, config.resultWriter, maxArtifactTypeLen)
return printReferrersResult(config, referrersList, maxArtifactTypeLen)
}
func SearchRepos(config searchConfig) error {
username, password := getUsernameAndPassword(config.user)
repoErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go config.searchService.getRepos(ctx, config, username, password, repoErr, &wg)
wg.Add(1)
errCh := make(chan error, 1)
go collectResults(config, &wg, repoErr, cancel, printImageTableHeader, errCh)
wg.Wait()
select {
case err := <-errCh:
return err
default:
return nil
}
}