Signed-off-by: Lisca Ana-Roberta <ana.kagome@yahoo.com> (#713)

list all images that have are base images for the given image + zli command

Signed-off-by: Lisca Ana-Roberta <ana.kagome@yahoo.com>
This commit is contained in:
Lisca Ana-Roberta
2022-09-23 19:23:31 +03:00
committed by GitHub
parent 944ae66844
commit 0f7b174fc0
8 changed files with 815 additions and 44 deletions
+2
View File
@@ -121,6 +121,8 @@ func setupImageFlags(imageCmd *cobra.Command, searchImageParams map[string]*stri
searchImageParams["imageName"] = imageCmd.Flags().StringP("name", "n", "", "List image details by name")
searchImageParams["digest"] = imageCmd.Flags().StringP("digest", "d", "",
"List images containing a specific manifest, config, or layer digest")
searchImageParams["derivedImage"] = imageCmd.Flags().StringP("derived-images", "D", "",
"List images that are derived from given image")
searchImageParams["baseImage"] = imageCmd.Flags().StringP("base-images", "b", "",
"List images that are base for the given image")
+111
View File
@@ -250,6 +250,101 @@ func TestSearchImageCmd(t *testing.T) {
})
}
// nolint: dupl
func TestDerivedImageList(t *testing.T) {
Convey("Test from real server", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{Enable: &defaultVal},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
go func(controller *api.Controller) {
// this blocks
if err := controller.Run(context.Background()); err != nil {
return
}
}(ctlr)
// wait till ready
for {
_, err := resty.R().Get(url)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
defer func(controller *api.Controller) {
ctx := context.Background()
_ = controller.Server.Shutdown(ctx)
}(ctlr)
err := uploadManifest(url)
So(err, ShouldBeNil)
t.Logf("rootDir: %s", ctlr.Config.Storage.RootDirectory)
Convey("Test derived images list working", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
args := []string{"imagetest", "--derived-images", "repo7:test:1.0"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(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 := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 492B")
})
Convey("Test derived images fail", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
err = os.Chmod(ctlr.Config.Storage.RootDirectory, 0o000)
So(err, ShouldBeNil)
defer func() {
err := os.Chmod(ctlr.Config.Storage.RootDirectory, 0o755)
So(err, ShouldBeNil)
}()
args := []string{"imagetest", "--derived-images", "repo7:test:1.0"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldNotBeNil)
})
Convey("Test derived images list cannot print", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
args := []string{"imagetest", "--derived-images", "repo7:test:1.0", "-o", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService))
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
})
})
}
// nolint: dupl
func TestBaseImageList(t *testing.T) {
Convey("Test from real server", t, func() {
port := test.GetFreePort()
@@ -1265,6 +1360,22 @@ func (service mockService) getRepos(ctx context.Context, config searchConfig, us
channel <- stringResult{"", nil}
}
func (service mockService) getDerivedImageListGQL(ctx context.Context, config searchConfig, username, password string,
derivedImage string,
) (*imageListStructForDerivedImagesGQL, error) {
imageListGQLResponse := &imageListStructForDerivedImagesGQL{}
imageListGQLResponse.Data.ImageList = []imageStruct{
{
RepoName: "dummyImageName",
Tag: "tag",
Digest: "DigestsAreReallyLong",
Size: "123445",
},
}
return imageListGQLResponse, nil
}
func (service mockService) getBaseImageListGQL(ctx context.Context, config searchConfig, username, password string,
derivedImage string,
) (*imageListStructForBaseImagesGQL, error) {
+26
View File
@@ -42,6 +42,7 @@ func getImageSearchersGQL() []searcher {
new(allImagesSearcherGQL),
new(imageByNameSearcherGQL),
new(imagesByDigestSearcherGQL),
new(derivedImageListSearcherGQL),
new(baseImageListSearcherGQL),
}
@@ -220,6 +221,31 @@ func (search imagesByDigestSearcher) search(config searchConfig) (bool, error) {
}
}
type derivedImageListSearcherGQL struct{}
func (search derivedImageListSearcherGQL) search(config searchConfig) (bool, error) {
if !canSearch(config.params, newSet("derivedImage")) {
return false, nil
}
username, password := getUsernameAndPassword(*config.user)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
imageList, err := config.searchService.getDerivedImageListGQL(ctx, config, username,
password, *config.params["derivedImage"])
if err != nil {
return true, err
}
if err := printResult(config, imageList.Data.ImageList); err != nil {
return true, err
}
return true, nil
}
type baseImageListSearcherGQL struct{}
func (search baseImageListSearcherGQL) search(config searchConfig) (bool, error) {
+35
View File
@@ -34,6 +34,8 @@ type SearchService interface {
cveID string) (*imagesForCve, error)
getFixedTagsForCVEGQL(ctx context.Context, config searchConfig, username, password, imageName,
cveID string) (*fixedTags, error)
getDerivedImageListGQL(ctx context.Context, config searchConfig, username, password string,
derivedImage string) (*imageListStructForDerivedImagesGQL, error)
getBaseImageListGQL(ctx context.Context, config searchConfig, username, password string,
baseImage string) (*imageListStructForBaseImagesGQL, error)
@@ -61,6 +63,32 @@ func NewSearchService() SearchService {
return searchService{}
}
func (service searchService) getDerivedImageListGQL(ctx context.Context, config searchConfig, username, password string,
derivedImage string,
) (*imageListStructForDerivedImagesGQL, error) {
query := fmt.Sprintf(`
{
DerivedImageList(image:"%s"){
RepoName,
Tag,
Digest,
ConfigDigest,
LastUpdated,
IsSigned,
Size
}
}`, derivedImage)
result := &imageListStructForDerivedImagesGQL{}
err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
if errResult := checkResultGraphQLQuery(ctx, err, result.Errors); errResult != nil {
return nil, errResult
}
return result, nil
}
func (service searchService) getBaseImageListGQL(ctx context.Context, config searchConfig, username, password string,
baseImage string,
) (*imageListStructForBaseImagesGQL, error) {
@@ -831,6 +859,13 @@ type imageListStructForDigestGQL struct {
} `json:"data"`
}
type imageListStructForDerivedImagesGQL struct {
Errors []errorGraphQL `json:"errors"`
Data struct {
ImageList []imageStruct `json:"DerivedImageList"` // nolint:tagliatelle
} `json:"data"`
}
type imageListStructForBaseImagesGQL struct {
Errors []errorGraphQL `json:"errors"`
Data struct {