Skip to content

Commit

Permalink
feat(cli): refactor
Browse files Browse the repository at this point in the history
Signed-off-by: Laurentiu Niculae <[email protected]>
  • Loading branch information
laurentiuNiculae committed Aug 8, 2023
1 parent ed90e3b commit 5fb6c6f
Show file tree
Hide file tree
Showing 38 changed files with 2,318 additions and 181 deletions.
7 changes: 5 additions & 2 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var (
ErrInvalidArgs = errors.New("cli: Invalid Arguments")
ErrInvalidFlagsCombination = errors.New("cli: Invalid combination of flags")
ErrInvalidURL = errors.New("cli: invalid URL format")
ErrExtensionNotEnabled = errors.New("cli: functionality is not built in current version")
ErrExtensionNotEnabled = errors.New("cli: functionality is not built in current server version")
ErrUnauthorizedAccess = errors.New("auth: unauthorized access. check credentials")
ErrCannotResetConfigKey = errors.New("cli: cannot reset given config key")
ErrConfigNotFound = errors.New("cli: config with the given name does not exist")
Expand Down Expand Up @@ -74,7 +74,7 @@ var (
ErrEmptyRepoName = errors.New("metadb: repo name can't be empty string")
ErrEmptyTag = errors.New("metadb: tag can't be empty string")
ErrEmptyDigest = errors.New("metadb: digest can't be empty string")
ErrInvalidRepoRefFormat = errors.New("invalid image reference format")
ErrInvalidRepoRefFormat = errors.New("invalid image reference format [repo:tag] or [repo@digest]")
ErrLimitIsNegative = errors.New("pageturner: limit has negative value")
ErrOffsetIsNegative = errors.New("pageturner: offset has negative value")
ErrSortCriteriaNotSupported = errors.New("pageturner: the sort criteria is not supported")
Expand Down Expand Up @@ -110,4 +110,7 @@ var (
ErrInvalidPublicKeyContent = errors.New("signatures: invalid public key content")
ErrInvalidStateCookie = errors.New("auth: state cookie not present or differs from original state")
ErrSyncNoURLsLeft = errors.New("sync: no valid registry urls left after filtering local ones")
ErrInvalidCLIParameter = errors.New("cli: invalid parameter")
ErrGQLEndpointNotFound = errors.New("cli: the server doesn't have a gql endpoint")
ErrGQLQueryNotSupported = errors.New("cli: query is not supported or has different arguments")
)
2 changes: 2 additions & 0 deletions pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import "github.com/spf13/cobra"
func enableCli(rootCmd *cobra.Command) {
rootCmd.AddCommand(NewConfigCommand())
rootCmd.AddCommand(NewImageCommand(NewSearchService()))
rootCmd.AddCommand(NewImagesCommand(NewSearchService()))
rootCmd.AddCommand(NewCveCommand(NewSearchService()))
rootCmd.AddCommand(NewCVESCommand(NewSearchService()))
rootCmd.AddCommand(NewRepoCommand(NewSearchService()))
rootCmd.AddCommand(NewSearchCommand(NewSearchService()))
}
4 changes: 2 additions & 2 deletions pkg/cli/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func doHTTPRequest(req *http.Request, verifyTLS bool, debug bool,

if debug {
fmt.Fprintln(configWriter, "[debug] ", req.Method, req.URL, "[status] ",
resp.StatusCode, " ", "[respoonse header] ", resp.Header)
resp.StatusCode, " ", "[response header] ", resp.Header)
}

defer resp.Body.Close()
Expand All @@ -136,7 +136,7 @@ func doHTTPRequest(req *http.Request, verifyTLS bool, debug bool,

bodyBytes, _ := io.ReadAll(resp.Body)

return nil, errors.New(string(bodyBytes)) //nolint: goerr113
return nil, errors.New(resp.Status + " " + string(bodyBytes)) //nolint: goerr113
}

if resultsPtr == nil {
Expand Down
13 changes: 13 additions & 0 deletions pkg/cli/cmdflags/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cmdflags

const (
URLFlag = "url"
ConfigFlag = "config"
UserFlag = "user"
OutputFormatFlag = "output"
FixedFlag = "fixed"
VerboseFlag = "verbose"
VersionFlag = "version"
DebugFlag = "debug"
SearchedCVEID = "id"
)
2 changes: 1 addition & 1 deletion pkg/cli/cve_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

const (
cveDBRetryInterval = 3
cveDBRetryInterval = 1
)

func NewCveCommand(searchService SearchService) *cobra.Command {
Expand Down
132 changes: 131 additions & 1 deletion pkg/cli/cve_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
zotErrors "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/cli/cmdflags"
zcommon "zotregistry.io/zot/pkg/common"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/extensions/monitoring"
Expand Down Expand Up @@ -1056,6 +1057,135 @@ func TestServerCVEResponse(t *testing.T) {
})
}

func TestCVECommandGQL(t *testing.T) {
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},
},
}

ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)

cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()

Convey("commands without gql", t, func() {
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL))
defer os.Remove(configPath)

Convey("cveid", func() {
args := []string{"cveid", "CVE-1942"}
cmd := NewCVESCommand(mockService{})
cmd.PersistentFlags().String(cmdflags.ConfigFlag, "cvetest", "")
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "image-name tag 6e2f80bf false 123kB")
})

Convey("fixed", func() {
args := []string{"fixed", "image-name", "CVE-123"}
cmd := NewCVESCommand(mockService{})
cmd.PersistentFlags().String(cmdflags.ConfigFlag, "cvetest", "")
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "image-name tag 6e2f80bf false 123kB")
})

Convey("image", func() {
args := []string{"image", "repo:tag"}
cmd := NewCVESCommand(mockService{})
cmd.PersistentFlags().String(cmdflags.ConfigFlag, "cvetest", "")
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE")
})
})
}

func TestCVECommandREST(t *testing.T) {
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port

ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)

cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()

Convey("commands without gql", t, func() {
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL))
defer os.Remove(configPath)

Convey("cveid", func() {
args := []string{"cveid", "CVE-1942"}
cmd := NewCVESCommand(mockService{})
cmd.PersistentFlags().String(cmdflags.ConfigFlag, "cvetest", "")
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})

Convey("fixed", func() {
args := []string{"fixed", "image-name", "CVE-123"}
cmd := NewCVESCommand(mockService{})
cmd.PersistentFlags().String(cmdflags.ConfigFlag, "cvetest", "")
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})

Convey("image", func() {
args := []string{"image", "repo:tag"}
cmd := NewCVESCommand(mockService{})
cmd.PersistentFlags().String(cmdflags.ConfigFlag, "cvetest", "")
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}

func MockNewCveCommand(searchService SearchService) *cobra.Command {
searchCveParams := make(map[string]*string)

Expand Down Expand Up @@ -1282,7 +1412,7 @@ type mockServiceForRetry struct {
}

func (service *mockServiceForRetry) getImagesByCveID(ctx context.Context, config searchConfig,
username, password, cvid string, rch chan stringResult, wtgrp *sync.WaitGroup,
username, password, cveid string, rch chan stringResult, wtgrp *sync.WaitGroup,
) {
service.retryCounter += 1

Expand Down
30 changes: 30 additions & 0 deletions pkg/cli/cves_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//go:build search
// +build search

package cli

import (
"github.com/spf13/cobra"

"zotregistry.io/zot/pkg/cli/cmdflags"
)

func NewCVESCommand(searchService SearchService) *cobra.Command {
cvesCmd := &cobra.Command{
Use: "cves [command]",
Short: "Lookup CVEs in images hosted on the zot registry",
Long: `List CVEs (Common Vulnerabilities and Exposures) of images hosted on the zot registry`,
}

cvesCmd.SetUsageTemplate(cvesCmd.UsageTemplate() + usageFooter)

cvesCmd.PersistentFlags().StringP(cmdflags.OutputFormatFlag, "o", "", "Specify output format [text/json/yaml]")
cvesCmd.PersistentFlags().Bool(cmdflags.VerboseFlag, false, "Show verbose output")
cvesCmd.PersistentFlags().Bool(cmdflags.DebugFlag, false, "Show debug output")

cvesCmd.AddCommand(NewCveForImageCommand(searchService))
cvesCmd.AddCommand(NewImagesByCVEIDCommand(searchService))
cvesCmd.AddCommand(NewFixedTagsCommand(searchService))

return cvesCmd
}
Loading

0 comments on commit 5fb6c6f

Please sign in to comment.