feat(repodb): DerivedImageList and BaseImageList make use of RepoDB (#1135)

- derivedImageList and baseImageList now use FilterTags to obtain results,
each with its own filter function
- images that have the exact same manifest as the one provided as a
parameter are no longer considered base images or derived images
- both calls can be made with specific pagination parameters, and the
response will include PageInfo

Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro>

fix(tests): fix one of the pagination tests

The results were not reliable as the 2 returned tags were sorted by created date/time
which was not set, resulting in an unpredictable order

Signed-off-by: Andrei Aaron <andaaron@cisco.com>
(cherry picked from commit be504200a1127371422aeb0e5c0219e2a1ead20a)
(cherry picked from commit ed8d797e639f262a63840120afe92da7db9a7600)
Signed-off-by: Andrei Aaron <aaaron@luxoft.com>

Signed-off-by: Andrei Aaron <andaaron@cisco.com>
Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
Co-authored-by: Alex Stan <alexandrustan96@yahoo.ro>
This commit is contained in:
Andrei Aaron
2023-01-26 00:06:02 +02:00
committed by GitHub
parent be4b8c6243
commit feb7328f50
15 changed files with 1525 additions and 482 deletions
+104 -28
View File
@@ -483,7 +483,7 @@ func TestDerivedImageList(t *testing.T) {
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifest(url)
err := uploadManifestDerivedBase(url)
if err != nil {
panic(err)
}
@@ -493,7 +493,7 @@ func TestDerivedImageList(t *testing.T) {
Convey("Test from real server", t, func() {
Convey("Test derived images list working", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
args := []string{"imagetest", "--derived-images", "repo7:test:1.0"}
args := []string{"imagetest", "--derived-images", "repo7:test:2.0"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService))
@@ -507,19 +507,11 @@ func TestDerivedImageList(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 2694fdb0 false 824B")
})
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"}
Convey("Test derived images list fails", func() {
args := []string{"imagetest", "--derived-images", "repo7:test:missing"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService))
@@ -533,7 +525,7 @@ func TestDerivedImageList(t *testing.T) {
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"}
args := []string{"imagetest", "--derived-images", "repo7:test:2.0", "-o", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService))
@@ -564,7 +556,7 @@ func TestBaseImageList(t *testing.T) {
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifest(url)
err := uploadManifestDerivedBase(url)
if err != nil {
panic(err)
}
@@ -588,19 +580,11 @@ func TestBaseImageList(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:2.0 3fc80493 false 494B")
})
Convey("Test base 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", "--base-images", "repo7:test:1.0"}
Convey("Test base images list fail", func() {
args := []string{"imagetest", "--base-images", "repo7:test:missing"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService))
@@ -1481,6 +1465,98 @@ func uploadManifest(url string) error {
return nil
}
func uploadManifestDerivedBase(url string) error {
// create a blob/layer
_, _ = resty.R().Post(url + "/v2/repo7/blobs/uploads/")
content1 := []byte("this is a blob5.0")
content2 := []byte("this is a blob5.1")
content3 := []byte("this is a blob5.2")
digest1 := godigest.FromBytes(content1)
digest2 := godigest.FromBytes(content2)
digest3 := godigest.FromBytes(content3)
_, _ = resty.R().SetQueryParam("digest", digest1.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content1).Post(url + "/v2/repo7/blobs/uploads/")
_, _ = resty.R().SetQueryParam("digest", digest2.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content2).Post(url + "/v2/repo7/blobs/uploads/")
_, _ = resty.R().SetQueryParam("digest", digest3.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content3).Post(url + "/v2/repo7/blobs/uploads/")
// upload image config blob
resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc := test.Location(url, resp)
cblob, cdigest := test.GetImageConfig()
_, _ = resty.R().
SetContentLength(true).
SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", cdigest.String()).
SetBody(cblob).
Put(loc)
// create a manifest
manifest := ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest1,
Size: int64(len(content1)),
}, {
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest2,
Size: int64(len(content2)),
}, {
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest3,
Size: int64(len(content3)),
},
},
}
manifest.SchemaVersion = 2
content, err := json.Marshal(manifest)
if err != nil {
return err
}
_, _ = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(url + "/v2/repo7/manifests/test:1.0")
content1 = []byte("this is a blob5.0")
digest1 = godigest.FromBytes(content1)
// create a manifest with one common layer blob
manifest = ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest1,
Size: int64(len(content1)),
},
},
}
manifest.SchemaVersion = 2
content, err = json.Marshal(manifest)
if err != nil {
return err
}
_, _ = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(url + "/v2/repo7/manifests/test:2.0")
return nil
}
type mockService struct{}
func (service mockService) getRepos(ctx context.Context, config searchConfig, username,
@@ -1501,7 +1577,7 @@ func (service mockService) getDerivedImageListGQL(ctx context.Context, config se
derivedImage string,
) (*imageListStructForDerivedImagesGQL, error) {
imageListGQLResponse := &imageListStructForDerivedImagesGQL{}
imageListGQLResponse.Data.ImageList = []imageStruct{
imageListGQLResponse.Data.ImageList.Results = []imageStruct{
{
RepoName: "dummyImageName",
Tag: "tag",
@@ -1519,7 +1595,7 @@ func (service mockService) getBaseImageListGQL(ctx context.Context, config searc
derivedImage string,
) (*imageListStructForBaseImagesGQL, error) {
imageListGQLResponse := &imageListStructForBaseImagesGQL{}
imageListGQLResponse.Data.ImageList = []imageStruct{
imageListGQLResponse.Data.ImageList.Results = []imageStruct{
{
RepoName: "dummyImageName",
Tag: "tag",
+2 -2
View File
@@ -241,7 +241,7 @@ func (search derivedImageListSearcherGQL) search(config searchConfig) (bool, err
return true, err
}
if err := printResult(config, imageList.Data.ImageList); err != nil {
if err := printResult(config, imageList.Data.ImageList.Results); err != nil {
return true, err
}
@@ -266,7 +266,7 @@ func (search baseImageListSearcherGQL) search(config searchConfig) (bool, error)
return true, err
}
if err := printResult(config, imageList.Data.ImageList); err != nil {
if err := printResult(config, imageList.Data.ImageList.Results); err != nil {
return true, err
}
+29 -18
View File
@@ -71,14 +71,16 @@ func (service searchService) getDerivedImageListGQL(ctx context.Context, config
query := fmt.Sprintf(`
{
DerivedImageList(image:"%s"){
RepoName,
Tag,
Digest,
ConfigDigest,
Layers {Size Digest},
LastUpdated,
IsSigned,
Size
Results{
RepoName,
Tag,
Digest,
ConfigDigest,
Layers {Size Digest},
LastUpdated,
IsSigned,
Size
}
}
}`, derivedImage)
@@ -98,14 +100,16 @@ func (service searchService) getBaseImageListGQL(ctx context.Context, config sea
query := fmt.Sprintf(`
{
BaseImageList(image:"%s"){
RepoName,
Tag,
Digest,
ConfigDigest,
Layers {Size Digest},
LastUpdated,
IsSigned,
Size
Results{
RepoName,
Tag,
Digest,
ConfigDigest,
Layers {Size Digest},
LastUpdated,
IsSigned,
Size
}
}
}`, baseImage)
@@ -862,6 +866,13 @@ type imageStruct struct {
IsSigned bool `json:"isSigned"`
}
type DerivedImageList struct {
Results []imageStruct `json:"results"`
}
type BaseImageList struct {
Results []imageStruct `json:"results"`
}
type imageListStructGQL struct {
Errors []errorGraphQL `json:"errors"`
Data struct {
@@ -879,14 +890,14 @@ type imageListStructForDigestGQL struct {
type imageListStructForDerivedImagesGQL struct {
Errors []errorGraphQL `json:"errors"`
Data struct {
ImageList []imageStruct `json:"DerivedImageList"` //nolint:tagliatelle
ImageList DerivedImageList `json:"DerivedImageList"` //nolint:tagliatelle
} `json:"data"`
}
type imageListStructForBaseImagesGQL struct {
Errors []errorGraphQL `json:"errors"`
Data struct {
ImageList []imageStruct `json:"BaseImageList"` //nolint:tagliatelle
ImageList BaseImageList `json:"BaseImageList"` //nolint:tagliatelle
} `json:"data"`
}