0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-30 22:34:13 -05:00

refactor(test): make sure cli tests are not internal unless they need to be (#1878)

As part of this change searchConfig needed to be exported,
as it was passed as a parameter to exported functions

At this moment most of the tests remaining internal depend on the mock service.
The interface it implements has unexported methods.

Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
Andrei Aaron 2023-10-03 21:15:39 +03:00 committed by GitHub
parent 99e29c0f46
commit ca1c3288cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 3227 additions and 3118 deletions

View file

@ -179,7 +179,7 @@ type httpJob struct {
password string password string
imageName string imageName string
tagName string tagName string
config searchConfig config SearchConfig
} }
const rateLimiterBuffer = 5000 const rateLimiterBuffer = 5000
@ -218,8 +218,8 @@ func (p *requestsPool) doJob(ctx context.Context, job *httpJob) {
defer p.wtgrp.Done() defer p.wtgrp.Done()
// Check manifest media type // Check manifest media type
header, err := makeHEADRequest(ctx, job.url, job.username, job.password, job.config.verifyTLS, header, err := makeHEADRequest(ctx, job.url, job.username, job.password, job.config.VerifyTLS,
job.config.debug) job.config.Debug)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -227,7 +227,7 @@ func (p *requestsPool) doJob(ctx context.Context, job *httpJob) {
p.outputCh <- stringResult{"", err} p.outputCh <- stringResult{"", err}
} }
verbose := job.config.verbose verbose := job.config.Verbose
switch header.Get("Content-Type") { switch header.Get("Content-Type") {
case ispec.MediaTypeImageManifest: case ispec.MediaTypeImageManifest:
@ -242,7 +242,7 @@ func (p *requestsPool) doJob(ctx context.Context, job *httpJob) {
} }
platformStr := getPlatformStr(image.Manifests[0].Platform) platformStr := getPlatformStr(image.Manifests[0].Platform)
str, err := image.string(job.config.outputFormat, len(job.imageName), len(job.tagName), len(platformStr), verbose) str, err := image.string(job.config.OutputFormat, len(job.imageName), len(job.tagName), len(platformStr), verbose)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -270,7 +270,7 @@ func (p *requestsPool) doJob(ctx context.Context, job *httpJob) {
platformStr := getPlatformStr(image.Manifests[0].Platform) platformStr := getPlatformStr(image.Manifests[0].Platform)
str, err := image.string(job.config.outputFormat, len(job.imageName), len(job.tagName), len(platformStr), verbose) str, err := image.string(job.config.OutputFormat, len(job.imageName), len(job.tagName), len(platformStr), verbose)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -294,7 +294,7 @@ func fetchImageIndexStruct(ctx context.Context, job *httpJob) (*imageStruct, err
var indexContent ispec.Index var indexContent ispec.Index
header, err := makeGETRequest(ctx, job.url, job.username, job.password, header, err := makeGETRequest(ctx, job.url, job.username, job.password,
job.config.verifyTLS, job.config.debug, &indexContent, job.config.resultWriter) job.config.VerifyTLS, job.config.Debug, &indexContent, job.config.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return nil, context.Canceled return nil, context.Canceled
@ -376,16 +376,16 @@ func fetchImageManifestStruct(ctx context.Context, job *httpJob) (*imageStruct,
}, nil }, nil
} }
func fetchManifestStruct(ctx context.Context, repo, manifestReference string, searchConf searchConfig, func fetchManifestStruct(ctx context.Context, repo, manifestReference string, searchConf SearchConfig,
username, password string, username, password string,
) (common.ManifestSummary, error) { ) (common.ManifestSummary, error) {
manifestResp := ispec.Manifest{} manifestResp := ispec.Manifest{}
URL := fmt.Sprintf("%s/v2/%s/manifests/%s", URL := fmt.Sprintf("%s/v2/%s/manifests/%s",
searchConf.servURL, repo, manifestReference) searchConf.ServURL, repo, manifestReference)
header, err := makeGETRequest(ctx, URL, username, password, header, err := makeGETRequest(ctx, URL, username, password,
searchConf.verifyTLS, searchConf.debug, &manifestResp, searchConf.resultWriter) searchConf.VerifyTLS, searchConf.Debug, &manifestResp, searchConf.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return common.ManifestSummary{}, context.Canceled return common.ManifestSummary{}, context.Canceled
@ -465,16 +465,16 @@ func fetchManifestStruct(ctx context.Context, repo, manifestReference string, se
}, nil }, nil
} }
func fetchConfig(ctx context.Context, repo, configDigest string, searchConf searchConfig, func fetchConfig(ctx context.Context, repo, configDigest string, searchConf SearchConfig,
username, password string, username, password string,
) (ispec.Image, error) { ) (ispec.Image, error) {
configContent := ispec.Image{} configContent := ispec.Image{}
URL := fmt.Sprintf("%s/v2/%s/blobs/%s", URL := fmt.Sprintf("%s/v2/%s/blobs/%s",
searchConf.servURL, repo, configDigest) searchConf.ServURL, repo, configDigest)
_, err := makeGETRequest(ctx, URL, username, password, _, err := makeGETRequest(ctx, URL, username, password,
searchConf.verifyTLS, searchConf.debug, &configContent, searchConf.resultWriter) searchConf.VerifyTLS, searchConf.Debug, &configContent, searchConf.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return ispec.Image{}, context.Canceled return ispec.Image{}, context.Canceled
@ -486,16 +486,16 @@ func fetchConfig(ctx context.Context, repo, configDigest string, searchConf sear
return configContent, nil return configContent, nil
} }
func isNotationSigned(ctx context.Context, repo, digestStr string, searchConf searchConfig, func isNotationSigned(ctx context.Context, repo, digestStr string, searchConf SearchConfig,
username, password string, username, password string,
) bool { ) bool {
var referrers ispec.Index var referrers ispec.Index
URL := fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s", URL := fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s",
searchConf.servURL, repo, digestStr, common.ArtifactTypeNotation) searchConf.ServURL, repo, digestStr, common.ArtifactTypeNotation)
_, err := makeGETRequest(ctx, URL, username, password, _, err := makeGETRequest(ctx, URL, username, password,
searchConf.verifyTLS, searchConf.debug, &referrers, searchConf.resultWriter) searchConf.VerifyTLS, searchConf.Debug, &referrers, searchConf.ResultWriter)
if err != nil { if err != nil {
return false return false
} }
@ -507,16 +507,16 @@ func isNotationSigned(ctx context.Context, repo, digestStr string, searchConf se
return false return false
} }
func isCosignSigned(ctx context.Context, repo, digestStr string, searchConf searchConfig, func isCosignSigned(ctx context.Context, repo, digestStr string, searchConf SearchConfig,
username, password string, username, password string,
) bool { ) bool {
var result interface{} var result interface{}
cosignTag := strings.Replace(digestStr, ":", "-", 1) + "." + remote.SignatureTagSuffix cosignTag := strings.Replace(digestStr, ":", "-", 1) + "." + remote.SignatureTagSuffix
URL := fmt.Sprintf("%s/v2/%s/manifests/%s", searchConf.servURL, repo, cosignTag) URL := fmt.Sprintf("%s/v2/%s/manifests/%s", searchConf.ServURL, repo, cosignTag)
_, err := makeGETRequest(ctx, URL, username, password, searchConf.verifyTLS, _, err := makeGETRequest(ctx, URL, username, password, searchConf.VerifyTLS,
searchConf.debug, &result, searchConf.resultWriter) searchConf.Debug, &result, searchConf.ResultWriter)
return err == nil return err == nil
} }

View file

@ -1,7 +1,7 @@
//go:build search //go:build search
// +build search // +build search
package client package client_test
import ( import (
"bytes" "bytes"
@ -9,6 +9,7 @@ import (
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"os" "os"
"path"
"path/filepath" "path/filepath"
"testing" "testing"
@ -18,6 +19,7 @@ import (
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants" "zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/cli/client"
extConf "zotregistry.io/zot/pkg/extensions/config" extConf "zotregistry.io/zot/pkg/extensions/config"
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
) )
@ -91,7 +93,7 @@ func TestTLSWithAuth(t *testing.T) {
defer os.RemoveAll(destCertsDir) defer os.RemoveAll(destCertsDir)
args := []string{"name", "dummyImageName", "--url", HOST1} args := []string{"name", "dummyImageName", "--url", HOST1}
imageCmd := NewImageCommand(new(searchService)) imageCmd := client.NewImageCommand(client.NewSearchService())
imageBuff := bytes.NewBufferString("") imageBuff := bytes.NewBufferString("")
imageCmd.SetOut(imageBuff) imageCmd.SetOut(imageBuff)
imageCmd.SetErr(imageBuff) imageCmd.SetErr(imageBuff)
@ -105,7 +107,7 @@ func TestTLSWithAuth(t *testing.T) {
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`, fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix)) BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath) defer os.Remove(configPath)
imageCmd = NewImageCommand(new(searchService)) imageCmd = client.NewImageCommand(client.NewSearchService())
imageBuff = bytes.NewBufferString("") imageBuff = bytes.NewBufferString("")
imageCmd.SetOut(imageBuff) imageCmd.SetOut(imageBuff)
imageCmd.SetErr(imageBuff) imageCmd.SetErr(imageBuff)
@ -120,7 +122,7 @@ func TestTLSWithAuth(t *testing.T) {
fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`, fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s%s%s","showspinner":false}]}`,
BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix)) BaseSecureURL1, constants.RoutePrefix, constants.ExtCatalogPrefix))
defer os.Remove(configPath) defer os.Remove(configPath)
imageCmd = NewImageCommand(new(searchService)) imageCmd = client.NewImageCommand(client.NewSearchService())
imageBuff = bytes.NewBufferString("") imageBuff = bytes.NewBufferString("")
imageCmd.SetOut(imageBuff) imageCmd.SetOut(imageBuff)
imageCmd.SetErr(imageBuff) imageCmd.SetErr(imageBuff)
@ -174,7 +176,7 @@ func TestTLSWithoutAuth(t *testing.T) {
defer os.RemoveAll(destCertsDir) defer os.RemoveAll(destCertsDir)
args := []string{"list", "--config", "imagetest"} args := []string{"list", "--config", "imagetest"}
imageCmd := NewImageCommand(new(searchService)) imageCmd := client.NewImageCommand(client.NewSearchService())
imageBuff := bytes.NewBufferString("") imageBuff := bytes.NewBufferString("")
imageCmd.SetOut(imageBuff) imageCmd.SetOut(imageBuff)
imageCmd.SetErr(imageBuff) imageCmd.SetErr(imageBuff)
@ -215,7 +217,7 @@ func TestTLSBadCerts(t *testing.T) {
defer os.Remove(configPath) defer os.Remove(configPath)
args := []string{"list", "--config", "imagetest"} args := []string{"list", "--config", "imagetest"}
imageCmd := NewImageCommand(new(searchService)) imageCmd := client.NewImageCommand(client.NewSearchService())
imageBuff := bytes.NewBufferString("") imageBuff := bytes.NewBufferString("")
imageCmd.SetOut(imageBuff) imageCmd.SetOut(imageBuff)
imageCmd.SetErr(imageBuff) imageCmd.SetErr(imageBuff)
@ -226,3 +228,20 @@ func TestTLSBadCerts(t *testing.T) {
}) })
}) })
} }
func makeConfigFile(content string) string {
os.Setenv("HOME", os.TempDir())
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
configPath := path.Join(home, "/.zot")
if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil {
panic(err)
}
return configPath
}

View file

@ -1,7 +1,7 @@
//go:build search //go:build search
// +build search // +build search
package client package client_test
import ( import (
"bytes" "bytes"
@ -14,6 +14,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
zerr "zotregistry.io/zot/errors" zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/cli/client"
) )
func TestConfigCmdBasics(t *testing.T) { func TestConfigCmdBasics(t *testing.T) {
@ -21,7 +22,7 @@ func TestConfigCmdBasics(t *testing.T) {
args := []string{"--help"} args := []string{"--help"}
configPath := makeConfigFile("showspinner = false") configPath := makeConfigFile("showspinner = false")
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -33,7 +34,7 @@ func TestConfigCmdBasics(t *testing.T) {
args[0] = "-h" args[0] = "-h"
configPath := makeConfigFile("showspinner = false") configPath := makeConfigFile("showspinner = false")
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -48,7 +49,7 @@ func TestConfigCmdBasics(t *testing.T) {
args := []string{} args := []string{}
configPath := makeConfigFile("showspinner = false") configPath := makeConfigFile("showspinner = false")
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -64,7 +65,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"add", "configtest1", "https://test-url.com"} args := []string{"add", "configtest1", "https://test-url.com"}
file := makeConfigFile("") file := makeConfigFile("")
defer os.Remove(file) defer os.Remove(file)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -90,7 +91,7 @@ func TestConfigCmdMain(t *testing.T) {
panic(err) panic(err)
} }
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -118,7 +119,7 @@ func TestConfigCmdMain(t *testing.T) {
panic(err) panic(err)
} }
cmd := NewConfigAddCommand() cmd := client.NewConfigAddCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -140,7 +141,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"--list"} args := []string{"--list"}
configPath := makeConfigFile(`{"configs":{"_name":"configtest","url":"https://test-url.com","showspinner":false}}`) configPath := makeConfigFile(`{"configs":{"_name":"configtest","url":"https://test-url.com","showspinner":false}}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -153,7 +154,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"add", "configtest1", "test..com"} args := []string{"add", "configtest1", "test..com"}
file := makeConfigFile("") file := makeConfigFile("")
defer os.Remove(file) defer os.Remove(file)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -167,7 +168,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"remove", "configtest"} args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -185,7 +186,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"remove", "configtest"} args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[]`) configPath := makeConfigFile(`{"configs":[]`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -199,7 +200,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"remove", "configtest"} args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"asdf":[]`) configPath := makeConfigFile(`{"asdf":[]`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -213,7 +214,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"remove", "configtest"} args := []string{"remove", "configtest"}
configPath := makeConfigFile(`{"configs":[asdad]`) configPath := makeConfigFile(`{"configs":[asdad]`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -232,7 +233,7 @@ func TestConfigCmdMain(t *testing.T) {
}() }()
err := os.Chmod(configPath, 0o400) // Read-only, so we fail only on updating the file, not reading err := os.Chmod(configPath, 0o400) // Read-only, so we fail only on updating the file, not reading
So(err, ShouldBeNil) So(err, ShouldBeNil)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -246,7 +247,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"--list"} args := []string{"--list"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -259,7 +260,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"-l"} args := []string{"-l"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -273,7 +274,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"-l"} args := []string{"-l"}
configPath := makeConfigFile(``) configPath := makeConfigFile(``)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -288,7 +289,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "--list"} args := []string{"configtest", "--list"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -302,7 +303,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "-l"} args := []string{"configtest", "-l"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -317,7 +318,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "-l"} args := []string{"configtest", "-l"}
configPath := makeConfigFile(``) configPath := makeConfigFile(``)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -332,7 +333,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "url"} args := []string{"configtest", "url"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -345,7 +346,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "url"} args := []string{"configtest", "url"}
configPath := makeConfigFile(``) configPath := makeConfigFile(``)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -360,7 +361,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "showspinner", "false"} args := []string{"configtest", "showspinner", "false"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com"}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com"}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -381,7 +382,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "showspinner", "false"} args := []string{"configtest", "showspinner", "false"}
configPath := makeConfigFile(``) configPath := makeConfigFile(``)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -396,7 +397,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "url", "https://new-url.com"} args := []string{"configtest", "url", "https://new-url.com"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -419,7 +420,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "showspinner", "--reset"} args := []string{"configtest", "showspinner", "--reset"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -441,7 +442,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"configtest", "url", "--reset"} args := []string{"configtest", "url", "--reset"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -455,7 +456,7 @@ func TestConfigCmdMain(t *testing.T) {
args := []string{"add", "configtest", "https://test-url.com/new"} args := []string{"add", "configtest", "https://test-url.com/new"}
configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`) configPath := makeConfigFile(`{"configs":[{"_name":"configtest","url":"https://test-url.com","showspinner":false}]}`)
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewConfigCommand() cmd := client.NewConfigCommand()
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)

View file

@ -6,22 +6,14 @@ package client
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"net/http"
"os" "os"
"path"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"time"
regTypes "github.com/google/go-containerregistry/pkg/v1/types"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
zerr "zotregistry.io/zot/errors" zerr "zotregistry.io/zot/errors"
@ -29,18 +21,7 @@ import (
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
zcommon "zotregistry.io/zot/pkg/common" zcommon "zotregistry.io/zot/pkg/common"
extconf "zotregistry.io/zot/pkg/extensions/config" extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/log"
mTypes "zotregistry.io/zot/pkg/meta/types"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/storage/local"
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
"zotregistry.io/zot/pkg/test/deprecated"
. "zotregistry.io/zot/pkg/test/image-utils"
"zotregistry.io/zot/pkg/test/mocks"
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
) )
func TestSearchCVECmd(t *testing.T) { func TestSearchCVECmd(t *testing.T) {
@ -397,659 +378,6 @@ func TestSearchCVECmd(t *testing.T) {
}) })
} }
func TestNegativeServerResponse(t *testing.T) {
Convey("Test from real server without search endpoint", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
srcStorageCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
err := WriteImageToFileSystem(CreateDefaultVulnerableImage(), "zot-cve-test", "0.0.1", srcStorageCtlr)
So(err, ShouldBeNil)
conf.Storage.RootDirectory = dir
trivyConfig := &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
}
cveConfig := &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: trivyConfig,
}
defaultVal := false
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: cveConfig,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
if err != nil {
panic(err)
}
logPath := logFile.Name()
defer os.Remove(logPath)
writers := io.MultiWriter(os.Stdout, logFile)
ctlr := api.NewController(conf)
ctlr.Log.Logger = ctlr.Log.Output(writers)
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
_, err = test.ReadLogFileAndSearchString(logPath, "CVE config not provided, skipping CVE update", 90*time.Second)
if err != nil {
panic(err)
}
Convey("Status Code Not Found", func() {
args := []string{"list", "zot-cve-test:0.0.1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, zerr.ErrExtensionNotEnabled.Error())
})
})
Convey("Test non-existing manifest blob", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
imageStore := local.NewImageStore(dir, false, false,
log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), nil, nil)
storeController := storage.StoreController{
DefaultStore: imageStore,
}
num := 10
config, layers, manifest, err := deprecated.GetRandomImageComponents(num) //nolint:staticcheck
So(err, ShouldBeNil)
err = WriteImageToFileSystem(
Image{
Manifest: manifest,
Layers: layers,
Config: config,
}, "zot-cve-test", "0.0.1", storeController,
)
So(err, ShouldBeNil)
err = os.RemoveAll(path.Join(dir, "zot-cve-test/blobs"))
if err != nil {
panic(err)
}
conf.Storage.RootDirectory = dir
trivyConfig := &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
}
cveConfig := &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: trivyConfig,
}
defaultVal := true
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: cveConfig,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
if err != nil {
panic(err)
}
logPath := logFile.Name()
defer os.Remove(logPath)
writers := io.MultiWriter(os.Stdout, logFile)
ctlr := api.NewController(conf)
ctlr.Log.Logger = ctlr.Log.Output(writers)
ctx := context.Background()
if err := ctlr.Init(ctx); err != nil {
panic(err)
}
ctlr.CveScanner = getMockCveScanner(ctlr.MetaDB)
go func() {
if err := ctlr.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
panic(err)
}
}()
defer ctlr.Shutdown()
test.WaitTillServerReady(url)
_, err = test.ReadLogFileAndSearchString(logPath, "DB update completed, next update scheduled", 90*time.Second)
if err != nil {
panic(err)
}
args := []string{"fixed", "zot-cve-test", "CVE-2019-9923", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
})
}
//nolint:dupl
func TestServerCVEResponse(t *testing.T) {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
conf.Storage.RootDirectory = dir
trivyConfig := &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
}
cveConfig := &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: trivyConfig,
}
defaultVal := true
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: cveConfig,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
if err != nil {
panic(err)
}
logPath := logFile.Name()
defer os.Remove(logPath)
writers := io.MultiWriter(os.Stdout, logFile)
ctlr := api.NewController(conf)
ctlr.Log.Logger = ctlr.Log.Output(writers)
ctx := context.Background()
if err := ctlr.Init(ctx); err != nil {
panic(err)
}
ctlr.CveScanner = getMockCveScanner(ctlr.MetaDB)
go func() {
if err := ctlr.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
panic(err)
}
}()
defer ctlr.Shutdown()
test.WaitTillServerReady(url)
config, layers, manifest, err := deprecated.GetImageComponents(100) //nolint:staticcheck
if err != nil {
panic(err)
}
err = UploadImage(Image{Manifest: manifest, Config: config, Layers: layers}, url, "zot-cve-test", "0.0.1")
if err != nil {
panic(err)
}
_, err = test.ReadLogFileAndSearchString(logPath, "DB update completed, next update scheduled", 90*time.Second)
if err != nil {
panic(err)
}
Convey("Test CVE by image name - GQL - positive", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
So(str, ShouldContainSubstring, "CVE")
})
Convey("Test CVE by image name - GQL - search CVE by title in results", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--cve-id", "CVE-C1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
So(str, ShouldContainSubstring, "CVE-C1")
So(str, ShouldNotContainSubstring, "CVE-2")
})
Convey("Test CVE by image name - GQL - search CVE by id in results", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--cve-id", "CVE-2", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
So(str, ShouldContainSubstring, "CVE-2")
So(str, ShouldNotContainSubstring, "CVE-1")
})
Convey("Test CVE by image name - GQL - search nonexistent CVE", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--cve-id", "CVE-100", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "No CVEs found for image")
})
Convey("Test CVE by image name - GQL - invalid image", t, func() {
args := []string{"list", "invalid:0.0.1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("Test CVE by image name - GQL - invalid image name and tag", t, func() {
args := []string{"list", "invalid:", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("Test CVE by image name - GQL - invalid output format", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "-f", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid output format")
})
Convey("Test images by CVE ID - GQL - positive", t, func() {
args := []string{"affected", "CVE-2019-9923", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldEqual, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 40d1f749 false 605B")
})
Convey("Test images by CVE ID - GQL - invalid CVE ID", t, func() {
args := []string{"affected", "CVE-invalid", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test images by CVE ID - GQL - invalid output format", t, func() {
args := []string{"affected", "CVE-2019-9923", "-f", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid output format")
})
Convey("Test fixed tags by image name and CVE ID - GQL - positive", t, func() {
args := []string{"fixed", "zot-cve-test", "CVE-2019-9923", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldEqual, "")
})
Convey("Test fixed tags by image name and CVE ID - GQL - random cve", t, func() {
args := []string{"fixed", "zot-cve-test", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by image name and CVE ID - GQL - random image", t, func() {
args := []string{"fixed", "zot-cv-test", "CVE-2019-20807", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by image name and CVE ID - GQL - invalid image", t, func() {
args := []string{"fixed", "zot-cv-test:tag", "CVE-2019-20807", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - GQL - positive", t, func() {
args := []string{"affected", "CVE-2019-9923", "--repo", "zot-cve-test", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldEqual,
"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 40d1f749 false 605B")
})
Convey("Test CVE by name and CVE ID - GQL - invalid name and CVE ID", t, func() {
args := []string{"affected", "CVE-20807", "--repo", "test", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - GQL - invalid output format", t, func() {
args := []string{"affected", "CVE-2019-9923", "--repo", "zot-cve-test", "-f", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid output format")
})
}
func TestCVESort(t *testing.T) {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
},
},
},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
image1 := CreateRandomImage()
storeController := ociutils.GetDefaultStoreController(rootDir, ctlr.Log)
err := WriteImageToFileSystem(image1, "repo", "tag", storeController)
if err != nil {
t.FailNow()
}
ctx := context.Background()
if err := ctlr.Init(ctx); err != nil {
panic(err)
}
ctlr.CveScanner = mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
return map[string]cvemodel.CVE{
"CVE-2023-1255": {
ID: "CVE-2023-1255",
Severity: "LOW",
Title: "Input buffer over-read in AES-XTS implementation and testing",
},
"CVE-2023-2650": {
ID: "CVE-2023-2650",
Severity: "MEDIUM",
Title: "Possible DoS translating ASN.1 object identifier and executer",
},
"CVE-2023-2975": {
ID: "CVE-2023-2975",
Severity: "HIGH",
Title: "AES-SIV cipher implementation contains a bug that can break",
},
"CVE-2023-3446": {
ID: "CVE-2023-3446",
Severity: "CRITICAL",
Title: "Excessive time spent checking DH keys and parenthesis",
},
"CVE-2023-3817": {
ID: "CVE-2023-3817",
Severity: "MEDIUM",
Title: "Excessive time spent checking DH q parameter and arguments",
},
}, nil
},
}
go func() {
if err := ctlr.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
panic(err)
}
}()
defer ctlr.Shutdown()
test.WaitTillServerReady(baseURL)
space := regexp.MustCompile(`\s+`)
Convey("test sorting", t, func() {
args := []string{"list", "repo:tag", "--sort-by", "severity", "--url", baseURL}
cmd := NewCVECommand(new(searchService))
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...")
args = []string{"list", "repo:tag", "--sort-by", "alpha-asc", "--url", baseURL}
cmd = NewCVECommand(new(searchService))
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ...")
args = []string{"list", "repo:tag", "--sort-by", "alpha-dsc", "--url", baseURL}
cmd = NewCVECommand(new(searchService))
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...")
})
}
func TestCVECommandGQL(t *testing.T) { func TestCVECommandGQL(t *testing.T) {
port := test.GetFreePort() port := test.GetFreePort()
baseURL := test.GetBaseURL(port) baseURL := test.GetBaseURL(port)
@ -1096,7 +424,7 @@ func TestCVECommandGQL(t *testing.T) {
args := []string{"affected", "CVE-12345", "--config", "cvetest"} args := []string{"affected", "CVE-12345", "--config", "cvetest"}
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewCVECommand(mockService{ cmd := NewCVECommand(mockService{
getTagsForCVEGQLFn: func(ctx context.Context, config searchConfig, username, password, getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, cveID string) (*zcommon.ImagesForCve, error, imageName, cveID string) (*zcommon.ImagesForCve, error,
) { ) {
if count == 0 { if count == 0 {
@ -1143,7 +471,7 @@ func TestCVECommandGQL(t *testing.T) {
args := []string{"fixed", "repo", "CVE-2222", "--config", "cvetest"} args := []string{"fixed", "repo", "CVE-2222", "--config", "cvetest"}
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewCVECommand(mockService{ cmd := NewCVECommand(mockService{
getFixedTagsForCVEGQLFn: func(ctx context.Context, config searchConfig, username, password, getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, cveID string) (*zcommon.ImageListWithCVEFixedResponse, error, imageName, cveID string) (*zcommon.ImageListWithCVEFixedResponse, error,
) { ) {
if count == 0 { if count == 0 {
@ -1190,7 +518,7 @@ func TestCVECommandGQL(t *testing.T) {
args := []string{"list", "repo:vuln", "--config", "cvetest"} args := []string{"list", "repo:vuln", "--config", "cvetest"}
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewCVECommand(mockService{ cmd := NewCVECommand(mockService{
getCveByImageGQLFn: func(ctx context.Context, config searchConfig, username, password, getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, searchedCVE string) (*cveResult, error, imageName, searchedCVE string) (*cveResult, error,
) { ) {
if count == 0 { if count == 0 {
@ -1371,115 +699,13 @@ func TestCVECommandErrors(t *testing.T) {
}) })
} }
func getMockCveScanner(metaDB mTypes.MetaDB) cveinfo.Scanner {
// MetaDB loaded with initial data now mock the scanner
// Setup test CVE data in mock scanner
scanner := mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
if image == "zot-cve-test@sha256:40d1f74918aefed733c590f798d7eafde8fc0a7ec63bb8bc52eaae133cf92495" ||
image == "zot-cve-test:0.0.1" {
return map[string]cvemodel.CVE{
"CVE-1": {
ID: "CVE-1",
Severity: "CRITICAL",
Title: "Title for CVE-C1",
Description: "Description of CVE-1",
},
"CVE-2019-9923": {
ID: "CVE-2019-9923",
Severity: "HIGH",
Title: "Title for CVE-2",
Description: "Description of CVE-2",
},
"CVE-3": {
ID: "CVE-3",
Severity: "MEDIUM",
Title: "Title for CVE-3",
Description: "Description of CVE-3",
},
"CVE-4": {
ID: "CVE-4",
Severity: "LOW",
Title: "Title for CVE-4",
Description: "Description of CVE-4",
},
"CVE-5": {
ID: "CVE-5",
Severity: "UNKNOWN",
Title: "Title for CVE-5",
Description: "Description of CVE-5",
},
}, nil
}
// By default the image has no vulnerabilities
return map[string]cvemodel.CVE{}, nil
},
IsImageFormatScannableFn: func(repo string, reference string) (bool, error) {
// Almost same logic compared to actual Trivy specific implementation
imageDir := repo
inputTag := reference
repoMeta, err := metaDB.GetRepoMeta(imageDir)
if err != nil {
return false, err
}
manifestDigestStr := reference
if zcommon.IsTag(reference) {
var ok bool
descriptor, ok := repoMeta.Tags[inputTag]
if !ok {
return false, zerr.ErrTagMetaNotFound
}
manifestDigestStr = descriptor.Digest
}
manifestDigest, err := godigest.Parse(manifestDigestStr)
if err != nil {
return false, err
}
manifestData, err := metaDB.GetManifestData(manifestDigest)
if err != nil {
return false, err
}
var manifestContent ispec.Manifest
err = json.Unmarshal(manifestData.ManifestBlob, &manifestContent)
if err != nil {
return false, zerr.ErrScanNotSupported
}
for _, imageLayer := range manifestContent.Layers {
switch imageLayer.MediaType {
case ispec.MediaTypeImageLayerGzip, ispec.MediaTypeImageLayer, string(regTypes.DockerLayer):
return true, nil
default:
return false, zerr.ErrScanNotSupported
}
}
return false, nil
},
}
return &scanner
}
type mockServiceForRetry struct { type mockServiceForRetry struct {
mockService mockService
retryCounter int retryCounter int
succeedOn int succeedOn int
} }
func (service *mockServiceForRetry) getTagsForCVEGQL(ctx context.Context, config searchConfig, username, password, repo, func (service *mockServiceForRetry) getTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, repo,
cveID string, cveID string,
) (*zcommon.ImagesForCve, error) { ) (*zcommon.ImagesForCve, error) {
service.retryCounter += 1 service.retryCounter += 1

View file

@ -0,0 +1,787 @@
//go:build search
// +build search
package client_test
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"regexp"
"strings"
"testing"
"time"
regTypes "github.com/google/go-containerregistry/pkg/v1/types"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/cli/client"
zcommon "zotregistry.io/zot/pkg/common"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/log"
mTypes "zotregistry.io/zot/pkg/meta/types"
"zotregistry.io/zot/pkg/storage"
"zotregistry.io/zot/pkg/storage/local"
test "zotregistry.io/zot/pkg/test/common"
. "zotregistry.io/zot/pkg/test/image-utils"
"zotregistry.io/zot/pkg/test/mocks"
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
)
func TestNegativeServerResponse(t *testing.T) {
Convey("Test from real server without search endpoint", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
srcStorageCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
err := WriteImageToFileSystem(CreateDefaultVulnerableImage(), "zot-cve-test", "0.0.1", srcStorageCtlr)
So(err, ShouldBeNil)
conf.Storage.RootDirectory = dir
trivyConfig := &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
}
cveConfig := &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: trivyConfig,
}
defaultVal := false
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: cveConfig,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
if err != nil {
panic(err)
}
logPath := logFile.Name()
defer os.Remove(logPath)
writers := io.MultiWriter(os.Stdout, logFile)
ctlr := api.NewController(conf)
ctlr.Log.Logger = ctlr.Log.Output(writers)
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
_, err = test.ReadLogFileAndSearchString(logPath, "CVE config not provided, skipping CVE update", 90*time.Second)
if err != nil {
panic(err)
}
Convey("Status Code Not Found", func() {
args := []string{"list", "zot-cve-test:0.0.1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, zerr.ErrExtensionNotEnabled.Error())
})
})
Convey("Test non-existing manifest blob", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
imageStore := local.NewImageStore(dir, false, false,
log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), nil, nil)
storeController := storage.StoreController{
DefaultStore: imageStore,
}
image := CreateRandomImage()
err := WriteImageToFileSystem(image, "zot-cve-test", "0.0.1", storeController)
So(err, ShouldBeNil)
err = os.RemoveAll(path.Join(dir, "zot-cve-test/blobs"))
if err != nil {
panic(err)
}
conf.Storage.RootDirectory = dir
trivyConfig := &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
}
cveConfig := &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: trivyConfig,
}
defaultVal := true
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: cveConfig,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
if err != nil {
panic(err)
}
logPath := logFile.Name()
defer os.Remove(logPath)
writers := io.MultiWriter(os.Stdout, logFile)
ctlr := api.NewController(conf)
ctlr.Log.Logger = ctlr.Log.Output(writers)
ctx := context.Background()
if err := ctlr.Init(ctx); err != nil {
panic(err)
}
ctlr.CveScanner = getMockCveScanner(ctlr.MetaDB)
go func() {
if err := ctlr.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
panic(err)
}
}()
defer ctlr.Shutdown()
test.WaitTillServerReady(url)
_, err = test.ReadLogFileAndSearchString(logPath, "DB update completed, next update scheduled", 90*time.Second)
if err != nil {
panic(err)
}
args := []string{"fixed", "zot-cve-test", "CVE-2019-9923", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
})
}
//nolint:dupl
func TestServerCVEResponse(t *testing.T) {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
conf.Storage.RootDirectory = dir
trivyConfig := &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
}
cveConfig := &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: trivyConfig,
}
defaultVal := true
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: cveConfig,
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
if err != nil {
panic(err)
}
logPath := logFile.Name()
defer os.Remove(logPath)
writers := io.MultiWriter(os.Stdout, logFile)
ctlr := api.NewController(conf)
ctlr.Log.Logger = ctlr.Log.Output(writers)
ctx := context.Background()
if err := ctlr.Init(ctx); err != nil {
panic(err)
}
ctlr.CveScanner = getMockCveScanner(ctlr.MetaDB)
go func() {
if err := ctlr.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
panic(err)
}
}()
defer ctlr.Shutdown()
test.WaitTillServerReady(url)
image := CreateDefaultImage()
err = UploadImage(image, url, "zot-cve-test", "0.0.1")
if err != nil {
panic(err)
}
_, err = test.ReadLogFileAndSearchString(logPath, "DB update completed, next update scheduled", 90*time.Second)
if err != nil {
panic(err)
}
Convey("Test CVE by image name - GQL - positive", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
So(str, ShouldContainSubstring, "CVE")
})
Convey("Test CVE by image name - GQL - search CVE by title in results", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--cve-id", "CVE-C1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
So(str, ShouldContainSubstring, "CVE-C1")
So(str, ShouldNotContainSubstring, "CVE-2")
})
Convey("Test CVE by image name - GQL - search CVE by id in results", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--cve-id", "CVE-2", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
So(str, ShouldContainSubstring, "CVE-2")
So(str, ShouldNotContainSubstring, "CVE-1")
})
Convey("Test CVE by image name - GQL - search nonexistent CVE", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "--cve-id", "CVE-100", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldContainSubstring, "No CVEs found for image")
})
Convey("Test CVE by image name - GQL - invalid image", t, func() {
args := []string{"list", "invalid:0.0.1", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("Test CVE by image name - GQL - invalid image name and tag", t, func() {
args := []string{"list", "invalid:", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("Test CVE by image name - GQL - invalid output format", t, func() {
args := []string{"list", "zot-cve-test:0.0.1", "-f", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid output format")
})
Convey("Test images by CVE ID - GQL - positive", t, func() {
args := []string{"affected", "CVE-2019-9923", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldEqual, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 db573b01 false 854B")
})
Convey("Test images by CVE ID - GQL - invalid CVE ID", t, func() {
args := []string{"affected", "CVE-invalid", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test images by CVE ID - GQL - invalid output format", t, func() {
args := []string{"affected", "CVE-2019-9923", "-f", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid output format")
})
Convey("Test fixed tags by image name and CVE ID - GQL - positive", t, func() {
args := []string{"fixed", "zot-cve-test", "CVE-2019-9923", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldEqual, "")
})
Convey("Test fixed tags by image name and CVE ID - GQL - random cve", t, func() {
args := []string{"fixed", "zot-cve-test", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by image name and CVE ID - GQL - random image", t, func() {
args := []string{"fixed", "zot-cv-test", "CVE-2019-20807", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by image name and CVE ID - GQL - invalid image", t, func() {
args := []string{"fixed", "zot-cv-test:tag", "CVE-2019-20807", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - GQL - positive", t, func() {
args := []string{"affected", "CVE-2019-9923", "--repo", "zot-cve-test", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldEqual,
"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 db573b01 false 854B")
})
Convey("Test CVE by name and CVE ID - GQL - invalid name and CVE ID", t, func() {
args := []string{"affected", "CVE-20807", "--repo", "test", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "REPOSITORY TAG OS/ARCH SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - GQL - invalid output format", t, func() {
args := []string{"affected", "CVE-2019-9923", "--repo", "zot-cve-test", "-f", "random", "--config", "cvetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cveCmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cveCmd.SetOut(buff)
cveCmd.SetErr(buff)
cveCmd.SetArgs(args)
err = cveCmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid output format")
})
}
func TestCVESort(t *testing.T) {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: &extconf.CVEConfig{
UpdateInterval: 2,
Trivy: &extconf.TrivyConfig{
DBRepository: "ghcr.io/project-zot/trivy-db",
},
},
},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
image1 := CreateRandomImage()
storeController := ociutils.GetDefaultStoreController(rootDir, ctlr.Log)
err := WriteImageToFileSystem(image1, "repo", "tag", storeController)
if err != nil {
t.FailNow()
}
ctx := context.Background()
if err := ctlr.Init(ctx); err != nil {
panic(err)
}
ctlr.CveScanner = mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
return map[string]cvemodel.CVE{
"CVE-2023-1255": {
ID: "CVE-2023-1255",
Severity: "LOW",
Title: "Input buffer over-read in AES-XTS implementation and testing",
},
"CVE-2023-2650": {
ID: "CVE-2023-2650",
Severity: "MEDIUM",
Title: "Possible DoS translating ASN.1 object identifier and executer",
},
"CVE-2023-2975": {
ID: "CVE-2023-2975",
Severity: "HIGH",
Title: "AES-SIV cipher implementation contains a bug that can break",
},
"CVE-2023-3446": {
ID: "CVE-2023-3446",
Severity: "CRITICAL",
Title: "Excessive time spent checking DH keys and parenthesis",
},
"CVE-2023-3817": {
ID: "CVE-2023-3817",
Severity: "MEDIUM",
Title: "Excessive time spent checking DH q parameter and arguments",
},
}, nil
},
}
go func() {
if err := ctlr.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
panic(err)
}
}()
defer ctlr.Shutdown()
test.WaitTillServerReady(baseURL)
space := regexp.MustCompile(`\s+`)
Convey("test sorting", t, func() {
args := []string{"list", "repo:tag", "--sort-by", "severity", "--url", baseURL}
cmd := client.NewCVECommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...")
args = []string{"list", "repo:tag", "--sort-by", "alpha-asc", "--url", baseURL}
cmd = client.NewCVECommand(client.NewSearchService())
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ...")
args = []string{"list", "repo:tag", "--sort-by", "alpha-dsc", "--url", baseURL}
cmd = client.NewCVECommand(client.NewSearchService())
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat...")
})
}
func getMockCveScanner(metaDB mTypes.MetaDB) cveinfo.Scanner {
// MetaDB loaded with initial data now mock the scanner
// Setup test CVE data in mock scanner
scanner := mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
if strings.Contains(image, "zot-cve-test@sha256:db573b01") ||
image == "zot-cve-test:0.0.1" {
return map[string]cvemodel.CVE{
"CVE-1": {
ID: "CVE-1",
Severity: "CRITICAL",
Title: "Title for CVE-C1",
Description: "Description of CVE-1",
},
"CVE-2019-9923": {
ID: "CVE-2019-9923",
Severity: "HIGH",
Title: "Title for CVE-2",
Description: "Description of CVE-2",
},
"CVE-3": {
ID: "CVE-3",
Severity: "MEDIUM",
Title: "Title for CVE-3",
Description: "Description of CVE-3",
},
"CVE-4": {
ID: "CVE-4",
Severity: "LOW",
Title: "Title for CVE-4",
Description: "Description of CVE-4",
},
"CVE-5": {
ID: "CVE-5",
Severity: "UNKNOWN",
Title: "Title for CVE-5",
Description: "Description of CVE-5",
},
}, nil
}
// By default the image has no vulnerabilities
return map[string]cvemodel.CVE{}, nil
},
IsImageFormatScannableFn: func(repo string, reference string) (bool, error) {
// Almost same logic compared to actual Trivy specific implementation
imageDir := repo
inputTag := reference
repoMeta, err := metaDB.GetRepoMeta(imageDir)
if err != nil {
return false, err
}
manifestDigestStr := reference
if zcommon.IsTag(reference) {
var ok bool
descriptor, ok := repoMeta.Tags[inputTag]
if !ok {
return false, zerr.ErrTagMetaNotFound
}
manifestDigestStr = descriptor.Digest
}
manifestDigest, err := godigest.Parse(manifestDigestStr)
if err != nil {
return false, err
}
manifestData, err := metaDB.GetManifestData(manifestDigest)
if err != nil {
return false, err
}
var manifestContent ispec.Manifest
err = json.Unmarshal(manifestData.ManifestBlob, &manifestContent)
if err != nil {
return false, zerr.ErrScanNotSupported
}
for _, imageLayer := range manifestContent.Layers {
switch imageLayer.MediaType {
case ispec.MediaTypeImageLayerGzip, ispec.MediaTypeImageLayer, string(regTypes.DockerLayer):
return true, nil
default:
return false, zerr.ErrScanNotSupported
}
}
return false, nil
},
}
return &scanner
}

View file

@ -90,11 +90,11 @@ func haveSameArgs(query field, reqQuery GQLQuery) bool {
return true return true
} }
func CheckExtEndPointQuery(config searchConfig, requiredQueries ...GQLQuery) error { func CheckExtEndPointQuery(config SearchConfig, requiredQueries ...GQLQuery) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx := context.Background() ctx := context.Background()
discoverEndPoint, err := combineServerAndEndpointURL(config.servURL, fmt.Sprintf("%s%s", discoverEndPoint, err := combineServerAndEndpointURL(config.ServURL, fmt.Sprintf("%s%s",
constants.RoutePrefix, constants.ExtOciDiscoverPrefix)) constants.RoutePrefix, constants.ExtOciDiscoverPrefix))
if err != nil { if err != nil {
return err return err
@ -102,8 +102,8 @@ func CheckExtEndPointQuery(config searchConfig, requiredQueries ...GQLQuery) err
discoverResponse := &distext.ExtensionList{} discoverResponse := &distext.ExtensionList{}
_, err = makeGETRequest(ctx, discoverEndPoint, username, password, config.verifyTLS, _, err = makeGETRequest(ctx, discoverEndPoint, username, password, config.VerifyTLS,
config.debug, &discoverResponse, config.resultWriter) config.Debug, &discoverResponse, config.ResultWriter)
if err != nil { if err != nil {
return err return err
} }
@ -124,7 +124,7 @@ func CheckExtEndPointQuery(config searchConfig, requiredQueries ...GQLQuery) err
return fmt.Errorf("%w: search extension gql endpoints not found", zerr.ErrExtensionNotEnabled) return fmt.Errorf("%w: search extension gql endpoints not found", zerr.ErrExtensionNotEnabled)
} }
searchEndPoint, _ := combineServerAndEndpointURL(config.servURL, constants.FullSearchPrefix) searchEndPoint, _ := combineServerAndEndpointURL(config.ServURL, constants.FullSearchPrefix)
schemaQuery := ` schemaQuery := `
{ {
@ -153,8 +153,8 @@ func CheckExtEndPointQuery(config searchConfig, requiredQueries ...GQLQuery) err
queryResponse := &schemaList{} queryResponse := &schemaList{}
err = makeGraphQLRequest(ctx, searchEndPoint, schemaQuery, username, password, config.verifyTLS, err = makeGraphQLRequest(ctx, searchEndPoint, schemaQuery, username, password, config.VerifyTLS,
config.debug, queryResponse, config.resultWriter) config.Debug, queryResponse, config.ResultWriter)
if err != nil { if err != nil {
return fmt.Errorf("gql query failed: %w", err) return fmt.Errorf("gql query failed: %w", err)
} }

View file

@ -1,7 +1,7 @@
//go:build search && needprivileges //go:build search && needprivileges
// +build search,needprivileges // +build search,needprivileges
package client package client_test
import ( import (
"bytes" "bytes"
@ -19,6 +19,7 @@ import (
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants" "zotregistry.io/zot/pkg/api/constants"
"zotregistry.io/zot/pkg/cli/client"
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
) )
@ -94,7 +95,7 @@ func TestElevatedPrivilegesTLSNewControllerPrivilegedCert(t *testing.T) {
defer os.Remove(configPath) defer os.Remove(configPath)
args := []string{"list", "--config", "imagetest"} args := []string{"list", "--config", "imagetest"}
imageCmd := NewImageCommand(new(searchService)) imageCmd := client.NewImageCommand(client.NewSearchService())
imageBuff := bytes.NewBufferString("") imageBuff := bytes.NewBufferString("")
imageCmd.SetOut(imageBuff) imageCmd.SetOut(imageBuff)
imageCmd.SetErr(imageBuff) imageCmd.SetErr(imageBuff)

View file

@ -1,7 +1,7 @@
//go:build search //go:build search
// +build search // +build search
package client package client_test
import ( import (
"io" "io"
@ -11,6 +11,7 @@ import (
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/cli/client"
extconf "zotregistry.io/zot/pkg/extensions/config" extconf "zotregistry.io/zot/pkg/extensions/config"
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
) )
@ -36,57 +37,57 @@ func TestGQLQueries(t *testing.T) {
defer cm.StopServer() defer cm.StopServer()
searchConfig := searchConfig{ searchConfig := client.SearchConfig{
servURL: baseURL, ServURL: baseURL,
user: "", User: "",
verifyTLS: false, VerifyTLS: false,
debug: false, Debug: false,
resultWriter: io.Discard, ResultWriter: io.Discard,
} }
Convey("Make sure the current CLI used the right queries in case they change", t, func() { Convey("Make sure the current CLI used the right queries in case they change", t, func() {
Convey("ImageList", func() { Convey("ImageList", func() {
err := CheckExtEndPointQuery(searchConfig, ImageListQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.ImageListQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("ImageListForDigest", func() { Convey("ImageListForDigest", func() {
err := CheckExtEndPointQuery(searchConfig, ImageListForDigestQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.ImageListForDigestQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("BaseImageList", func() { Convey("BaseImageList", func() {
err := CheckExtEndPointQuery(searchConfig, BaseImageListQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.BaseImageListQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("DerivedImageList", func() { Convey("DerivedImageList", func() {
err := CheckExtEndPointQuery(searchConfig, DerivedImageListQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.DerivedImageListQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("CVEListForImage", func() { Convey("CVEListForImage", func() {
err := CheckExtEndPointQuery(searchConfig, CVEListForImageQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.CVEListForImageQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("ImageListForCVE", func() { Convey("ImageListForCVE", func() {
err := CheckExtEndPointQuery(searchConfig, ImageListForCVEQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.ImageListForCVEQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("ImageListWithCVEFixed", func() { Convey("ImageListWithCVEFixed", func() {
err := CheckExtEndPointQuery(searchConfig, ImageListWithCVEFixedQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.ImageListWithCVEFixedQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("Referrers", func() { Convey("Referrers", func() {
err := CheckExtEndPointQuery(searchConfig, ReferrersQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.ReferrersQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
Convey("GlobalSearch", func() { Convey("GlobalSearch", func() {
err := CheckExtEndPointQuery(searchConfig, GlobalSearchQuery()) err := client.CheckExtEndPointQuery(searchConfig, client.GlobalSearchQuery())
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
}) })

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
//go:build search //go:build search
// +build search // +build search
package client package client_test
import ( import (
"bytes" "bytes"
@ -15,6 +15,7 @@ import (
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/cli/client"
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
. "zotregistry.io/zot/pkg/test/image-utils" . "zotregistry.io/zot/pkg/test/image-utils"
) )
@ -43,7 +44,7 @@ func TestReposCommand(t *testing.T) {
defer os.Remove(configPath) defer os.Remove(configPath)
args := []string{"list", "--config", "repostest"} args := []string{"list", "--config", "repostest"}
cmd := NewRepoCommand(mockService{}) cmd := client.NewRepoCommand(client.NewSearchService())
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -57,7 +58,7 @@ func TestReposCommand(t *testing.T) {
So(actual, ShouldContainSubstring, "repo2") So(actual, ShouldContainSubstring, "repo2")
args = []string{"list", "--sort-by", "alpha-dsc", "--config", "repostest"} args = []string{"list", "--sort-by", "alpha-dsc", "--config", "repostest"}
cmd = NewRepoCommand(new(searchService)) cmd = client.NewRepoCommand(client.NewSearchService())
buff = bytes.NewBufferString("") buff = bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -72,7 +73,7 @@ func TestReposCommand(t *testing.T) {
So(strings.Index(actual, "repo2"), ShouldBeLessThan, strings.Index(actual, "repo1")) So(strings.Index(actual, "repo2"), ShouldBeLessThan, strings.Index(actual, "repo1"))
args = []string{"list", "--sort-by", "alpha-asc", "--config", "repostest"} args = []string{"list", "--sort-by", "alpha-asc", "--config", "repostest"}
cmd = NewRepoCommand(new(searchService)) cmd = client.NewRepoCommand(client.NewSearchService())
buff = bytes.NewBufferString("") buff = bytes.NewBufferString("")
cmd.SetOut(buff) cmd.SetOut(buff)
cmd.SetErr(buff) cmd.SetErr(buff)
@ -91,11 +92,13 @@ func TestReposCommand(t *testing.T) {
func TestSuggestions(t *testing.T) { func TestSuggestions(t *testing.T) {
Convey("Suggestions", t, func() { Convey("Suggestions", t, func() {
space := regexp.MustCompile(`\s+`) space := regexp.MustCompile(`\s+`)
suggestion := ShowSuggestionsIfUnknownCommand(NewRepoCommand(mockService{}), []string{"bad-command"}) suggestion := client.ShowSuggestionsIfUnknownCommand(
client.NewRepoCommand(client.NewSearchService()), []string{"bad-command"})
str := space.ReplaceAllString(suggestion.Error(), " ") str := space.ReplaceAllString(suggestion.Error(), " ")
So(str, ShouldContainSubstring, "unknown subcommand") So(str, ShouldContainSubstring, "unknown subcommand")
suggestion = ShowSuggestionsIfUnknownCommand(NewRepoCommand(mockService{}), []string{"listt"}) suggestion = client.ShowSuggestionsIfUnknownCommand(
client.NewRepoCommand(client.NewSearchService()), []string{"listt"})
str = space.ReplaceAllString(suggestion.Error(), " ") str = space.ReplaceAllString(suggestion.Error(), " ")
So(str, ShouldContainSubstring, "Did you mean this? list") So(str, ShouldContainSubstring, "Did you mean this? list")
}) })

View file

@ -10,9 +10,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"time"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api"
@ -20,713 +18,8 @@ import (
extconf "zotregistry.io/zot/pkg/extensions/config" extconf "zotregistry.io/zot/pkg/extensions/config"
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
. "zotregistry.io/zot/pkg/test/image-utils" . "zotregistry.io/zot/pkg/test/image-utils"
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
) )
const (
customArtTypeV1 = "application/custom.art.type.v1"
customArtTypeV2 = "application/custom.art.type.v2"
repoName = "repo"
)
func TestReferrerCLI(t *testing.T) {
Convey("Test GQL", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
repo := repoName
image := CreateRandomImage()
err := UploadImage(image, baseURL, repo, "tag")
So(err, ShouldBeNil)
ref1 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
Subject(image.DescriptorRef()).Build()
ref2 := CreateImageWith().
RandomLayers(1, 10).
ArtifactConfig(customArtTypeV1).
Subject(image.DescriptorRef()).Build()
ref3 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
ArtifactType(customArtTypeV2).
Subject(image.DescriptorRef()).Build()
err = UploadImage(ref1, baseURL, repo, ref1.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref2, baseURL, repo, ref2.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref3, baseURL, repo, ref3.DigestStr())
So(err, ShouldBeNil)
args := []string{"subject", repo + "@" + image.DigestStr(), "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := NewSearchCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
os.Remove(configPath)
args = []string{"subject", repo + ":" + "tag", "--config", "reftest"}
configPath = makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd = NewSearchCommand(new(searchService))
buff = &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
})
Convey("Test REST", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := false
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
repo := repoName
image := CreateRandomImage()
err := UploadImage(image, baseURL, repo, "tag")
So(err, ShouldBeNil)
ref1 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
Subject(image.DescriptorRef()).Build()
ref2 := CreateImageWith().
RandomLayers(1, 10).
ArtifactConfig(customArtTypeV1).
Subject(image.DescriptorRef()).Build()
ref3 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
ArtifactType(customArtTypeV2).
Subject(image.DescriptorRef()).Build()
err = UploadImage(ref1, baseURL, repo, ref1.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref2, baseURL, repo, ref2.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref3, baseURL, repo, ref3.DigestStr())
So(err, ShouldBeNil)
// get referrers by digest
args := []string{"subject", repo + "@" + image.DigestStr(), "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
cmd := NewSearchCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
os.Remove(configPath)
args = []string{"subject", repo + ":" + "tag", "--config", "reftest"}
configPath = makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff = &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
})
}
func TestFormatsReferrersCLI(t *testing.T) {
Convey("Create server", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := false
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
repo := repoName
image := CreateRandomImage()
err := UploadImage(image, baseURL, repo, "tag")
So(err, ShouldBeNil)
// add referrers
ref1 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
Subject(image.DescriptorRef()).Build()
ref2 := CreateImageWith().
RandomLayers(1, 10).
ArtifactConfig(customArtTypeV1).
Subject(image.DescriptorRef()).Build()
ref3 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
ArtifactType(customArtTypeV2).
Subject(image.DescriptorRef()).Build()
err = UploadImage(ref1, baseURL, repo, ref1.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref2, baseURL, repo, ref2.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref3, baseURL, repo, ref3.DigestStr())
So(err, ShouldBeNil)
Convey("JSON format", func() {
args := []string{"subject", repo + "@" + image.DigestStr(), "--format", "json", "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := NewSearchCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("YAML format", func() {
args := []string{"subject", repo + "@" + image.DigestStr(), "--format", "yaml", "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := NewSearchCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("Invalid format", func() {
args := []string{"subject", repo + "@" + image.DigestStr(), "--format", "invalid_format", "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := NewSearchCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestReferrersCLIErrors(t *testing.T) {
Convey("Errors", t, func() {
cmd := NewSearchCommand(new(searchService))
Convey("no url provided", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest","showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("getConfigValue", func() {
args := []string{"subject", "repo/alpine", "--config", "reftest"}
configPath := makeConfigFile(`bad-json`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad showspinnerConfig ", func() {
args := []string{"query", "repo", "--config", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest", "url":"http://127.0.0.1:8080", "showspinner":"bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad verifyTLSConfig ", func() {
args := []string{"query", "repo", "reftest"}
configPath := makeConfigFile(
`{"configs":[{"_name":"reftest", "url":"http://127.0.0.1:8080", "showspinner":false, "verify-tls": "bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("url from config is empty", func() {
args := []string{"subject", "repo/alpine", "--config", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest", "url":"", "showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad params combination", func() {
args := []string{"query", "repo", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest", "url":"http://127.0.0.1:8080", "showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestSearchCLI(t *testing.T) {
Convey("Test GQL", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
const (
repo1 = "repo"
r1tag1 = "repo1tag1"
r1tag2 = "repo1tag2"
repo2 = "repo/alpine"
r2tag1 = "repo2tag1"
r2tag2 = "repo2tag2"
repo3 = "repo/test/alpine"
r3tag1 = "repo3tag1"
r3tag2 = "repo3tag2"
)
image1 := CreateImageWith().
RandomLayers(1, 10).
ImageConfig(ispec.Image{
Created: DefaultTimeRef(),
Platform: ispec.Platform{OS: "Os", Architecture: "Arch"},
}).
Build()
formatterDigest1 := image1.Digest().Encoded()[:8]
image2 := CreateImageWith().
RandomLayers(1, 10).
DefaultConfig().
Build()
formatterDigest2 := image2.Digest().Encoded()[:8]
err := UploadImage(image1, baseURL, repo1, r1tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo1, r1tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo2, r2tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo2, r2tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo3, r3tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo3, r3tag2)
So(err, ShouldBeNil)
// search by repos
args := []string{"query", "test/alpin", "--verbose", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := NewSearchCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "NAME SIZE LAST UPDATED DOWNLOADS STARS PLATFORMS")
So(str, ShouldContainSubstring, "repo/test/alpine 1.1kB 2010-01-01 01:01:01 +0000 UTC 0 0")
So(str, ShouldContainSubstring, "Os/Arch")
So(str, ShouldContainSubstring, "linux/amd64")
fmt.Println("\n", buff.String())
os.Remove(configPath)
cmd = NewSearchCommand(new(searchService))
args = []string{"query", "repo/alpine:", "--config", "searchtest"}
configPath = makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff = &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(str, ShouldContainSubstring, "repo/alpine repo2tag1 Os/Arch "+formatterDigest1+" false 525B")
So(str, ShouldContainSubstring, "repo/alpine repo2tag2 linux/amd64 "+formatterDigest2+" false 552B")
fmt.Println("\n", buff.String())
})
}
func TestFormatsSearchCLI(t *testing.T) {
Convey("", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
const (
repo1 = "repo"
r1tag1 = "repo1tag1"
r1tag2 = "repo1tag2"
repo2 = "repo/alpine"
r2tag1 = "repo2tag1"
r2tag2 = "repo2tag2"
repo3 = "repo/test/alpine"
r3tag1 = "repo3tag1"
r3tag2 = "repo3tag2"
)
image1 := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
image2 := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
err := UploadImage(image1, baseURL, repo1, r1tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo1, r1tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo2, r2tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo2, r2tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo3, r3tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo3, r3tag2)
So(err, ShouldBeNil)
cmd := NewSearchCommand(new(searchService))
Convey("JSON format", func() {
args := []string{"query", "repo/alpine", "--format", "json", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("YAML format", func() {
args := []string{"query", "repo/alpine", "--format", "yaml", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("Invalid format", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestSearchCLIErrors(t *testing.T) {
Convey("Errors", t, func() {
cmd := NewSearchCommand(new(searchService))
Convey("no url provided", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"searchtest","showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("getConfigValue", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(`bad-json`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad showspinnerConfig ", func() {
args := []string{"query", "repo/alpine", "--config", "searchtest"}
configPath := makeConfigFile(
`{"configs":[{"_name":"searchtest", "url":"http://127.0.0.1:8080", "showspinner":"bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad verifyTLSConfig ", func() {
args := []string{"query", "repo/alpine", "--config", "searchtest"}
configPath := makeConfigFile(
`{"configs":[{"_name":"searchtest", "url":"http://127.0.0.1:8080", "showspinner":false, "verify-tls": "bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("url from config is empty", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"searchtest", "url":"", "showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestSearchCommandGQL(t *testing.T) { func TestSearchCommandGQL(t *testing.T) {
port := test.GetFreePort() port := test.GetFreePort()
baseURL := test.GetBaseURL(port) baseURL := test.GetBaseURL(port)
@ -865,70 +158,3 @@ func TestSearchCommandREST(t *testing.T) {
}) })
}) })
} }
func TestSearchSort(t *testing.T) {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: nil,
},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
image1 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2010, 1, 1, 1, 1, 1, 0, time.UTC)}).
Build()
image2 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2020, 1, 1, 1, 1, 1, 0, time.UTC)}).
Build()
storeController := ociutils.GetDefaultStoreController(rootDir, ctlr.Log)
err := WriteImageToFileSystem(image1, "b-repo", "tag2", storeController)
if err != nil {
t.FailNow()
}
err = WriteImageToFileSystem(image2, "a-test-repo", "tag2", storeController)
if err != nil {
t.FailNow()
}
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
Convey("test sorting", t, func() {
args := []string{"query", "repo", "--sort-by", "relevance", "--url", baseURL}
cmd := NewSearchCommand(new(searchService))
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
str := buff.String()
So(strings.Index(str, "b-repo"), ShouldBeLessThan, strings.Index(str, "a-test-repo"))
args = []string{"query", "repo", "--sort-by", "alpha-asc", "--url", baseURL}
cmd = NewSearchCommand(new(searchService))
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = buff.String()
So(strings.Index(str, "a-test-repo"), ShouldBeLessThan, strings.Index(str, "b-repo"))
})
}

View file

@ -0,0 +1,796 @@
//go:build search
// +build search
package client_test
import (
"bytes"
"fmt"
"os"
"regexp"
"strings"
"testing"
"time"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/cli/client"
extconf "zotregistry.io/zot/pkg/extensions/config"
test "zotregistry.io/zot/pkg/test/common"
. "zotregistry.io/zot/pkg/test/image-utils"
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
)
const (
customArtTypeV1 = "application/custom.art.type.v1"
customArtTypeV2 = "application/custom.art.type.v2"
repoName = "repo"
)
func TestReferrerCLI(t *testing.T) {
Convey("Test GQL", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
repo := repoName
image := CreateRandomImage()
err := UploadImage(image, baseURL, repo, "tag")
So(err, ShouldBeNil)
ref1 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
Subject(image.DescriptorRef()).Build()
ref2 := CreateImageWith().
RandomLayers(1, 10).
ArtifactConfig(customArtTypeV1).
Subject(image.DescriptorRef()).Build()
ref3 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
ArtifactType(customArtTypeV2).
Subject(image.DescriptorRef()).Build()
err = UploadImage(ref1, baseURL, repo, ref1.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref2, baseURL, repo, ref2.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref3, baseURL, repo, ref3.DigestStr())
So(err, ShouldBeNil)
args := []string{"subject", repo + "@" + image.DigestStr(), "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := client.NewSearchCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
os.Remove(configPath)
args = []string{"subject", repo + ":" + "tag", "--config", "reftest"}
configPath = makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd = client.NewSearchCommand(client.NewSearchService())
buff = &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
})
Convey("Test REST", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := false
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
repo := repoName
image := CreateRandomImage()
err := UploadImage(image, baseURL, repo, "tag")
So(err, ShouldBeNil)
ref1 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
Subject(image.DescriptorRef()).Build()
ref2 := CreateImageWith().
RandomLayers(1, 10).
ArtifactConfig(customArtTypeV1).
Subject(image.DescriptorRef()).Build()
ref3 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
ArtifactType(customArtTypeV2).
Subject(image.DescriptorRef()).Build()
err = UploadImage(ref1, baseURL, repo, ref1.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref2, baseURL, repo, ref2.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref3, baseURL, repo, ref3.DigestStr())
So(err, ShouldBeNil)
// get referrers by digest
args := []string{"subject", repo + "@" + image.DigestStr(), "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
cmd := client.NewSearchCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
os.Remove(configPath)
args = []string{"subject", repo + ":" + "tag", "--config", "reftest"}
configPath = makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff = &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST")
So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr())
So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr())
fmt.Println(buff.String())
})
}
func TestFormatsReferrersCLI(t *testing.T) {
Convey("Create server", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := false
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
repo := repoName
image := CreateRandomImage()
err := UploadImage(image, baseURL, repo, "tag")
So(err, ShouldBeNil)
// add referrers
ref1 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
Subject(image.DescriptorRef()).Build()
ref2 := CreateImageWith().
RandomLayers(1, 10).
ArtifactConfig(customArtTypeV1).
Subject(image.DescriptorRef()).Build()
ref3 := CreateImageWith().
RandomLayers(1, 10).
RandomConfig().
ArtifactType(customArtTypeV2).
Subject(image.DescriptorRef()).Build()
err = UploadImage(ref1, baseURL, repo, ref1.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref2, baseURL, repo, ref2.DigestStr())
So(err, ShouldBeNil)
err = UploadImage(ref3, baseURL, repo, ref3.DigestStr())
So(err, ShouldBeNil)
Convey("JSON format", func() {
args := []string{"subject", repo + "@" + image.DigestStr(), "--format", "json", "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := client.NewSearchCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("YAML format", func() {
args := []string{"subject", repo + "@" + image.DigestStr(), "--format", "yaml", "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := client.NewSearchCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("Invalid format", func() {
args := []string{"subject", repo + "@" + image.DigestStr(), "--format", "invalid_format", "--config", "reftest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := client.NewSearchCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestReferrersCLIErrors(t *testing.T) {
Convey("Errors", t, func() {
cmd := client.NewSearchCommand(client.NewSearchService())
Convey("no url provided", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest","showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("getConfigValue", func() {
args := []string{"subject", "repo/alpine", "--config", "reftest"}
configPath := makeConfigFile(`bad-json`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad showspinnerConfig ", func() {
args := []string{"query", "repo", "--config", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest", "url":"http://127.0.0.1:8080", "showspinner":"bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad verifyTLSConfig ", func() {
args := []string{"query", "repo", "reftest"}
configPath := makeConfigFile(
`{"configs":[{"_name":"reftest", "url":"http://127.0.0.1:8080", "showspinner":false, "verify-tls": "bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("url from config is empty", func() {
args := []string{"subject", "repo/alpine", "--config", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest", "url":"", "showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad params combination", func() {
args := []string{"query", "repo", "reftest"}
configPath := makeConfigFile(`{"configs":[{"_name":"reftest", "url":"http://127.0.0.1:8080", "showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestSearchCLI(t *testing.T) {
Convey("Test GQL", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
const (
repo1 = "repo"
r1tag1 = "repo1tag1"
r1tag2 = "repo1tag2"
repo2 = "repo/alpine"
r2tag1 = "repo2tag1"
r2tag2 = "repo2tag2"
repo3 = "repo/test/alpine"
r3tag1 = "repo3tag1"
r3tag2 = "repo3tag2"
)
image1 := CreateImageWith().
RandomLayers(1, 10).
ImageConfig(ispec.Image{
Created: DefaultTimeRef(),
Platform: ispec.Platform{OS: "Os", Architecture: "Arch"},
}).
Build()
formatterDigest1 := image1.Digest().Encoded()[:8]
image2 := CreateImageWith().
RandomLayers(1, 10).
DefaultConfig().
Build()
formatterDigest2 := image2.Digest().Encoded()[:8]
err := UploadImage(image1, baseURL, repo1, r1tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo1, r1tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo2, r2tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo2, r2tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo3, r3tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo3, r3tag2)
So(err, ShouldBeNil)
// search by repos
args := []string{"query", "test/alpin", "--verbose", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := client.NewSearchCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "NAME SIZE LAST UPDATED DOWNLOADS STARS PLATFORMS")
So(str, ShouldContainSubstring, "repo/test/alpine 1.1kB 2010-01-01 01:01:01 +0000 UTC 0 0")
So(str, ShouldContainSubstring, "Os/Arch")
So(str, ShouldContainSubstring, "linux/amd64")
fmt.Println("\n", buff.String())
os.Remove(configPath)
cmd = client.NewSearchCommand(client.NewSearchService())
args = []string{"query", "repo/alpine:", "--config", "searchtest"}
configPath = makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff = &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(str, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(str, ShouldContainSubstring, "repo/alpine repo2tag1 Os/Arch "+formatterDigest1+" false 525B")
So(str, ShouldContainSubstring, "repo/alpine repo2tag2 linux/amd64 "+formatterDigest2+" false 552B")
fmt.Println("\n", buff.String())
})
}
func TestFormatsSearchCLI(t *testing.T) {
Convey("", t, func() {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.Storage.GC = false
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
const (
repo1 = "repo"
r1tag1 = "repo1tag1"
r1tag2 = "repo1tag2"
repo2 = "repo/alpine"
r2tag1 = "repo2tag1"
r2tag2 = "repo2tag2"
repo3 = "repo/test/alpine"
r3tag1 = "repo3tag1"
r3tag2 = "repo3tag2"
)
image1 := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
image2 := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build()
err := UploadImage(image1, baseURL, repo1, r1tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo1, r1tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo2, r2tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo2, r2tag2)
So(err, ShouldBeNil)
err = UploadImage(image1, baseURL, repo3, r3tag1)
So(err, ShouldBeNil)
err = UploadImage(image2, baseURL, repo3, r3tag2)
So(err, ShouldBeNil)
cmd := client.NewSearchCommand(client.NewSearchService())
Convey("JSON format", func() {
args := []string{"query", "repo/alpine", "--format", "json", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("YAML format", func() {
args := []string{"query", "repo/alpine", "--format", "yaml", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
fmt.Println(buff.String())
})
Convey("Invalid format", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"searchtest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestSearchCLIErrors(t *testing.T) {
Convey("Errors", t, func() {
cmd := client.NewSearchCommand(client.NewSearchService())
Convey("no url provided", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"searchtest","showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("getConfigValue", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(`bad-json`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad showspinnerConfig ", func() {
args := []string{"query", "repo/alpine", "--config", "searchtest"}
configPath := makeConfigFile(
`{"configs":[{"_name":"searchtest", "url":"http://127.0.0.1:8080", "showspinner":"bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("bad verifyTLSConfig ", func() {
args := []string{"query", "repo/alpine", "--config", "searchtest"}
configPath := makeConfigFile(
`{"configs":[{"_name":"searchtest", "url":"http://127.0.0.1:8080", "showspinner":false, "verify-tls": "bad"}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("url from config is empty", func() {
args := []string{"query", "repo/alpine", "--format", "invalid", "--config", "searchtest"}
configPath := makeConfigFile(`{"configs":[{"_name":"searchtest", "url":"", "showspinner":false}]}`)
defer os.Remove(configPath)
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
func TestSearchSort(t *testing.T) {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: nil,
},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
image1 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2010, 1, 1, 1, 1, 1, 0, time.UTC)}).
Build()
image2 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2020, 1, 1, 1, 1, 1, 0, time.UTC)}).
Build()
storeController := ociutils.GetDefaultStoreController(rootDir, ctlr.Log)
err := WriteImageToFileSystem(image1, "b-repo", "tag2", storeController)
if err != nil {
t.FailNow()
}
err = WriteImageToFileSystem(image2, "a-test-repo", "tag2", storeController)
if err != nil {
t.FailNow()
}
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
Convey("test sorting", t, func() {
args := []string{"query", "repo", "--sort-by", "relevance", "--url", baseURL}
cmd := client.NewSearchCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
str := buff.String()
So(strings.Index(str, "b-repo"), ShouldBeLessThan, strings.Index(str, "a-test-repo"))
args = []string{"query", "repo", "--sort-by", "alpha-asc", "--url", baseURL}
cmd = client.NewSearchCommand(client.NewSearchService())
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = buff.String()
So(strings.Index(str, "a-test-repo"), ShouldBeLessThan, strings.Index(str, "b-repo"))
})
}

View file

@ -17,8 +17,8 @@ import (
const CveDBRetryInterval = 3 const CveDBRetryInterval = 3
func SearchAllImages(config searchConfig) error { func SearchAllImages(config SearchConfig) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
imageErr := make(chan stringResult) imageErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -26,7 +26,7 @@ func SearchAllImages(config searchConfig) error {
wg.Add(1) wg.Add(1)
go config.searchService.getAllImages(ctx, config, username, password, imageErr, &wg) go config.SearchService.getAllImages(ctx, config, username, password, imageErr, &wg)
wg.Add(1) wg.Add(1)
errCh := make(chan error, 1) errCh := make(chan error, 1)
@ -41,13 +41,13 @@ func SearchAllImages(config searchConfig) error {
} }
} }
func SearchAllImagesGQL(config searchConfig) error { func SearchAllImagesGQL(config SearchConfig) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
imageList, err := config.searchService.getImagesGQL(ctx, config, username, password, "") imageList, err := config.SearchService.getImagesGQL(ctx, config, username, password, "")
if err != nil { if err != nil {
return err return err
} }
@ -61,8 +61,8 @@ func SearchAllImagesGQL(config searchConfig) error {
return printImageResult(config, imageListData) return printImageResult(config, imageListData)
} }
func SearchImageByName(config searchConfig, image string) error { func SearchImageByName(config SearchConfig, image string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
imageErr := make(chan stringResult) imageErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -70,7 +70,7 @@ func SearchImageByName(config searchConfig, image string) error {
wg.Add(1) wg.Add(1)
go config.searchService.getImageByName(ctx, config, username, password, go config.SearchService.getImageByName(ctx, config, username, password,
image, imageErr, &wg) image, imageErr, &wg)
wg.Add(1) wg.Add(1)
@ -91,15 +91,15 @@ func SearchImageByName(config searchConfig, image string) error {
} }
} }
func SearchImageByNameGQL(config searchConfig, imageName string) error { func SearchImageByNameGQL(config SearchConfig, imageName string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
repo, tag := zcommon.GetImageDirAndTag(imageName) repo, tag := zcommon.GetImageDirAndTag(imageName)
imageList, err := config.searchService.getImagesGQL(ctx, config, username, password, repo) imageList, err := config.SearchService.getImagesGQL(ctx, config, username, password, repo)
if err != nil { if err != nil {
return err return err
} }
@ -115,8 +115,8 @@ func SearchImageByNameGQL(config searchConfig, imageName string) error {
return printImageResult(config, imageListData) return printImageResult(config, imageListData)
} }
func SearchImagesByDigest(config searchConfig, digest string) error { func SearchImagesByDigest(config SearchConfig, digest string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
imageErr := make(chan stringResult) imageErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -124,7 +124,7 @@ func SearchImagesByDigest(config searchConfig, digest string) error {
wg.Add(1) wg.Add(1)
go config.searchService.getImagesByDigest(ctx, config, username, password, go config.SearchService.getImagesByDigest(ctx, config, username, password,
digest, imageErr, &wg) digest, imageErr, &wg)
wg.Add(1) wg.Add(1)
@ -141,13 +141,13 @@ func SearchImagesByDigest(config searchConfig, digest string) error {
} }
} }
func SearchDerivedImageListGQL(config searchConfig, derivedImage string) error { func SearchDerivedImageListGQL(config SearchConfig, derivedImage string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
imageList, err := config.searchService.getDerivedImageListGQL(ctx, config, username, imageList, err := config.SearchService.getDerivedImageListGQL(ctx, config, username,
password, derivedImage) password, derivedImage)
if err != nil { if err != nil {
return err return err
@ -162,13 +162,13 @@ func SearchDerivedImageListGQL(config searchConfig, derivedImage string) error {
return printImageResult(config, imageListData) return printImageResult(config, imageListData)
} }
func SearchBaseImageListGQL(config searchConfig, baseImage string) error { func SearchBaseImageListGQL(config SearchConfig, baseImage string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
imageList, err := config.searchService.getBaseImageListGQL(ctx, config, username, imageList, err := config.SearchService.getBaseImageListGQL(ctx, config, username,
password, baseImage) password, baseImage)
if err != nil { if err != nil {
return err return err
@ -183,13 +183,13 @@ func SearchBaseImageListGQL(config searchConfig, baseImage string) error {
return printImageResult(config, imageListData) return printImageResult(config, imageListData)
} }
func SearchImagesForDigestGQL(config searchConfig, digest string) error { func SearchImagesForDigestGQL(config SearchConfig, digest string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
imageList, err := config.searchService.getImagesForDigestGQL(ctx, config, username, password, digest) imageList, err := config.SearchService.getImagesForDigestGQL(ctx, config, username, password, digest)
if err != nil { if err != nil {
return err return err
} }
@ -207,8 +207,8 @@ func SearchImagesForDigestGQL(config searchConfig, digest string) error {
return nil return nil
} }
func SearchCVEForImageGQL(config searchConfig, image, searchedCveID string) error { func SearchCVEForImageGQL(config SearchConfig, image, searchedCveID string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -218,7 +218,7 @@ func SearchCVEForImageGQL(config searchConfig, image, searchedCveID string) erro
err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error { err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error {
var err error var err error
cveList, err = config.searchService.getCveByImageGQL(ctx, config, username, password, image, searchedCveID) cveList, err = config.SearchService.getCveByImageGQL(ctx, config, username, password, image, searchedCveID)
if err != nil { if err != nil {
if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) { if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) {
cancel() cancel()
@ -226,7 +226,7 @@ func SearchCVEForImageGQL(config searchConfig, image, searchedCveID string) erro
return err return err
} }
fmt.Fprintf(config.resultWriter, fmt.Fprintf(config.ResultWriter,
"[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds())) "[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds()))
} }
@ -237,30 +237,30 @@ func SearchCVEForImageGQL(config searchConfig, image, searchedCveID string) erro
} }
if len(cveList.Data.CVEListForImage.CVEList) == 0 { if len(cveList.Data.CVEListForImage.CVEList) == 0 {
fmt.Fprint(config.resultWriter, "No CVEs found for image\n") fmt.Fprint(config.ResultWriter, "No CVEs found for image\n")
return nil return nil
} }
var builder strings.Builder var builder strings.Builder
if config.outputFormat == defaultOutputFormat || config.outputFormat == "" { if config.OutputFormat == defaultOutputFormat || config.OutputFormat == "" {
printCVETableHeader(&builder) printCVETableHeader(&builder)
fmt.Fprint(config.resultWriter, builder.String()) fmt.Fprint(config.ResultWriter, builder.String())
} }
out, err := cveList.string(config.outputFormat) out, err := cveList.string(config.OutputFormat)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprint(config.resultWriter, out) fmt.Fprint(config.ResultWriter, out)
return nil return nil
} }
func SearchImagesByCVEIDGQL(config searchConfig, repo, cveid string) error { func SearchImagesByCVEIDGQL(config SearchConfig, repo, cveid string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -270,7 +270,7 @@ func SearchImagesByCVEIDGQL(config searchConfig, repo, cveid string) error {
err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error { err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error {
var err error var err error
imageList, err = config.searchService.getTagsForCVEGQL(ctx, config, username, password, imageList, err = config.SearchService.getTagsForCVEGQL(ctx, config, username, password,
repo, cveid) repo, cveid)
if err != nil { if err != nil {
if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) { if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) {
@ -279,7 +279,7 @@ func SearchImagesByCVEIDGQL(config searchConfig, repo, cveid string) error {
return err return err
} }
fmt.Fprintf(config.resultWriter, fmt.Fprintf(config.ResultWriter,
"[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds())) "[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds()))
} }
@ -298,8 +298,8 @@ func SearchImagesByCVEIDGQL(config searchConfig, repo, cveid string) error {
return printImageResult(config, imageListData) return printImageResult(config, imageListData)
} }
func SearchFixedTagsGQL(config searchConfig, repo, cveid string) error { func SearchFixedTagsGQL(config SearchConfig, repo, cveid string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -309,7 +309,7 @@ func SearchFixedTagsGQL(config searchConfig, repo, cveid string) error {
err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error { err := zcommon.RetryWithContext(ctx, func(attempt int, retryIn time.Duration) error {
var err error var err error
fixedTags, err = config.searchService.getFixedTagsForCVEGQL(ctx, config, username, password, fixedTags, err = config.SearchService.getFixedTagsForCVEGQL(ctx, config, username, password,
repo, cveid) repo, cveid)
if err != nil { if err != nil {
if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) { if !strings.Contains(err.Error(), zerr.ErrCVEDBNotFound.Error()) {
@ -318,7 +318,7 @@ func SearchFixedTagsGQL(config searchConfig, repo, cveid string) error {
return err return err
} }
fmt.Fprintf(config.resultWriter, fmt.Fprintf(config.ResultWriter,
"[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds())) "[warning] CVE DB is not ready [%d] - retry in %d seconds\n", attempt, int(retryIn.Seconds()))
} }
@ -337,13 +337,13 @@ func SearchFixedTagsGQL(config searchConfig, repo, cveid string) error {
return printImageResult(config, imageList) return printImageResult(config, imageList)
} }
func GlobalSearchGQL(config searchConfig, query string) error { func GlobalSearchGQL(config SearchConfig, query string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
globalSearchResult, err := config.searchService.globalSearchGQL(ctx, config, username, password, query) globalSearchResult, err := config.SearchService.globalSearchGQL(ctx, config, username, password, query)
if err != nil { if err != nil {
return err return err
} }
@ -367,8 +367,8 @@ func GlobalSearchGQL(config searchConfig, query string) error {
return printRepoResults(config, reposList) return printRepoResults(config, reposList)
} }
func SearchReferrersGQL(config searchConfig, subject string) error { func SearchReferrersGQL(config SearchConfig, subject string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
repo, ref, refIsTag, err := zcommon.GetRepoReference(subject) repo, ref, refIsTag, err := zcommon.GetRepoReference(subject)
if err != nil { if err != nil {
@ -384,7 +384,7 @@ func SearchReferrersGQL(config searchConfig, subject string) error {
} }
} }
response, err := config.searchService.getReferrersGQL(context.Background(), config, username, password, repo, digest) response, err := config.SearchService.getReferrersGQL(context.Background(), config, username, password, repo, digest)
if err != nil { if err != nil {
return err return err
} }
@ -399,13 +399,13 @@ func SearchReferrersGQL(config searchConfig, subject string) error {
} }
} }
printReferrersTableHeader(config, config.resultWriter, maxArtifactTypeLen) printReferrersTableHeader(config, config.ResultWriter, maxArtifactTypeLen)
return printReferrersResult(config, referrersList, maxArtifactTypeLen) return printReferrersResult(config, referrersList, maxArtifactTypeLen)
} }
func SearchReferrers(config searchConfig, subject string) error { func SearchReferrers(config SearchConfig, subject string) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
repo, ref, refIsTag, err := zcommon.GetRepoReference(subject) repo, ref, refIsTag, err := zcommon.GetRepoReference(subject)
if err != nil { if err != nil {
@ -421,7 +421,7 @@ func SearchReferrers(config searchConfig, subject string) error {
} }
} }
referrersList, err := config.searchService.getReferrers(context.Background(), config, username, password, referrersList, err := config.SearchService.getReferrers(context.Background(), config, username, password,
repo, digest) repo, digest)
if err != nil { if err != nil {
return err return err
@ -435,13 +435,13 @@ func SearchReferrers(config searchConfig, subject string) error {
} }
} }
printReferrersTableHeader(config, config.resultWriter, maxArtifactTypeLen) printReferrersTableHeader(config, config.ResultWriter, maxArtifactTypeLen)
return printReferrersResult(config, referrersList, maxArtifactTypeLen) return printReferrersResult(config, referrersList, maxArtifactTypeLen)
} }
func SearchRepos(config searchConfig) error { func SearchRepos(config SearchConfig) error {
username, password := getUsernameAndPassword(config.user) username, password := getUsernameAndPassword(config.User)
repoErr := make(chan stringResult) repoErr := make(chan stringResult)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -449,7 +449,7 @@ func SearchRepos(config searchConfig) error {
wg.Add(1) wg.Add(1)
go config.searchService.getRepos(ctx, config, username, password, repoErr, &wg) go config.SearchService.getRepos(ctx, config, username, password, repoErr, &wg)
wg.Add(1) wg.Add(1)
errCh := make(chan error, 1) errCh := make(chan error, 1)

View file

@ -29,7 +29,7 @@ func TestSearchAllImages(t *testing.T) {
Convey("SearchAllImages", t, func() { Convey("SearchAllImages", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getAllImagesFn: func(ctx context.Context, config searchConfig, username, password string, getAllImagesFn: func(ctx context.Context, config SearchConfig, username, password string,
channel chan stringResult, wtgrp *sync.WaitGroup, channel chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
str, err := getMockImageStruct().stringPlainText(10, 10, 10, false) str, err := getMockImageStruct().stringPlainText(10, 10, 10, false)
@ -51,7 +51,7 @@ func TestSearchAllImagesGQL(t *testing.T) {
Convey("SearchAllImagesGQL", t, func() { Convey("SearchAllImagesGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesGQLFn: func(ctx context.Context, config searchConfig, username, password, imageName string, getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string,
) (*common.ImageListResponse, error) { ) (*common.ImageListResponse, error) {
return &common.ImageListResponse{ImageList: common.ImageList{ return &common.ImageListResponse{ImageList: common.ImageList{
PaginatedImagesResult: common.PaginatedImagesResult{ PaginatedImagesResult: common.PaginatedImagesResult{
@ -72,7 +72,7 @@ func TestSearchAllImagesGQL(t *testing.T) {
Convey("SearchAllImagesGQL error", t, func() { Convey("SearchAllImagesGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesGQLFn: func(ctx context.Context, config searchConfig, username, password, imageName string, getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string,
) (*common.ImageListResponse, error) { ) (*common.ImageListResponse, error) {
return &common.ImageListResponse{ImageList: common.ImageList{ return &common.ImageListResponse{ImageList: common.ImageList{
PaginatedImagesResult: common.PaginatedImagesResult{ PaginatedImagesResult: common.PaginatedImagesResult{
@ -91,7 +91,7 @@ func TestSearchImageByName(t *testing.T) {
Convey("SearchImageByName", t, func() { Convey("SearchImageByName", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImageByNameFn: func(ctx context.Context, config searchConfig, username string, password string, imageName string, getImageByNameFn: func(ctx context.Context, config SearchConfig, username string, password string, imageName string,
channel chan stringResult, wtgrp *sync.WaitGroup, channel chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
str, err := getMockImageStruct().stringPlainText(10, 10, 10, false) str, err := getMockImageStruct().stringPlainText(10, 10, 10, false)
@ -111,7 +111,7 @@ func TestSearchImageByName(t *testing.T) {
Convey("SearchImageByName error", t, func() { Convey("SearchImageByName error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImageByNameFn: func(ctx context.Context, config searchConfig, username string, password string, imageName string, getImageByNameFn: func(ctx context.Context, config SearchConfig, username string, password string, imageName string,
channel chan stringResult, wtgrp *sync.WaitGroup, channel chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
channel <- stringResult{StrValue: "", Err: zerr.ErrInjected} channel <- stringResult{StrValue: "", Err: zerr.ErrInjected}
@ -127,7 +127,7 @@ func TestSearchImageByNameGQL(t *testing.T) {
Convey("SearchImageByNameGQL", t, func() { Convey("SearchImageByNameGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesGQLFn: func(ctx context.Context, config searchConfig, username, password, imageName string, getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string,
) (*common.ImageListResponse, error) { ) (*common.ImageListResponse, error) {
return &common.ImageListResponse{ImageList: common.ImageList{ return &common.ImageListResponse{ImageList: common.ImageList{
PaginatedImagesResult: common.PaginatedImagesResult{ PaginatedImagesResult: common.PaginatedImagesResult{
@ -148,7 +148,7 @@ func TestSearchImageByNameGQL(t *testing.T) {
Convey("SearchImageByNameGQL error", t, func() { Convey("SearchImageByNameGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesGQLFn: func(ctx context.Context, config searchConfig, username, password, imageName string, getImagesGQLFn: func(ctx context.Context, config SearchConfig, username, password, imageName string,
) (*common.ImageListResponse, error) { ) (*common.ImageListResponse, error) {
return &common.ImageListResponse{ImageList: common.ImageList{ return &common.ImageListResponse{ImageList: common.ImageList{
PaginatedImagesResult: common.PaginatedImagesResult{ PaginatedImagesResult: common.PaginatedImagesResult{
@ -167,7 +167,7 @@ func TestSearchImagesByDigest(t *testing.T) {
Convey("SearchImagesByDigest", t, func() { Convey("SearchImagesByDigest", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesByDigestFn: func(ctx context.Context, config searchConfig, username string, password string, digest string, getImagesByDigestFn: func(ctx context.Context, config SearchConfig, username string, password string, digest string,
rch chan stringResult, wtgrp *sync.WaitGroup, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
str, err := getMockImageStruct().stringPlainText(10, 10, 10, false) str, err := getMockImageStruct().stringPlainText(10, 10, 10, false)
@ -187,7 +187,7 @@ func TestSearchImagesByDigest(t *testing.T) {
Convey("SearchImagesByDigest error", t, func() { Convey("SearchImagesByDigest error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesByDigestFn: func(ctx context.Context, config searchConfig, username string, password string, digest string, getImagesByDigestFn: func(ctx context.Context, config SearchConfig, username string, password string, digest string,
rch chan stringResult, wtgrp *sync.WaitGroup, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
rch <- stringResult{StrValue: "", Err: zerr.ErrInjected} rch <- stringResult{StrValue: "", Err: zerr.ErrInjected}
@ -203,7 +203,7 @@ func TestSearchDerivedImageListGQL(t *testing.T) {
Convey("SearchDerivedImageListGQL", t, func() { Convey("SearchDerivedImageListGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getDerivedImageListGQLFn: func(ctx context.Context, config searchConfig, username string, password string, getDerivedImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string,
derivedImage string) (*common.DerivedImageListResponse, error, derivedImage string) (*common.DerivedImageListResponse, error,
) { ) {
return &common.DerivedImageListResponse{DerivedImageList: common.DerivedImageList{ return &common.DerivedImageListResponse{DerivedImageList: common.DerivedImageList{
@ -227,7 +227,7 @@ func TestSearchDerivedImageListGQL(t *testing.T) {
Convey("SearchDerivedImageListGQL error", t, func() { Convey("SearchDerivedImageListGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getDerivedImageListGQLFn: func(ctx context.Context, config searchConfig, username string, password string, getDerivedImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string,
derivedImage string) (*common.DerivedImageListResponse, error, derivedImage string) (*common.DerivedImageListResponse, error,
) { ) {
return &common.DerivedImageListResponse{DerivedImageList: common.DerivedImageList{ return &common.DerivedImageListResponse{DerivedImageList: common.DerivedImageList{
@ -245,7 +245,7 @@ func TestSearchBaseImageListGQL(t *testing.T) {
Convey("SearchBaseImageListGQL", t, func() { Convey("SearchBaseImageListGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getBaseImageListGQLFn: func(ctx context.Context, config searchConfig, username string, password string, getBaseImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string,
derivedImage string) (*common.BaseImageListResponse, error, derivedImage string) (*common.BaseImageListResponse, error,
) { ) {
return &common.BaseImageListResponse{BaseImageList: common.BaseImageList{ return &common.BaseImageListResponse{BaseImageList: common.BaseImageList{
@ -267,7 +267,7 @@ func TestSearchBaseImageListGQL(t *testing.T) {
Convey("SearchBaseImageListGQL error", t, func() { Convey("SearchBaseImageListGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getBaseImageListGQLFn: func(ctx context.Context, config searchConfig, username string, password string, getBaseImageListGQLFn: func(ctx context.Context, config SearchConfig, username string, password string,
derivedImage string) (*common.BaseImageListResponse, error, derivedImage string) (*common.BaseImageListResponse, error,
) { ) {
return &common.BaseImageListResponse{BaseImageList: common.BaseImageList{ return &common.BaseImageListResponse{BaseImageList: common.BaseImageList{
@ -285,7 +285,7 @@ func TestSearchImagesForDigestGQL(t *testing.T) {
Convey("SearchImagesForDigestGQL", t, func() { Convey("SearchImagesForDigestGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesForDigestGQLFn: func(ctx context.Context, config searchConfig, username string, getImagesForDigestGQLFn: func(ctx context.Context, config SearchConfig, username string,
password string, digest string) (*common.ImagesForDigest, error, password string, digest string) (*common.ImagesForDigest, error,
) { ) {
return &common.ImagesForDigest{ImagesForDigestList: common.ImagesForDigestList{ return &common.ImagesForDigest{ImagesForDigestList: common.ImagesForDigestList{
@ -307,7 +307,7 @@ func TestSearchImagesForDigestGQL(t *testing.T) {
Convey("SearchImagesForDigestGQL error", t, func() { Convey("SearchImagesForDigestGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getImagesForDigestGQLFn: func(ctx context.Context, config searchConfig, username string, getImagesForDigestGQLFn: func(ctx context.Context, config SearchConfig, username string,
password string, digest string) (*common.ImagesForDigest, error, password string, digest string) (*common.ImagesForDigest, error,
) { ) {
return &common.ImagesForDigest{ImagesForDigestList: common.ImagesForDigestList{ return &common.ImagesForDigest{ImagesForDigestList: common.ImagesForDigestList{
@ -325,7 +325,7 @@ func TestSearchCVEForImageGQL(t *testing.T) {
Convey("SearchCVEForImageGQL", t, func() { Convey("SearchCVEForImageGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getCveByImageGQLFn: func(ctx context.Context, config searchConfig, username string, password string, getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username string, password string,
imageName string, searchedCVE string) (*cveResult, error, imageName string, searchedCVE string) (*cveResult, error,
) { ) {
return &cveResult{ return &cveResult{
@ -363,7 +363,7 @@ func TestSearchCVEForImageGQL(t *testing.T) {
Convey("SearchCVEForImageGQL", t, func() { Convey("SearchCVEForImageGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getCveByImageGQLFn: func(ctx context.Context, config searchConfig, username string, password string, getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username string, password string,
imageName string, searchedCVE string) (*cveResult, error, imageName string, searchedCVE string) (*cveResult, error,
) { ) {
return &cveResult{}, zerr.ErrInjected return &cveResult{}, zerr.ErrInjected
@ -379,7 +379,7 @@ func TestSearchImagesByCVEIDGQL(t *testing.T) {
Convey("SearchImagesByCVEIDGQL", t, func() { Convey("SearchImagesByCVEIDGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getTagsForCVEGQLFn: func(ctx context.Context, config searchConfig, username, password, getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, cveID string) (*common.ImagesForCve, error, imageName, cveID string) (*common.ImagesForCve, error,
) { ) {
return &common.ImagesForCve{ return &common.ImagesForCve{
@ -405,7 +405,7 @@ func TestSearchImagesByCVEIDGQL(t *testing.T) {
Convey("SearchImagesByCVEIDGQL error", t, func() { Convey("SearchImagesByCVEIDGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getTagsForCVEGQLFn: func(ctx context.Context, config searchConfig, username, password, getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, cveID string) (*common.ImagesForCve, error, imageName, cveID string) (*common.ImagesForCve, error,
) { ) {
return &common.ImagesForCve{ return &common.ImagesForCve{
@ -425,7 +425,7 @@ func TestSearchFixedTagsGQL(t *testing.T) {
Convey("SearchFixedTagsGQL", t, func() { Convey("SearchFixedTagsGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getFixedTagsForCVEGQLFn: func(ctx context.Context, config searchConfig, username, password, getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, cveID string) (*common.ImageListWithCVEFixedResponse, error, imageName, cveID string) (*common.ImageListWithCVEFixedResponse, error,
) { ) {
return &common.ImageListWithCVEFixedResponse{ return &common.ImageListWithCVEFixedResponse{
@ -449,7 +449,7 @@ func TestSearchFixedTagsGQL(t *testing.T) {
Convey("SearchFixedTagsGQL error", t, func() { Convey("SearchFixedTagsGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getFixedTagsForCVEGQLFn: func(ctx context.Context, config searchConfig, username, password, getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
imageName, cveID string) (*common.ImageListWithCVEFixedResponse, error, imageName, cveID string) (*common.ImageListWithCVEFixedResponse, error,
) { ) {
return &common.ImageListWithCVEFixedResponse{ return &common.ImageListWithCVEFixedResponse{
@ -469,7 +469,7 @@ func TestSearchReferrersGQL(t *testing.T) {
Convey("SearchReferrersGQL", t, func() { Convey("SearchReferrersGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getReferrersGQLFn: func(ctx context.Context, config searchConfig, username, password, getReferrersGQLFn: func(ctx context.Context, config SearchConfig, username, password,
repo, digest string) (*common.ReferrersResp, error, repo, digest string) (*common.ReferrersResp, error,
) { ) {
return &common.ReferrersResp{ return &common.ReferrersResp{
@ -497,7 +497,7 @@ func TestSearchReferrersGQL(t *testing.T) {
Convey("SearchReferrersGQL error", t, func() { Convey("SearchReferrersGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getReferrersGQLFn: func(ctx context.Context, config searchConfig, username, password, getReferrersGQLFn: func(ctx context.Context, config SearchConfig, username, password,
repo, digest string) (*common.ReferrersResp, error, repo, digest string) (*common.ReferrersResp, error,
) { ) {
return &common.ReferrersResp{}, zerr.ErrInjected return &common.ReferrersResp{}, zerr.ErrInjected
@ -513,7 +513,7 @@ func TestGlobalSearchGQL(t *testing.T) {
Convey("GlobalSearchGQL", t, func() { Convey("GlobalSearchGQL", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
globalSearchGQLFn: func(ctx context.Context, config searchConfig, username, password, globalSearchGQLFn: func(ctx context.Context, config SearchConfig, username, password,
query string) (*common.GlobalSearch, error, query string) (*common.GlobalSearch, error,
) { ) {
return &common.GlobalSearch{ return &common.GlobalSearch{
@ -538,7 +538,7 @@ func TestGlobalSearchGQL(t *testing.T) {
Convey("GlobalSearchGQL error", t, func() { Convey("GlobalSearchGQL error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
globalSearchGQLFn: func(ctx context.Context, config searchConfig, username, password, globalSearchGQLFn: func(ctx context.Context, config SearchConfig, username, password,
query string) (*common.GlobalSearch, error, query string) (*common.GlobalSearch, error,
) { ) {
return &common.GlobalSearch{}, zerr.ErrInjected return &common.GlobalSearch{}, zerr.ErrInjected
@ -554,7 +554,7 @@ func TestSearchReferrers(t *testing.T) {
Convey("SearchReferrers", t, func() { Convey("SearchReferrers", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getReferrersFn: func(ctx context.Context, config searchConfig, username string, password string, getReferrersFn: func(ctx context.Context, config SearchConfig, username string, password string,
repo string, digest string) (referrersResult, error, repo string, digest string) (referrersResult, error,
) { ) {
return referrersResult([]common.Referrer{ return referrersResult([]common.Referrer{
@ -580,7 +580,7 @@ func TestSearchReferrers(t *testing.T) {
Convey("SearchReferrers error", t, func() { Convey("SearchReferrers error", t, func() {
buff := bytes.NewBufferString("") buff := bytes.NewBufferString("")
searchConfig := getMockSearchConfig(buff, mockService{ searchConfig := getMockSearchConfig(buff, mockService{
getReferrersFn: func(ctx context.Context, config searchConfig, username string, password string, getReferrersFn: func(ctx context.Context, config SearchConfig, username string, password string,
repo string, digest string) (referrersResult, error, repo string, digest string) (referrersResult, error,
) { ) {
return referrersResult{}, zerr.ErrInjected return referrersResult{}, zerr.ErrInjected
@ -607,17 +607,17 @@ func TestSearchRepos(t *testing.T) {
}) })
} }
func getMockSearchConfig(buff *bytes.Buffer, mockService mockService) searchConfig { func getMockSearchConfig(buff *bytes.Buffer, mockService mockService) SearchConfig {
return searchConfig{ return SearchConfig{
resultWriter: buff, ResultWriter: buff,
user: "", User: "",
searchService: mockService, SearchService: mockService,
servURL: "http://127.0.0.1:8000", ServURL: "http://127.0.0.1:8000",
outputFormat: "", OutputFormat: "",
verifyTLS: false, VerifyTLS: false,
fixedFlag: false, FixedFlag: false,
verbose: false, Verbose: false,
debug: false, Debug: false,
} }
} }
@ -744,19 +744,19 @@ func TestUtils(t *testing.T) {
Convey("CheckExtEndPointQuery", t, func() { Convey("CheckExtEndPointQuery", t, func() {
// invalid url // invalid url
err := CheckExtEndPointQuery(searchConfig{ err := CheckExtEndPointQuery(SearchConfig{
user: "", User: "",
servURL: "bad-url", ServURL: "bad-url",
}) })
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
// good url but no connection // good url but no connection
err = CheckExtEndPointQuery(searchConfig{ err = CheckExtEndPointQuery(SearchConfig{
user: "", User: "",
servURL: "http://127.0.0.1:5000", ServURL: "http://127.0.0.1:5000",
verifyTLS: false, VerifyTLS: false,
debug: false, Debug: false,
resultWriter: io.Discard, ResultWriter: io.Discard,
}) })
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })

View file

@ -32,49 +32,49 @@ const (
) )
type SearchService interface { //nolint:interfacebloat type SearchService interface { //nolint:interfacebloat
getImagesGQL(ctx context.Context, config searchConfig, username, password string, getImagesGQL(ctx context.Context, config SearchConfig, username, password string,
imageName string) (*common.ImageListResponse, error) imageName string) (*common.ImageListResponse, error)
getImagesForDigestGQL(ctx context.Context, config searchConfig, username, password string, getImagesForDigestGQL(ctx context.Context, config SearchConfig, username, password string,
digest string) (*common.ImagesForDigest, error) digest string) (*common.ImagesForDigest, error)
getCveByImageGQL(ctx context.Context, config searchConfig, username, password, getCveByImageGQL(ctx context.Context, config SearchConfig, username, password,
imageName string, searchedCVE string) (*cveResult, error) imageName string, searchedCVE string) (*cveResult, error)
getTagsForCVEGQL(ctx context.Context, config searchConfig, username, password, repo, getTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, repo,
cveID string) (*common.ImagesForCve, error) cveID string) (*common.ImagesForCve, error)
getFixedTagsForCVEGQL(ctx context.Context, config searchConfig, username, password, imageName, getFixedTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, imageName,
cveID string) (*common.ImageListWithCVEFixedResponse, error) cveID string) (*common.ImageListWithCVEFixedResponse, error)
getDerivedImageListGQL(ctx context.Context, config searchConfig, username, password string, getDerivedImageListGQL(ctx context.Context, config SearchConfig, username, password string,
derivedImage string) (*common.DerivedImageListResponse, error) derivedImage string) (*common.DerivedImageListResponse, error)
getBaseImageListGQL(ctx context.Context, config searchConfig, username, password string, getBaseImageListGQL(ctx context.Context, config SearchConfig, username, password string,
baseImage string) (*common.BaseImageListResponse, error) baseImage string) (*common.BaseImageListResponse, error)
getReferrersGQL(ctx context.Context, config searchConfig, username, password string, getReferrersGQL(ctx context.Context, config SearchConfig, username, password string,
repo, digest string) (*common.ReferrersResp, error) repo, digest string) (*common.ReferrersResp, error)
globalSearchGQL(ctx context.Context, config searchConfig, username, password string, globalSearchGQL(ctx context.Context, config SearchConfig, username, password string,
query string) (*common.GlobalSearch, error) query string) (*common.GlobalSearch, error)
getAllImages(ctx context.Context, config searchConfig, username, password string, getAllImages(ctx context.Context, config SearchConfig, username, password string,
channel chan stringResult, wtgrp *sync.WaitGroup) channel chan stringResult, wtgrp *sync.WaitGroup)
getImagesByDigest(ctx context.Context, config searchConfig, username, password, digest string, getImagesByDigest(ctx context.Context, config SearchConfig, username, password, digest string,
channel chan stringResult, wtgrp *sync.WaitGroup) channel chan stringResult, wtgrp *sync.WaitGroup)
getRepos(ctx context.Context, config searchConfig, username, password string, getRepos(ctx context.Context, config SearchConfig, username, password string,
channel chan stringResult, wtgrp *sync.WaitGroup) channel chan stringResult, wtgrp *sync.WaitGroup)
getImageByName(ctx context.Context, config searchConfig, username, password, imageName string, getImageByName(ctx context.Context, config SearchConfig, username, password, imageName string,
channel chan stringResult, wtgrp *sync.WaitGroup) channel chan stringResult, wtgrp *sync.WaitGroup)
getReferrers(ctx context.Context, config searchConfig, username, password string, repo, digest string, getReferrers(ctx context.Context, config SearchConfig, username, password string, repo, digest string,
) (referrersResult, error) ) (referrersResult, error)
} }
type searchConfig struct { type SearchConfig struct {
searchService SearchService SearchService SearchService
servURL string ServURL string
user string User string
outputFormat string OutputFormat string
sortBy string SortBy string
verifyTLS bool VerifyTLS bool
fixedFlag bool FixedFlag bool
verbose bool Verbose bool
debug bool Debug bool
resultWriter io.Writer ResultWriter io.Writer
spinner spinnerState Spinner spinnerState
} }
type searchService struct{} type searchService struct{}
@ -83,7 +83,7 @@ func NewSearchService() SearchService {
return searchService{} return searchService{}
} }
func (service searchService) getDerivedImageListGQL(ctx context.Context, config searchConfig, username, password string, func (service searchService) getDerivedImageListGQL(ctx context.Context, config SearchConfig, username, password string,
derivedImage string, derivedImage string,
) (*common.DerivedImageListResponse, error) { ) (*common.DerivedImageListResponse, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -107,7 +107,7 @@ func (service searchService) getDerivedImageListGQL(ctx context.Context, config
IsSigned IsSigned
} }
} }
}`, derivedImage, Flag2SortCriteria(config.sortBy)) }`, derivedImage, Flag2SortCriteria(config.SortBy))
result := &common.DerivedImageListResponse{} result := &common.DerivedImageListResponse{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result) err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
@ -119,7 +119,7 @@ func (service searchService) getDerivedImageListGQL(ctx context.Context, config
return result, nil return result, nil
} }
func (service searchService) getReferrersGQL(ctx context.Context, config searchConfig, username, password string, func (service searchService) getReferrersGQL(ctx context.Context, config SearchConfig, username, password string,
repo, digest string, repo, digest string,
) (*common.ReferrersResp, error) { ) (*common.ReferrersResp, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -146,7 +146,7 @@ func (service searchService) getReferrersGQL(ctx context.Context, config searchC
return result, nil return result, nil
} }
func (service searchService) globalSearchGQL(ctx context.Context, config searchConfig, username, password string, func (service searchService) globalSearchGQL(ctx context.Context, config SearchConfig, username, password string,
query string, query string,
) (*common.GlobalSearch, error) { ) (*common.GlobalSearch, error) {
GQLQuery := fmt.Sprintf(` GQLQuery := fmt.Sprintf(`
@ -179,7 +179,7 @@ func (service searchService) globalSearchGQL(ctx context.Context, config searchC
StarCount StarCount
} }
} }
}`, query, Flag2SortCriteria(config.sortBy)) }`, query, Flag2SortCriteria(config.SortBy))
result := &common.GlobalSearchResultResp{} result := &common.GlobalSearchResultResp{}
@ -191,7 +191,7 @@ func (service searchService) globalSearchGQL(ctx context.Context, config searchC
return &result.GlobalSearch, nil return &result.GlobalSearch, nil
} }
func (service searchService) getBaseImageListGQL(ctx context.Context, config searchConfig, username, password string, func (service searchService) getBaseImageListGQL(ctx context.Context, config SearchConfig, username, password string,
baseImage string, baseImage string,
) (*common.BaseImageListResponse, error) { ) (*common.BaseImageListResponse, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -215,7 +215,7 @@ func (service searchService) getBaseImageListGQL(ctx context.Context, config sea
IsSigned IsSigned
} }
} }
}`, baseImage, Flag2SortCriteria(config.sortBy)) }`, baseImage, Flag2SortCriteria(config.SortBy))
result := &common.BaseImageListResponse{} result := &common.BaseImageListResponse{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result) err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
@ -227,7 +227,7 @@ func (service searchService) getBaseImageListGQL(ctx context.Context, config sea
return result, nil return result, nil
} }
func (service searchService) getImagesGQL(ctx context.Context, config searchConfig, username, password string, func (service searchService) getImagesGQL(ctx context.Context, config SearchConfig, username, password string,
imageName string, imageName string,
) (*common.ImageListResponse, error) { ) (*common.ImageListResponse, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -251,7 +251,7 @@ func (service searchService) getImagesGQL(ctx context.Context, config searchConf
IsSigned IsSigned
} }
} }
}`, imageName, Flag2SortCriteria(config.sortBy)) }`, imageName, Flag2SortCriteria(config.SortBy))
result := &common.ImageListResponse{} result := &common.ImageListResponse{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result) err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
@ -263,7 +263,7 @@ func (service searchService) getImagesGQL(ctx context.Context, config searchConf
return result, nil return result, nil
} }
func (service searchService) getImagesForDigestGQL(ctx context.Context, config searchConfig, username, password string, func (service searchService) getImagesForDigestGQL(ctx context.Context, config SearchConfig, username, password string,
digest string, digest string,
) (*common.ImagesForDigest, error) { ) (*common.ImagesForDigest, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -287,7 +287,7 @@ func (service searchService) getImagesForDigestGQL(ctx context.Context, config s
IsSigned IsSigned
} }
} }
}`, digest, Flag2SortCriteria(config.sortBy)) }`, digest, Flag2SortCriteria(config.SortBy))
result := &common.ImagesForDigest{} result := &common.ImagesForDigest{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result) err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
@ -299,7 +299,7 @@ func (service searchService) getImagesForDigestGQL(ctx context.Context, config s
return result, nil return result, nil
} }
func (service searchService) getCveByImageGQL(ctx context.Context, config searchConfig, username, password, func (service searchService) getCveByImageGQL(ctx context.Context, config SearchConfig, username, password,
imageName, searchedCVE string, imageName, searchedCVE string,
) (*cveResult, error) { ) (*cveResult, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -310,7 +310,7 @@ func (service searchService) getCveByImageGQL(ctx context.Context, config search
PackageList {Name InstalledVersion FixedVersion} PackageList {Name InstalledVersion FixedVersion}
} }
} }
}`, imageName, searchedCVE, Flag2SortCriteria(config.sortBy)) }`, imageName, searchedCVE, Flag2SortCriteria(config.SortBy))
result := &cveResult{} result := &cveResult{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result) err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
@ -322,7 +322,7 @@ func (service searchService) getCveByImageGQL(ctx context.Context, config search
return result, nil return result, nil
} }
func (service searchService) getTagsForCVEGQL(ctx context.Context, config searchConfig, func (service searchService) getTagsForCVEGQL(ctx context.Context, config SearchConfig,
username, password, repo, cveID string, username, password, repo, cveID string,
) (*common.ImagesForCve, error) { ) (*common.ImagesForCve, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -347,7 +347,7 @@ func (service searchService) getTagsForCVEGQL(ctx context.Context, config search
} }
} }
}`, }`,
cveID, Flag2SortCriteria(config.sortBy)) cveID, Flag2SortCriteria(config.SortBy))
result := &common.ImagesForCve{} result := &common.ImagesForCve{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result) err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
@ -371,7 +371,7 @@ func (service searchService) getTagsForCVEGQL(ctx context.Context, config search
return filteredResults, nil return filteredResults, nil
} }
func (service searchService) getFixedTagsForCVEGQL(ctx context.Context, config searchConfig, func (service searchService) getFixedTagsForCVEGQL(ctx context.Context, config SearchConfig,
username, password, imageName, cveID string, username, password, imageName, cveID string,
) (*common.ImageListWithCVEFixedResponse, error) { ) (*common.ImageListWithCVEFixedResponse, error) {
query := fmt.Sprintf(` query := fmt.Sprintf(`
@ -409,10 +409,10 @@ func (service searchService) getFixedTagsForCVEGQL(ctx context.Context, config s
return result, nil return result, nil
} }
func (service searchService) getReferrers(ctx context.Context, config searchConfig, username, password string, func (service searchService) getReferrers(ctx context.Context, config SearchConfig, username, password string,
repo, digest string, repo, digest string,
) (referrersResult, error) { ) (referrersResult, error) {
referrersEndpoint, err := combineServerAndEndpointURL(config.servURL, referrersEndpoint, err := combineServerAndEndpointURL(config.ServURL,
fmt.Sprintf("/v2/%s/referrers/%s", repo, digest)) fmt.Sprintf("/v2/%s/referrers/%s", repo, digest))
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
@ -423,8 +423,8 @@ func (service searchService) getReferrers(ctx context.Context, config searchConf
} }
referrerResp := &ispec.Index{} referrerResp := &ispec.Index{}
_, err = makeGETRequest(ctx, referrersEndpoint, username, password, config.verifyTLS, _, err = makeGETRequest(ctx, referrersEndpoint, username, password, config.VerifyTLS,
config.debug, &referrerResp, config.resultWriter) config.Debug, &referrerResp, config.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
@ -447,7 +447,7 @@ func (service searchService) getReferrers(ctx context.Context, config searchConf
return referrersList, nil return referrersList, nil
} }
func (service searchService) getImageByName(ctx context.Context, config searchConfig, func (service searchService) getImageByName(ctx context.Context, config SearchConfig,
username, password, imageName string, rch chan stringResult, wtgrp *sync.WaitGroup, username, password, imageName string, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
defer wtgrp.Done() defer wtgrp.Done()
@ -466,7 +466,7 @@ func (service searchService) getImageByName(ctx context.Context, config searchCo
localWg.Wait() localWg.Wait()
} }
func (service searchService) getAllImages(ctx context.Context, config searchConfig, username, password string, func (service searchService) getAllImages(ctx context.Context, config SearchConfig, username, password string,
rch chan stringResult, wtgrp *sync.WaitGroup, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
defer wtgrp.Done() defer wtgrp.Done()
@ -474,7 +474,7 @@ func (service searchService) getAllImages(ctx context.Context, config searchConf
catalog := &catalogResponse{} catalog := &catalogResponse{}
catalogEndPoint, err := combineServerAndEndpointURL(config.servURL, fmt.Sprintf("%s%s", catalogEndPoint, err := combineServerAndEndpointURL(config.ServURL, fmt.Sprintf("%s%s",
constants.RoutePrefix, constants.ExtCatalogPrefix)) constants.RoutePrefix, constants.ExtCatalogPrefix))
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
@ -485,8 +485,8 @@ func (service searchService) getAllImages(ctx context.Context, config searchConf
return return
} }
_, err = makeGETRequest(ctx, catalogEndPoint, username, password, config.verifyTLS, _, err = makeGETRequest(ctx, catalogEndPoint, username, password, config.VerifyTLS,
config.debug, catalog, config.resultWriter) config.Debug, catalog, config.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -513,14 +513,14 @@ func (service searchService) getAllImages(ctx context.Context, config searchConf
localWg.Wait() localWg.Wait()
} }
func getImage(ctx context.Context, config searchConfig, username, password, imageName string, func getImage(ctx context.Context, config SearchConfig, username, password, imageName string,
rch chan stringResult, wtgrp *sync.WaitGroup, pool *requestsPool, rch chan stringResult, wtgrp *sync.WaitGroup, pool *requestsPool,
) { ) {
defer wtgrp.Done() defer wtgrp.Done()
repo, imageTag := common.GetImageDirAndTag(imageName) repo, imageTag := common.GetImageDirAndTag(imageName)
tagListEndpoint, err := combineServerAndEndpointURL(config.servURL, fmt.Sprintf("/v2/%s/tags/list", repo)) tagListEndpoint, err := combineServerAndEndpointURL(config.ServURL, fmt.Sprintf("/v2/%s/tags/list", repo))
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -531,8 +531,8 @@ func getImage(ctx context.Context, config searchConfig, username, password, imag
} }
tagList := &tagListResp{} tagList := &tagListResp{}
_, err = makeGETRequest(ctx, tagListEndpoint, username, password, config.verifyTLS, _, err = makeGETRequest(ctx, tagListEndpoint, username, password, config.VerifyTLS,
config.debug, &tagList, config.resultWriter) config.Debug, &tagList, config.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
@ -567,7 +567,7 @@ func getImage(ctx context.Context, config searchConfig, username, password, imag
} }
} }
func (service searchService) getImagesByDigest(ctx context.Context, config searchConfig, username, func (service searchService) getImagesByDigest(ctx context.Context, config SearchConfig, username,
password string, digest string, rch chan stringResult, wtgrp *sync.WaitGroup, password string, digest string, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
defer wtgrp.Done() defer wtgrp.Done()
@ -652,16 +652,16 @@ func isContextDone(ctx context.Context) bool {
// Query using GQL, the query string is passed as a parameter // Query using GQL, the query string is passed as a parameter
// errors are returned in the stringResult channel, the unmarshalled payload is in resultPtr. // errors are returned in the stringResult channel, the unmarshalled payload is in resultPtr.
func (service searchService) makeGraphQLQuery(ctx context.Context, func (service searchService) makeGraphQLQuery(ctx context.Context,
config searchConfig, username, password, query string, config SearchConfig, username, password, query string,
resultPtr interface{}, resultPtr interface{},
) error { ) error {
endPoint, err := combineServerAndEndpointURL(config.servURL, constants.FullSearchPrefix) endPoint, err := combineServerAndEndpointURL(config.ServURL, constants.FullSearchPrefix)
if err != nil { if err != nil {
return err return err
} }
err = makeGraphQLRequest(ctx, endPoint, query, username, password, config.verifyTLS, err = makeGraphQLRequest(ctx, endPoint, query, username, password, config.VerifyTLS,
config.debug, resultPtr, config.resultWriter) config.Debug, resultPtr, config.ResultWriter)
if err != nil { if err != nil {
return err return err
} }
@ -697,12 +697,12 @@ func checkResultGraphQLQuery(ctx context.Context, err error, resultErrors []comm
return nil return nil
} }
func addManifestCallToPool(ctx context.Context, config searchConfig, pool *requestsPool, func addManifestCallToPool(ctx context.Context, config SearchConfig, pool *requestsPool,
username, password, imageName, tagName string, rch chan stringResult, wtgrp *sync.WaitGroup, username, password, imageName, tagName string, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
defer wtgrp.Done() defer wtgrp.Done()
manifestEndpoint, err := combineServerAndEndpointURL(config.servURL, manifestEndpoint, err := combineServerAndEndpointURL(config.ServURL,
fmt.Sprintf("/v2/%s/manifests/%s", imageName, tagName)) fmt.Sprintf("/v2/%s/manifests/%s", imageName, tagName))
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
@ -1304,7 +1304,7 @@ func getRepoTableWriter(writer io.Writer) *tablewriter.Table {
return table return table
} }
func (service searchService) getRepos(ctx context.Context, config searchConfig, username, password string, func (service searchService) getRepos(ctx context.Context, config SearchConfig, username, password string,
rch chan stringResult, wtgrp *sync.WaitGroup, rch chan stringResult, wtgrp *sync.WaitGroup,
) { ) {
defer wtgrp.Done() defer wtgrp.Done()
@ -1312,7 +1312,7 @@ func (service searchService) getRepos(ctx context.Context, config searchConfig,
catalog := &catalogResponse{} catalog := &catalogResponse{}
catalogEndPoint, err := combineServerAndEndpointURL(config.servURL, fmt.Sprintf("%s%s", catalogEndPoint, err := combineServerAndEndpointURL(config.ServURL, fmt.Sprintf("%s%s",
constants.RoutePrefix, constants.ExtCatalogPrefix)) constants.RoutePrefix, constants.ExtCatalogPrefix))
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
@ -1323,8 +1323,8 @@ func (service searchService) getRepos(ctx context.Context, config searchConfig,
return return
} }
_, err = makeGETRequest(ctx, catalogEndPoint, username, password, config.verifyTLS, _, err = makeGETRequest(ctx, catalogEndPoint, username, password, config.VerifyTLS,
config.debug, catalog, config.resultWriter) config.Debug, catalog, config.ResultWriter)
if err != nil { if err != nil {
if isContextDone(ctx) { if isContextDone(ctx) {
return return
@ -1334,15 +1334,15 @@ func (service searchService) getRepos(ctx context.Context, config searchConfig,
return return
} }
fmt.Fprintln(config.resultWriter, "\nREPOSITORY NAME") fmt.Fprintln(config.ResultWriter, "\nREPOSITORY NAME")
if config.sortBy == SortByAlphabeticAsc { if config.SortBy == SortByAlphabeticAsc {
for i := 0; i < len(catalog.Repositories); i++ { for i := 0; i < len(catalog.Repositories); i++ {
fmt.Fprintln(config.resultWriter, catalog.Repositories[i]) fmt.Fprintln(config.ResultWriter, catalog.Repositories[i])
} }
} else { } else {
for i := len(catalog.Repositories) - 1; i >= 0; i-- { for i := len(catalog.Repositories) - 1; i >= 0; i-- {
fmt.Fprintln(config.resultWriter, catalog.Repositories[i]) fmt.Fprintln(config.ResultWriter, catalog.Repositories[i])
} }
} }
} }

View file

@ -30,31 +30,31 @@ func ref[T any](input T) *T {
return &ref return &ref
} }
func fetchImageDigest(repo, ref, username, password string, config searchConfig) (string, error) { func fetchImageDigest(repo, ref, username, password string, config SearchConfig) (string, error) {
url, err := combineServerAndEndpointURL(config.servURL, fmt.Sprintf("/v2/%s/manifests/%s", repo, ref)) url, err := combineServerAndEndpointURL(config.ServURL, fmt.Sprintf("/v2/%s/manifests/%s", repo, ref))
if err != nil { if err != nil {
return "", err return "", err
} }
res, err := makeHEADRequest(context.Background(), url, username, password, config.verifyTLS, false) res, err := makeHEADRequest(context.Background(), url, username, password, config.VerifyTLS, false)
digestStr := res.Get(constants.DistContentDigestKey) digestStr := res.Get(constants.DistContentDigestKey)
return digestStr, err return digestStr, err
} }
func collectResults(config searchConfig, wg *sync.WaitGroup, imageErr chan stringResult, func collectResults(config SearchConfig, wg *sync.WaitGroup, imageErr chan stringResult,
cancel context.CancelFunc, printHeader printHeader, errCh chan error, cancel context.CancelFunc, printHeader printHeader, errCh chan error,
) { ) {
var foundResult bool var foundResult bool
defer wg.Done() defer wg.Done()
config.spinner.startSpinner() config.Spinner.startSpinner()
for { for {
select { select {
case result, ok := <-imageErr: case result, ok := <-imageErr:
config.spinner.stopSpinner() config.Spinner.stopSpinner()
if !ok { if !ok {
cancel() cancel()
@ -69,18 +69,18 @@ func collectResults(config searchConfig, wg *sync.WaitGroup, imageErr chan strin
return return
} }
if !foundResult && (config.outputFormat == defaultOutputFormat || config.outputFormat == "") { if !foundResult && (config.OutputFormat == defaultOutputFormat || config.OutputFormat == "") {
var builder strings.Builder var builder strings.Builder
printHeader(&builder, config.verbose, 0, 0, 0) printHeader(&builder, config.Verbose, 0, 0, 0)
fmt.Fprint(config.resultWriter, builder.String()) fmt.Fprint(config.ResultWriter, builder.String())
} }
foundResult = true foundResult = true
fmt.Fprint(config.resultWriter, result.StrValue) fmt.Fprint(config.ResultWriter, result.StrValue)
case <-time.After(waitTimeout): case <-time.After(waitTimeout):
config.spinner.stopSpinner() config.Spinner.stopSpinner()
cancel() cancel()
errCh <- zerr.ErrCLITimeout errCh <- zerr.ErrCLITimeout
@ -193,8 +193,8 @@ func printCVETableHeader(writer io.Writer) {
table.Render() table.Render()
} }
func printReferrersTableHeader(config searchConfig, writer io.Writer, maxArtifactTypeLen int) { func printReferrersTableHeader(config SearchConfig, writer io.Writer, maxArtifactTypeLen int) {
if config.outputFormat != "" && config.outputFormat != defaultOutputFormat { if config.OutputFormat != "" && config.OutputFormat != defaultOutputFormat {
return return
} }
@ -269,18 +269,18 @@ func printRepoTableHeader(writer io.Writer, repoMaxLen, maxTimeLen int, verbose
table.Render() table.Render()
} }
func printReferrersResult(config searchConfig, referrersList referrersResult, maxArtifactTypeLen int) error { func printReferrersResult(config SearchConfig, referrersList referrersResult, maxArtifactTypeLen int) error {
out, err := referrersList.string(config.outputFormat, maxArtifactTypeLen) out, err := referrersList.string(config.OutputFormat, maxArtifactTypeLen)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprint(config.resultWriter, out) fmt.Fprint(config.ResultWriter, out)
return nil return nil
} }
func printImageResult(config searchConfig, imageList []imageStruct) error { func printImageResult(config SearchConfig, imageList []imageStruct) error {
var builder strings.Builder var builder strings.Builder
maxImgNameLen := 0 maxImgNameLen := 0
maxTagLen := 0 maxTagLen := 0
@ -305,29 +305,29 @@ func printImageResult(config searchConfig, imageList []imageStruct) error {
} }
} }
if config.outputFormat == defaultOutputFormat || config.outputFormat == "" { if config.OutputFormat == defaultOutputFormat || config.OutputFormat == "" {
printImageTableHeader(&builder, config.verbose, maxImgNameLen, maxTagLen, maxPlatformLen) printImageTableHeader(&builder, config.Verbose, maxImgNameLen, maxTagLen, maxPlatformLen)
} }
fmt.Fprint(config.resultWriter, builder.String()) fmt.Fprint(config.ResultWriter, builder.String())
} }
for i := range imageList { for i := range imageList {
img := imageList[i] img := imageList[i]
verbose := config.verbose verbose := config.Verbose
out, err := img.string(config.outputFormat, maxImgNameLen, maxTagLen, maxPlatformLen, verbose) out, err := img.string(config.OutputFormat, maxImgNameLen, maxTagLen, maxPlatformLen, verbose)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprint(config.resultWriter, out) fmt.Fprint(config.ResultWriter, out)
} }
return nil return nil
} }
func printRepoResults(config searchConfig, repoList []repoStruct) error { func printRepoResults(config SearchConfig, repoList []repoStruct) error {
maxRepoNameLen := 0 maxRepoNameLen := 0
maxTimeLen := 0 maxTimeLen := 0
@ -341,31 +341,31 @@ func printRepoResults(config searchConfig, repoList []repoStruct) error {
} }
} }
if len(repoList) > 0 && (config.outputFormat == defaultOutputFormat || config.outputFormat == "") { if len(repoList) > 0 && (config.OutputFormat == defaultOutputFormat || config.OutputFormat == "") {
printRepoTableHeader(config.resultWriter, maxRepoNameLen, maxTimeLen, config.verbose) printRepoTableHeader(config.ResultWriter, maxRepoNameLen, maxTimeLen, config.Verbose)
} }
for _, repo := range repoList { for _, repo := range repoList {
out, err := repo.string(config.outputFormat, maxRepoNameLen, maxTimeLen, config.verbose) out, err := repo.string(config.OutputFormat, maxRepoNameLen, maxTimeLen, config.Verbose)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprint(config.resultWriter, out) fmt.Fprint(config.ResultWriter, out)
} }
return nil return nil
} }
func GetSearchConfigFromFlags(cmd *cobra.Command, searchService SearchService) (searchConfig, error) { func GetSearchConfigFromFlags(cmd *cobra.Command, searchService SearchService) (SearchConfig, error) {
serverURL, err := GetServerURLFromFlags(cmd) serverURL, err := GetServerURLFromFlags(cmd)
if err != nil { if err != nil {
return searchConfig{}, err return SearchConfig{}, err
} }
isSpinner, verifyTLS, err := GetCliConfigOptions(cmd) isSpinner, verifyTLS, err := GetCliConfigOptions(cmd)
if err != nil { if err != nil {
return searchConfig{}, err return SearchConfig{}, err
} }
flags := cmd.Flags() flags := cmd.Flags()
@ -379,18 +379,18 @@ func GetSearchConfigFromFlags(cmd *cobra.Command, searchService SearchService) (
spin := spinner.New(spinner.CharSets[39], spinnerDuration, spinner.WithWriter(cmd.ErrOrStderr())) spin := spinner.New(spinner.CharSets[39], spinnerDuration, spinner.WithWriter(cmd.ErrOrStderr()))
spin.Prefix = prefix spin.Prefix = prefix
return searchConfig{ return SearchConfig{
searchService: searchService, SearchService: searchService,
servURL: serverURL, ServURL: serverURL,
user: user, User: user,
outputFormat: outputFormat, OutputFormat: outputFormat,
verifyTLS: verifyTLS, VerifyTLS: verifyTLS,
fixedFlag: fixed, FixedFlag: fixed,
verbose: verbose, Verbose: verbose,
debug: debug, Debug: debug,
sortBy: sortBy, SortBy: sortBy,
spinner: spinnerState{spin, isSpinner}, Spinner: spinnerState{spin, isSpinner},
resultWriter: cmd.OutOrStdout(), ResultWriter: cmd.OutOrStdout(),
}, nil }, nil
} }

View file

@ -20,19 +20,19 @@ import (
test "zotregistry.io/zot/pkg/test/common" test "zotregistry.io/zot/pkg/test/common"
) )
func getDefaultSearchConf(baseURL string) searchConfig { func getDefaultSearchConf(baseURL string) SearchConfig {
verifyTLS := false verifyTLS := false
debug := false debug := false
verbose := true verbose := true
outputFormat := "text" outputFormat := "text"
return searchConfig{ return SearchConfig{
servURL: baseURL, ServURL: baseURL,
resultWriter: io.Discard, ResultWriter: io.Discard,
verifyTLS: verifyTLS, VerifyTLS: verifyTLS,
debug: debug, Debug: debug,
verbose: verbose, Verbose: verbose,
outputFormat: outputFormat, OutputFormat: outputFormat,
} }
} }