From 1bad90bb9d682881f2073a5237eec7d90a9f044e Mon Sep 17 00:00:00 2001 From: Lisca Ana-Roberta <55219463+aokirisaki@users.noreply.github.com> Date: Fri, 23 Sep 2022 19:24:01 +0300 Subject: [PATCH] add debug flag for zli commands (#785) Signed-off-by: Lisca Ana-Roberta Signed-off-by: Lisca Ana-Roberta --- pkg/cli/client.go | 24 ++++++++++++++++------ pkg/cli/cve_cmd.go | 6 +++++- pkg/cli/cve_cmd_test.go | 21 ++++++++++++++++++- pkg/cli/image_cmd.go | 8 +++++--- pkg/cli/image_cmd_test.go | 43 +++++++++++++++++++++++++++++++++++++-- pkg/cli/repo_cmd.go | 4 +++- pkg/cli/searcher.go | 1 + pkg/cli/service.go | 12 +++++++---- 8 files changed, 101 insertions(+), 18 deletions(-) diff --git a/pkg/cli/client.go b/pkg/cli/client.go index 52793ed1..2336de49 100644 --- a/pkg/cli/client.go +++ b/pkg/cli/client.go @@ -10,6 +10,7 @@ import ( "crypto/x509" "encoding/json" "errors" + "fmt" "io" "net/http" "net/url" @@ -66,7 +67,7 @@ func createHTTPClient(verifyTLS bool, host string) *http.Client { } func makeGETRequest(ctx context.Context, url, username, password string, - verifyTLS bool, resultsPtr interface{}, + verifyTLS bool, debug bool, resultsPtr interface{}, configWriter io.Writer, ) (http.Header, error) { req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { @@ -75,11 +76,11 @@ func makeGETRequest(ctx context.Context, url, username, password string, req.SetBasicAuth(username, password) - return doHTTPRequest(req, verifyTLS, resultsPtr) + return doHTTPRequest(req, verifyTLS, debug, resultsPtr, configWriter) } func makeGraphQLRequest(ctx context.Context, url, query, username, - password string, verifyTLS bool, resultsPtr interface{}, + password string, verifyTLS bool, debug bool, resultsPtr interface{}, configWriter io.Writer, ) error { req, err := http.NewRequestWithContext(ctx, "GET", url, bytes.NewBufferString(query)) if err != nil { @@ -94,7 +95,7 @@ func makeGraphQLRequest(ctx context.Context, url, query, username, req.SetBasicAuth(username, password) req.Header.Add("Content-Type", "application/json") - _, err = doHTTPRequest(req, verifyTLS, resultsPtr) + _, err = doHTTPRequest(req, verifyTLS, debug, resultsPtr, configWriter) if err != nil { return err } @@ -102,7 +103,9 @@ func makeGraphQLRequest(ctx context.Context, url, query, username, return nil } -func doHTTPRequest(req *http.Request, verifyTLS bool, resultsPtr interface{}) (http.Header, error) { +func doHTTPRequest(req *http.Request, verifyTLS bool, debug bool, + resultsPtr interface{}, configWriter io.Writer, +) (http.Header, error) { var httpClient *http.Client host := req.Host @@ -119,11 +122,20 @@ func doHTTPRequest(req *http.Request, verifyTLS bool, resultsPtr interface{}) (h httpClientLock.Unlock() + if debug { + fmt.Fprintln(configWriter, "[debug] ", req.Method, " ", req.URL, "[request header] ", req.Header) + } + resp, err := httpClient.Do(req) if err != nil { return nil, err } + if debug { + fmt.Fprintln(configWriter, "[debug] ", req.Method, req.URL, "[status] ", + resp.StatusCode, " ", "[respoonse header] ", resp.Header) + } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -252,7 +264,7 @@ func (p *requestsPool) doJob(ctx context.Context, job *manifestJob) { defer p.wtgrp.Done() header, err := makeGETRequest(ctx, job.url, job.username, job.password, - *job.config.verifyTLS, &job.manifestResp) + *job.config.verifyTLS, *job.config.debug, &job.manifestResp, job.config.resultWriter) if err != nil { if isContextDone(ctx) { return diff --git a/pkg/cli/cve_cmd.go b/pkg/cli/cve_cmd.go index fdc72e2a..a995c2d0 100644 --- a/pkg/cli/cve_cmd.go +++ b/pkg/cli/cve_cmd.go @@ -23,7 +23,7 @@ func NewCveCommand(searchService SearchService) *cobra.Command { var servURL, user, outputFormat string - var isSpinner, verifyTLS, fixedFlag, verbose bool + var isSpinner, verifyTLS, fixedFlag, verbose, debug bool cveCmd := &cobra.Command{ Use: "cve [config-name]", @@ -87,6 +87,7 @@ func NewCveCommand(searchService SearchService) *cobra.Command { fixedFlag: &fixedFlag, verifyTLS: &verifyTLS, verbose: &verbose, + debug: &debug, resultWriter: cmd.OutOrStdout(), spinner: spinnerState{spin, isSpinner}, } @@ -109,6 +110,7 @@ func NewCveCommand(searchService SearchService) *cobra.Command { user: &user, outputFormat: &outputFormat, fixedFlag: &fixedFlag, + debug: &debug, } setupCveFlags(cveCmd, vars) @@ -127,6 +129,7 @@ func setupCveFlags(cveCmd *cobra.Command, variables cveFlagVariables) { " JSON and YAML format return all info for CVEs") cveCmd.Flags().BoolVar(variables.fixedFlag, "fixed", false, "List tags which have fixed a CVE") + cveCmd.Flags().BoolVar(variables.debug, "debug", false, "Show debug output") } type cveFlagVariables struct { @@ -135,6 +138,7 @@ type cveFlagVariables struct { user *string outputFormat *string fixedFlag *bool + debug *bool } type field struct { diff --git a/pkg/cli/cve_cmd_test.go b/pkg/cli/cve_cmd_test.go index 8f277810..c1bd3fa6 100644 --- a/pkg/cli/cve_cmd_test.go +++ b/pkg/cli/cve_cmd_test.go @@ -152,6 +152,22 @@ func TestSearchCVECmd(t *testing.T) { So(err, ShouldBeNil) }) + Convey("Test debug flag", t, func() { + args := []string{"cvetest", "--image", "dummyImageName:tag", "--debug"} + configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","url":"https://test-url.com","showspinner":false}]}`) + defer os.Remove(configPath) + cmd := NewCveCommand(new(searchService)) + buff := bytes.NewBufferString("") + cmd.SetOut(buff) + cmd.SetErr(buff) + cmd.SetArgs(args) + err := cmd.Execute() + space := regexp.MustCompile(`\s+`) + str := space.ReplaceAllString(buff.String(), " ") + So(strings.TrimSpace(str), ShouldContainSubstring, "GET") + So(err, ShouldNotBeNil) + }) + Convey("Test CVE by name and CVE ID", t, func() { args := []string{"cvetest", "--image", "dummyImageName", "--cve-id", "aCVEID", "--url", "someURL"} configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) @@ -935,7 +951,7 @@ func MockNewCveCommand(searchService SearchService) *cobra.Command { var servURL, user, outputFormat string - var verifyTLS, fixedFlag, verbose bool + var verifyTLS, fixedFlag, verbose, debug bool cveCmd := &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { @@ -974,6 +990,7 @@ func MockNewCveCommand(searchService SearchService) *cobra.Command { } verbose = false + debug = false searchConfig := searchConfig{ params: searchCveParams, @@ -984,6 +1001,7 @@ func MockNewCveCommand(searchService SearchService) *cobra.Command { fixedFlag: &fixedFlag, verifyTLS: &verifyTLS, verbose: &verbose, + debug: &debug, resultWriter: cmd.OutOrStdout(), } @@ -1005,6 +1023,7 @@ func MockNewCveCommand(searchService SearchService) *cobra.Command { user: &user, outputFormat: &outputFormat, fixedFlag: &fixedFlag, + debug: &debug, } setupCveFlags(cveCmd, vars) diff --git a/pkg/cli/image_cmd.go b/pkg/cli/image_cmd.go index 82d0e97c..35f5073b 100644 --- a/pkg/cli/image_cmd.go +++ b/pkg/cli/image_cmd.go @@ -19,7 +19,7 @@ func NewImageCommand(searchService SearchService) *cobra.Command { var servURL, user, outputFormat string - var isSpinner, verifyTLS, verbose bool + var isSpinner, verifyTLS, verbose, debug bool imageCmd := &cobra.Command{ Use: "images [config-name]", @@ -78,6 +78,7 @@ func NewImageCommand(searchService SearchService) *cobra.Command { user: &user, outputFormat: &outputFormat, verbose: &verbose, + debug: &debug, spinner: spinnerState{spin, isSpinner}, verifyTLS: &verifyTLS, resultWriter: cmd.OutOrStdout(), @@ -95,7 +96,7 @@ func NewImageCommand(searchService SearchService) *cobra.Command { }, } - setupImageFlags(imageCmd, searchImageParams, &servURL, &user, &outputFormat, &verbose) + setupImageFlags(imageCmd, searchImageParams, &servURL, &user, &outputFormat, &verbose, &debug) imageCmd.SetUsageTemplate(imageCmd.UsageTemplate() + usageFooter) return imageCmd @@ -116,7 +117,7 @@ func parseBooleanConfig(configPath, configName, configParam string) (bool, error } func setupImageFlags(imageCmd *cobra.Command, searchImageParams map[string]*string, - servURL, user, outputFormat *string, verbose *bool, + servURL, user, outputFormat *string, verbose *bool, debug *bool, ) { searchImageParams["imageName"] = imageCmd.Flags().StringP("name", "n", "", "List image details by name") searchImageParams["digest"] = imageCmd.Flags().StringP("digest", "d", "", @@ -130,6 +131,7 @@ func setupImageFlags(imageCmd *cobra.Command, searchImageParams map[string]*stri imageCmd.Flags().StringVarP(user, "user", "u", "", `User Credentials of zot server in "username:password" format`) imageCmd.Flags().StringVarP(outputFormat, "output", "o", "", "Specify output format [text/json/yaml]") imageCmd.Flags().BoolVar(verbose, "verbose", false, "Show verbose output") + imageCmd.Flags().BoolVar(debug, "debug", false, "Show debug output") } func searchImage(searchConfig searchConfig) error { diff --git a/pkg/cli/image_cmd_test.go b/pkg/cli/image_cmd_test.go index 96328f7c..fa4de9da 100644 --- a/pkg/cli/image_cmd_test.go +++ b/pkg/cli/image_cmd_test.go @@ -452,6 +452,24 @@ func TestListRepos(t *testing.T) { So(err, ShouldBeNil) }) + Convey("Test listing repositories with debug flag", t, func() { + args := []string{"config-test", "--debug"} + configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`) + defer os.Remove(configPath) + cmd := NewRepoCommand(new(searchService)) + + buff := bytes.NewBufferString("") + cmd.SetOut(buff) + cmd.SetErr(buff) + cmd.SetArgs(args) + err := cmd.Execute() + So(err, ShouldNotBeNil) + space := regexp.MustCompile(`\s+`) + str := space.ReplaceAllString(buff.String(), " ") + actual := strings.TrimSpace(str) + So(actual, ShouldContainSubstring, "GET") + }) + Convey("Test error on home directory", t, func() { args := []string{"config-test"} @@ -753,6 +771,26 @@ func TestServerResponseGQL(t *testing.T) { So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 3a1d2d0c 15B b8781e88 15B") }) + Convey("Test all images with debug flag", func() { + args := []string{"imagetest", "--debug"} + configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) + defer os.Remove(configPath) + cmd := NewImageCommand(new(searchService)) + 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, "GET") + So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST SIZE") + So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 15B") + So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 15B") + }) + Convey("Test image by name config url", func() { args := []string{"imagetest", "--name", "repo7"} configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) @@ -1183,7 +1221,7 @@ func MockNewImageCommand(searchService SearchService) *cobra.Command { var servURL, user, outputFormat string - var verifyTLS, verbose bool + var verifyTLS, verbose, debug bool imageCmd := &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { @@ -1228,6 +1266,7 @@ func MockNewImageCommand(searchService SearchService) *cobra.Command { user: &user, outputFormat: &outputFormat, verbose: &verbose, + debug: &debug, verifyTLS: &verifyTLS, resultWriter: cmd.OutOrStdout(), } @@ -1244,7 +1283,7 @@ func MockNewImageCommand(searchService SearchService) *cobra.Command { }, } - setupImageFlags(imageCmd, searchImageParams, &servURL, &user, &outputFormat, &verbose) + setupImageFlags(imageCmd, searchImageParams, &servURL, &user, &outputFormat, &verbose, &debug) imageCmd.SetUsageTemplate(imageCmd.UsageTemplate() + usageFooter) return imageCmd diff --git a/pkg/cli/repo_cmd.go b/pkg/cli/repo_cmd.go index 673d1786..71664f74 100644 --- a/pkg/cli/repo_cmd.go +++ b/pkg/cli/repo_cmd.go @@ -15,7 +15,7 @@ import ( func NewRepoCommand(searchService SearchService) *cobra.Command { var servURL, user, outputFormat string - var isSpinner, verifyTLS, verbose bool + var isSpinner, verifyTLS, verbose, debug bool repoCmd := &cobra.Command{ Use: "repos [config-name]", @@ -73,6 +73,7 @@ func NewRepoCommand(searchService SearchService) *cobra.Command { user: &user, outputFormat: &outputFormat, verbose: &verbose, + debug: &debug, spinner: spinnerState{spin, isSpinner}, verifyTLS: &verifyTLS, resultWriter: cmd.OutOrStdout(), @@ -94,6 +95,7 @@ func NewRepoCommand(searchService SearchService) *cobra.Command { repoCmd.Flags().StringVar(&servURL, "url", "", "Specify zot server URL if config-name is not mentioned") repoCmd.Flags().StringVarP(&user, "user", "u", "", `User Credentials of zot server in "username:password" format`) + repoCmd.Flags().BoolVar(&debug, "debug", false, "Show debug output") return repoCmd } diff --git a/pkg/cli/searcher.go b/pkg/cli/searcher.go index e07df57d..bf4aadbd 100644 --- a/pkg/cli/searcher.go +++ b/pkg/cli/searcher.go @@ -85,6 +85,7 @@ type searchConfig struct { verifyTLS *bool fixedFlag *bool verbose *bool + debug *bool resultWriter io.Writer spinner spinnerState } diff --git a/pkg/cli/service.go b/pkg/cli/service.go index cde7bb6b..1861d3e3 100644 --- a/pkg/cli/service.go +++ b/pkg/cli/service.go @@ -263,7 +263,8 @@ func (service searchService) getAllImages(ctx context.Context, config searchConf return } - _, err = makeGETRequest(ctx, catalogEndPoint, username, password, *config.verifyTLS, catalog) + _, err = makeGETRequest(ctx, catalogEndPoint, username, password, *config.verifyTLS, + *config.debug, catalog, config.resultWriter) if err != nil { if isContextDone(ctx) { return @@ -306,7 +307,8 @@ func getImage(ctx context.Context, config searchConfig, username, password, imag } tagList := &tagListResp{} - _, err = makeGETRequest(ctx, tagListEndpoint, username, password, *config.verifyTLS, &tagList) + _, err = makeGETRequest(ctx, tagListEndpoint, username, password, *config.verifyTLS, + *config.debug, &tagList, config.resultWriter) if err != nil { if isContextDone(ctx) { @@ -658,7 +660,8 @@ func (service searchService) makeGraphQLQuery(ctx context.Context, return err } - err = makeGraphQLRequest(ctx, endPoint, query, username, password, *config.verifyTLS, resultPtr) + err = makeGraphQLRequest(ctx, endPoint, query, username, password, *config.verifyTLS, + *config.debug, resultPtr, config.resultWriter) if err != nil { return err } @@ -1082,7 +1085,8 @@ func (service searchService) getRepos(ctx context.Context, config searchConfig, return } - _, err = makeGETRequest(ctx, catalogEndPoint, username, password, *config.verifyTLS, catalog) + _, err = makeGETRequest(ctx, catalogEndPoint, username, password, *config.verifyTLS, + *config.debug, catalog, config.resultWriter) if err != nil { if isContextDone(ctx) { return