diff --git a/README.md b/README.md index 80b779f..b385c4e 100644 --- a/README.md +++ b/README.md @@ -32,36 +32,52 @@ $ trivy $IMAGENAME:$(dockertags -limit 1 -format json $IMAGENAME | jq -r .[0].ta ## Examples ```bash -$ dockertags goodwithtech/dockle -+---------+-------+----------------------+-------------+ -| TAG | SIZE | CREATED AT | UPLOADED AT | -+---------+-------+----------------------+-------------+ -| latest | 21.1M | 2019-12-16T14:05:18Z | NULL | -| v0.2.4 | 21.1M | 2019-12-05T05:18:04Z | NULL | -| v0.2.3 | 21.1M | 2019-11-17T15:03:10Z | NULL | -| v0.2.2 | 21.1M | 2019-11-17T14:45:53Z | NULL | -...... -| v0.0.18 | 20.4M | 2019-06-10T18:31:45Z | NULL | -+---------+-------+----------------------+-------------+ +$ dockertags alpine ++----------+------+----------------------+-------------+ +| TAG | SIZE | CREATED AT | UPLOADED AT | ++----------+------+----------------------+-------------+ +| 3 | 2.7M | 2019-12-24T20:40:57Z | NULL | +| 3.11 | | | | +| latest | | | | +| 3.11.2 | | | | ++----------+------+----------------------+-------------+ +| edge | 2.7M | 2019-12-20T00:41:30Z | NULL | +| 20191219 | | | | ++----------+------+----------------------+-------------+ +| 3.11.0 | 2.7M | 2019-12-20T00:41:21Z | NULL | ++----------+------+----------------------+-------------+ +| 20191114 | 2.7M | 2019-11-14T22:41:11Z | NULL | ++----------+------+----------------------+-------------+ +| 3.10 | 2.7M | 2019-10-21T18:41:18Z | NULL | +| 3.10.3 | | | | ++----------+------+----------------------+-------------+ +| 20190925 | 2.7M | 2019-09-25T22:40:50Z | NULL | ++----------+------+----------------------+-------------+ +| 3.10.2 | 2.7M | 2019-08-20T21:40:57Z | NULL | ++----------+------+----------------------+-------------+ +| 3.8 | 2.1M | 2019-08-20T06:41:01Z | NULL | +| 3.8.4 | | | | ++----------+------+----------------------+-------------+ +| 20190809 | 2.7M | 2019-08-09T21:41:13Z | NULL | ++----------+------+----------------------+-------------+ +| 3.10.1 | 2.7M | 2019-07-11T22:41:17Z | NULL | ++----------+------+----------------------+-------------+ + + # You can set limit, filter and format -$ dockertags -limit 2 -contain v -contain 2 -format json goodwithtech/dockle +$ dockertags -limit 1 -contain latest -format json alpine [ { "tags": [ - "v0.2.4" - ], - "byte": 22154435, - "created_at": "2019-12-05T05:18:04.174078Z", - "uploaded_at": null - }, - { - "tags": [ - "v0.2.3" + "latest", + "3.11.2", + "3.11", + "3" ], - "byte": 22154435, - "created_at": "2019-11-17T15:03:10.914092Z", - "uploaded_at": null + "byte": 2801778, + "created_at": "2019-12-24T20:40:57.918177Z", + "uploaded_at": "0001-01-01T00:00:00Z" } ] ``` diff --git a/go.mod b/go.mod index 900d3dd..e5b678e 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect + github.com/google/go-cmp v0.3.1 github.com/moul/http2curl v1.0.0 // indirect github.com/olekukonko/tablewriter v0.0.2-0.20190618033246-cc27d85e17ce github.com/opencontainers/go-digest v1.0.0-rc1 diff --git a/go.sum b/go.sum index bc6350a..4bcefcd 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,8 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= diff --git a/internal/report/table.go b/internal/report/table.go index c56dfbe..9042f08 100644 --- a/internal/report/table.go +++ b/internal/report/table.go @@ -3,6 +3,7 @@ package report import ( "io" "os" + "sort" "strings" "time" @@ -21,14 +22,18 @@ func (w TableWriter) Write(tags types.ImageTags) (err error) { table.SetHeader([]string{"Tag", "Size", "Created At", "Uploaded At"}) for _, tag := range tags { + targets := utils.StrByLen(tag.Tags) + sort.Sort(targets) + table.Append([]string{ - strings.Join(tag.Tags, ","), + strings.Join(targets, tablewriter.NEWLINE), getBytesize(tag.Byte), ttos(tag.CreatedAt), ttos(tag.UploadedAt), }) } table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetRowLine(true) table.Render() return nil diff --git a/internal/utils/strlen.go b/internal/utils/strlen.go new file mode 100644 index 0000000..6050447 --- /dev/null +++ b/internal/utils/strlen.go @@ -0,0 +1,15 @@ +package utils + +type StrByLen []string + +func (a StrByLen) Len() int { + return len(a) +} + +func (a StrByLen) Less(i, j int) bool { + return len(a[i]) < len(a[j]) +} + +func (a StrByLen) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} diff --git a/pkg/provider/dockerhub/dockerhub.go b/pkg/provider/dockerhub/dockerhub.go index b6837e8..7e0bd7e 100644 --- a/pkg/provider/dockerhub/dockerhub.go +++ b/pkg/provider/dockerhub/dockerhub.go @@ -3,6 +3,7 @@ package dockerhub import ( "context" "fmt" + "sort" "golang.org/x/sync/errgroup" @@ -32,6 +33,19 @@ type ImageSummary struct { Name string `json:"name"` FullSize int `json:"full_size"` LastUpdated string `json:"last_updated"` + Images Images `json:"images"` +} + +type Images []Image +type Image struct { + Digest string `json:"digest"` + Architecture string `json:"architecture"` +} + +func (t Images) Len() int { return len(t) } +func (t Images) Swap(i, j int) { t[i], t[j] = t[j], t[i] } +func (t Images) Less(i, j int) bool { + return (t[i].Digest) > (t[j].Digest) } func (p *DockerHub) Run(ctx context.Context, domain, repository string, reqOpt *types.RequestOption, filterOpt *types.FilterOption) (types.ImageTags, error) { @@ -46,14 +60,18 @@ func (p *DockerHub) Run(ctx context.Context, domain, repository string, reqOpt * if err != nil { return nil, err } - imageTags := p.convertResultToTag(tagResp.Results) - if reqOpt.MaxCount > 0 && len(imageTags) > reqOpt.MaxCount { - return imageTags, nil - } + // imageTags := p.convertResultToTag(tagResp.Results) + // if reqOpt.MaxCount > 0 && len(imageTags) > reqOpt.MaxCount { + // return imageTags, nil + // } + + // create all in one []ImageSummary + totalTagSummary := tagResp.Results lastPage := calcMaxRequestPage(tagResp.Count, reqOpt.MaxCount, filterOpt) // create ch (page - 1), already fetched first page, - tagsPerPage := make(chan types.ImageTags, lastPage-1) + //tagsPerPage := make(chan types.ImageTags, lastPage-1) + tagsPerPage := make(chan []ImageSummary, lastPage-1) eg := errgroup.Group{} for page := 2; page <= lastPage; page++ { page := page @@ -62,7 +80,7 @@ func (p *DockerHub) Run(ctx context.Context, domain, repository string, reqOpt * if err != nil { return err } - tagsPerPage <- p.convertResultToTag(tagResp.Results) + tagsPerPage <- tagResp.Results return nil }) } @@ -73,32 +91,59 @@ func (p *DockerHub) Run(ctx context.Context, domain, repository string, reqOpt * for page := 2; page <= lastPage; page++ { select { case tags := <-tagsPerPage: - imageTags = append(imageTags, tags...) + totalTagSummary = append(totalTagSummary, tags...) } } - return imageTags, nil + return p.convertResultToTag(totalTagSummary), nil } func (p *DockerHub) convertResultToTag(summaries []ImageSummary) types.ImageTags { - tags := []types.ImageTag{} - for _, detail := range summaries { - if detail.Name == "" { + // TODO : refactor it + + // create map : key is image hash + pools := map[string]types.ImageTag{} + for _, imageSummary := range summaries { + if imageSummary.Name == "" { + log.Logger.Debugf("no tag data :%v", imageSummary) continue } - createdAt, _ := time.Parse(time.RFC3339Nano, detail.LastUpdated) - tagNames := []string{detail.Name} - if !utils.MatchConditionTags(p.filterOpt, tagNames) { + if len(imageSummary.Images) == 0 { + log.Logger.Debugf("no image layer data :%v", imageSummary) continue } - tags = append(tags, types.ImageTag{ - Tags: tagNames, - Byte: detail.FullSize, - CreatedAt: createdAt, - }) + sort.Sort(imageSummary.Images) + firstHash := imageSummary.Images[0].Digest + target, ok := pools[firstHash] + // create first one if not exist + if !ok { + pools[firstHash] = createImageTag(imageSummary) + continue + } + // update exist ImageTag + target.Tags = append(target.Tags, imageSummary.Name) + pools[firstHash] = target + } + + tags := []types.ImageTag{} + for _, imageTag := range pools { + if !utils.MatchConditionTags(p.filterOpt, imageTag.Tags) { + continue + } + tags = append(tags, imageTag) } return tags } +func createImageTag(is ImageSummary) types.ImageTag { + createdAt, _ := time.Parse(time.RFC3339Nano, is.LastUpdated) + tagNames := []string{is.Name} + return types.ImageTag{ + Tags: tagNames, + Byte: is.FullSize, + CreatedAt: createdAt, + } +} + // getTagResponse returns the tags for a specific repository. // curl 'https://registry.hub.docker.com/v2/repositories/library/debian/tags/' func getTagResponse(ctx context.Context, auth dockertypes.AuthConfig, timeout time.Duration, repository string, page int) (tagsResponse, error) { @@ -113,13 +158,15 @@ func getTagResponse(ctx context.Context, auth dockertypes.AuthConfig, timeout ti } func calcMaxRequestPage(totalCnt, needCnt int, option *types.FilterOption) int { - maxPage := totalCnt/types.ImagePerPage + 1 - if needCnt == 0 || len(option.Contain) != 0 { - return maxPage - } - needPage := needCnt/types.ImagePerPage + 1 - if needPage >= maxPage { - return maxPage - } - return needPage + // TODO : currently always fetch all pages for show alias + return totalCnt/types.ImagePerPage + 1 + // maxPage := totalCnt/types.ImagePerPage + 1 + // if needCnt == 0 || len(option.Contain) != 0 { + // return maxPage + // } + // needPage := needCnt/types.ImagePerPage + 1 + // if needPage >= maxPage { + // return maxPage + // } + // return needPage } diff --git a/pkg/provider/dockerhub/dockerhub_test.go b/pkg/provider/dockerhub/dockerhub_test.go new file mode 100644 index 0000000..0a5a061 --- /dev/null +++ b/pkg/provider/dockerhub/dockerhub_test.go @@ -0,0 +1,85 @@ +package dockerhub + +import ( + "encoding/json" + "io/ioutil" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/goodwithtech/dockertags/internal/types" +) + +func TestScanImage(t *testing.T) { + testcases := map[string]struct { + filePath string + filterOpt types.FilterOption + expected types.ImageTags + }{ + "debian page1": { + filePath: "./testdata/page1.json", + filterOpt: types.FilterOption{}, + expected: types.ImageTags{ + types.ImageTag{ + Tags: []string{"unstable-slim", "unstable-20191118-slim"}, + }, + types.ImageTag{ + Tags: []string{"unstable", "unstable-20191118"}, + }, + types.ImageTag{ + Tags: []string{"testing-slim", "testing-20191118-slim"}, + }, + types.ImageTag{ + Tags: []string{"testing-backports"}, + }, + types.ImageTag{ + Tags: []string{"testing", "testing-20191118"}, + }, + types.ImageTag{ + Tags: []string{"stretch-slim"}, + }, + }, + }, + "debian filter slim": { + filePath: "./testdata/page1.json", + filterOpt: types.FilterOption{Contain: []string{"slim"}}, + expected: types.ImageTags{ + types.ImageTag{ + Tags: []string{"unstable-slim", "unstable-20191118-slim"}, + }, + types.ImageTag{ + Tags: []string{"testing-slim", "testing-20191118-slim"}, + }, + types.ImageTag{ + Tags: []string{"stretch-slim"}, + }, + }, + }, + } + + for tc, v := range testcases { + dockerHub := DockerHub{filterOpt: &v.filterOpt} + var data tagsResponse + file, err := ioutil.ReadFile(v.filePath) + if err != nil { + t.Errorf("readfile error: %w", err) + continue + } + json.Unmarshal(file, &data) + actual := dockerHub.convertResultToTag(data.Results) + opts := []cmp.Option{ + cmp.Transformer("Sort", func(in []string) []string { + out := append([]string{}, in...) // Copy input to avoid mutating it + sort.Strings(out) + return out + }), + cmpopts.IgnoreFields(types.ImageTag{}, "Byte", "CreatedAt"), + } + sort.Sort(actual) + if diff := cmp.Diff(v.expected, actual, opts...); diff != "" { + t.Errorf("%s: diff %v", tc, diff) + } + } +} diff --git a/pkg/provider/dockerhub/testdata/page1.json b/pkg/provider/dockerhub/testdata/page1.json new file mode 100644 index 0000000..bfbba7c --- /dev/null +++ b/pkg/provider/dockerhub/testdata/page1.json @@ -0,0 +1,847 @@ +{ + "count": 535, + "next": "https://registry.hub.docker.com/v2/repositories/library/debian/tags/?page=2", + "previous": null, + "results": [ + { + "name": "unstable-slim", + "full_size": 27770486, + "images": [ + { + "size": 28737447, + "digest": "sha256:0a69ee0bb8d4bb1fab07d985813fce2f55c9c3911d30b85b1da8c556851ba98f", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 26670752, + "digest": "sha256:7c1890a916f5a4572c5941eed2952fe518f0f962ee8b51883a80fa99c3521074", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 31299332, + "digest": "sha256:af6a41a4147be448b99befff0216877a5f982caa7b94b17689ddf783dfdb13fb", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 26457563, + "digest": "sha256:4398583092b4bff413cb67471d2703eaead5f9e2405dbf80c4f515d7a1eb24fb", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 25710653, + "digest": "sha256:8c279d26352cbb75f7afdfdbbd8fa1503d70c21e29ed697a315f28024cea60e7", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 23567986, + "digest": "sha256:b5c3b48ea6ae08ffbabac80e1ee017d551368e9ae028493257d37984b9131f4b", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 27770486, + "digest": "sha256:06dcd5bd1693f192a0a0b5d8451c43e8b294931fbdd4077f258ff900dd788a93", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 5956353, + "repository": 6315, + "creator": 621950, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:01:09.74331Z" + }, + { + "name": "unstable-20191118-slim", + "full_size": 27770486, + "images": [ + { + "size": 26670752, + "digest": "sha256:7c1890a916f5a4572c5941eed2952fe518f0f962ee8b51883a80fa99c3521074", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 26457563, + "digest": "sha256:4398583092b4bff413cb67471d2703eaead5f9e2405dbf80c4f515d7a1eb24fb", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 25710653, + "digest": "sha256:8c279d26352cbb75f7afdfdbbd8fa1503d70c21e29ed697a315f28024cea60e7", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 27770486, + "digest": "sha256:06dcd5bd1693f192a0a0b5d8451c43e8b294931fbdd4077f258ff900dd788a93", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 31299332, + "digest": "sha256:af6a41a4147be448b99befff0216877a5f982caa7b94b17689ddf783dfdb13fb", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 28737447, + "digest": "sha256:0a69ee0bb8d4bb1fab07d985813fce2f55c9c3911d30b85b1da8c556851ba98f", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 23567986, + "digest": "sha256:b5c3b48ea6ae08ffbabac80e1ee017d551368e9ae028493257d37984b9131f4b", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + } + ], + "id": 77266861, + "repository": 6315, + "creator": 1156886, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:01:03.551394Z" + }, + { + "name": "unstable-20191118", + "full_size": 51302998, + "images": [ + { + "size": 50259191, + "digest": "sha256:cf0a802cc88f3596bc16ee702d9a790683e50d71e36c7391e35af637660a28e5", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 47015922, + "digest": "sha256:6d73fad4b05882d055376f98e5453b599ec6a20de1d1ac938420802ada9d8130", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 51302998, + "digest": "sha256:73df2931068bb33252c3c86594da818e23ca298cc580b8b378022f9fa74f74b8", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 55128391, + "digest": "sha256:8347e968bbd6d84238eeb3badd44c565009294556fdf615b9e37c0193e7bf7b7", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 49263130, + "digest": "sha256:74606051b9104909274b3aa01714484c8b149d8e77a4abde33e6db34d19761ac", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 49968440, + "digest": "sha256:931fbc8e7e61eab27b6907d28872fb837c4cc2d105ea69d8e40790b6c04557a1", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 52411371, + "digest": "sha256:11e3b8f71aecd9267c1e844278afa01c2fd6aad24d7a09327ad7ff08c8451e4a", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 77266858, + "repository": 6315, + "creator": 1156886, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:54.3179Z" + }, + { + "name": "unstable", + "full_size": 51302998, + "images": [ + { + "size": 49968440, + "digest": "sha256:931fbc8e7e61eab27b6907d28872fb837c4cc2d105ea69d8e40790b6c04557a1", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 55128391, + "digest": "sha256:8347e968bbd6d84238eeb3badd44c565009294556fdf615b9e37c0193e7bf7b7", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 47015922, + "digest": "sha256:6d73fad4b05882d055376f98e5453b599ec6a20de1d1ac938420802ada9d8130", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 52411371, + "digest": "sha256:11e3b8f71aecd9267c1e844278afa01c2fd6aad24d7a09327ad7ff08c8451e4a", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 50259191, + "digest": "sha256:cf0a802cc88f3596bc16ee702d9a790683e50d71e36c7391e35af637660a28e5", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 49263130, + "digest": "sha256:74606051b9104909274b3aa01714484c8b149d8e77a4abde33e6db34d19761ac", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 51302998, + "digest": "sha256:73df2931068bb33252c3c86594da818e23ca298cc580b8b378022f9fa74f74b8", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 11123, + "repository": 6315, + "creator": 7, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:48.18706Z" + }, + { + "name": "testing-slim", + "full_size": 27764368, + "images": [ + { + "size": 28735414, + "digest": "sha256:a3ed7808f955565ce8306d12457b55142d00082322f200929106df0a47be6a24", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 27764368, + "digest": "sha256:9493179cd3d54f466c8819a2a1bfec74f02571d4466c53b0f3a7d0d8f93c8193", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 23562693, + "digest": "sha256:e8f91639723e09cd06b67d7350941d9259bba797c8e6369391baa72140626b01", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 26444802, + "digest": "sha256:6d10a72d2f03d7e487e18ea2e50da0a3be26674d863731a4a8a49e1ed0e476bd", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 31287771, + "digest": "sha256:dfebd9f2b9962f1daf3be5c81a2f941a203c30121ab9c4dd10b925a456024396", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 26662818, + "digest": "sha256:50fdf0820936f2da4f3810565dc4c202e39190ea4aed93c015e9f5515d3bcf18", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 25708037, + "digest": "sha256:883f0ddfeb966293073cc11214ceb3e50d6468f4c4fcaae1bdd00c1fca1907b5", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + } + ], + "id": 5956327, + "repository": 6315, + "creator": 621950, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:38.948314Z" + }, + { + "name": "testing-backports", + "full_size": 51291130, + "images": [ + { + "size": 52411733, + "digest": "sha256:41230cb5ffe4898b071871373d109ceee3983506a4d67ce76c8f539b09728ac4", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 50254334, + "digest": "sha256:2402d0314bf3027a1d1e6a8c94778224c463f5b5f6f24ba2ceb271ae6bee87a1", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 49268237, + "digest": "sha256:7c44df1bb8db120b456e8057ba0041dbdf0b78f8c2e66fb486721ddee789561c", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 51291130, + "digest": "sha256:408a340acf4b20ecd24bea9e0347068f3808fef0c2ad64aebd5918b5ca29a49f", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 47005040, + "digest": "sha256:e7808905c7c28f8855f71ecb8851e08d74755df26d62454642e2a404a3d4b9e5", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 55119411, + "digest": "sha256:a33cb9703eb32ae90f6229d7102033789a2cc971601a03a5b8661e695b1088fd", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 49940365, + "digest": "sha256:a7e7321022ac83f1bbebf598cb3c67cdf293b7c4c374231046e9157986efa120", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 11654455, + "repository": 6315, + "creator": 2215, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:34.251225Z" + }, + { + "name": "testing-20191118-slim", + "full_size": 27764368, + "images": [ + { + "size": 28735414, + "digest": "sha256:a3ed7808f955565ce8306d12457b55142d00082322f200929106df0a47be6a24", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 31287771, + "digest": "sha256:dfebd9f2b9962f1daf3be5c81a2f941a203c30121ab9c4dd10b925a456024396", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 26662818, + "digest": "sha256:50fdf0820936f2da4f3810565dc4c202e39190ea4aed93c015e9f5515d3bcf18", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 23562693, + "digest": "sha256:e8f91639723e09cd06b67d7350941d9259bba797c8e6369391baa72140626b01", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 25708037, + "digest": "sha256:883f0ddfeb966293073cc11214ceb3e50d6468f4c4fcaae1bdd00c1fca1907b5", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 27764368, + "digest": "sha256:9493179cd3d54f466c8819a2a1bfec74f02571d4466c53b0f3a7d0d8f93c8193", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 26444802, + "digest": "sha256:6d10a72d2f03d7e487e18ea2e50da0a3be26674d863731a4a8a49e1ed0e476bd", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 77266842, + "repository": 6315, + "creator": 1156886, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:26.606082Z" + }, + { + "name": "testing-20191118", + "full_size": 51290905, + "images": [ + { + "size": 51290905, + "digest": "sha256:1e9ae682e239cb6b539c4d2e3945c8f6d62c87e55df24be2d6b049e0cd6efdfd", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 55119187, + "digest": "sha256:3d07e60ad8916daca6016eb44e693b3c65b018ec2ba2ed4088892c549a561243", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 49940141, + "digest": "sha256:868be1721e5c655b557ab4c391b3758158ee0a6794f8eec7f4304cdec2142733", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 49268013, + "digest": "sha256:5e994cd9d64ce17b03708525141f7911dc2b05646247009c652b6800a71da7ad", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 52411510, + "digest": "sha256:e9137b989e1d00a08cd3a30f1668cf1cb01a4d99f47cce3b02443d8dc3e71e16", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 50254108, + "digest": "sha256:dc428d7fb5c8a96b65ab272b0a7e5f1c47aa89cc3f1ba55a7513be8b62498259", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 47004816, + "digest": "sha256:49621f8580714781d422f6d3dbb1bcbdf1e8be44ad7cfef76ad5f8327ed8e332", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + } + ], + "id": 77266835, + "repository": 6315, + "creator": 1156886, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:18.892041Z" + }, + { + "name": "testing", + "full_size": 51290905, + "images": [ + { + "size": 51290905, + "digest": "sha256:1e9ae682e239cb6b539c4d2e3945c8f6d62c87e55df24be2d6b049e0cd6efdfd", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 49940141, + "digest": "sha256:868be1721e5c655b557ab4c391b3758158ee0a6794f8eec7f4304cdec2142733", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 50254108, + "digest": "sha256:dc428d7fb5c8a96b65ab272b0a7e5f1c47aa89cc3f1ba55a7513be8b62498259", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 47004816, + "digest": "sha256:49621f8580714781d422f6d3dbb1bcbdf1e8be44ad7cfef76ad5f8327ed8e332", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 49268013, + "digest": "sha256:5e994cd9d64ce17b03708525141f7911dc2b05646247009c652b6800a71da7ad", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 55119187, + "digest": "sha256:3d07e60ad8916daca6016eb44e693b3c65b018ec2ba2ed4088892c549a561243", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 52411510, + "digest": "sha256:e9137b989e1d00a08cd3a30f1668cf1cb01a4d99f47cce3b02443d8dc3e71e16", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 11113, + "repository": 6315, + "creator": 7, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:13.762866Z" + }, + { + "name": "stretch-slim", + "full_size": 22524572, + "images": [ + { + "size": 20385759, + "digest": "sha256:2e24d88457527d82b1cab0a9327eab35de3e1902691a346bc474af36462df043", + "architecture": "arm64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v8", + "features": "" + }, + { + "size": 22380089, + "digest": "sha256:bef911ad7b423dcaa98615040e63ee750f8b78d97587e60b48d4a22b12e06f08", + "architecture": "s390x", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 23152070, + "digest": "sha256:e5763d6be644027e414d06907e214ed5aa3899ab7ac1a9d10427347eab587353", + "architecture": "386", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 22524572, + "digest": "sha256:df522a8ee081e81521d499440e2620dd39ee75434940e4890b0424d83f6332c2", + "architecture": "amd64", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + }, + { + "size": 21202864, + "digest": "sha256:14bfaabb2f662fd51c251b6d27671ee78f022b9f5c0cf9bb63a0b849cd5faea8", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v5", + "features": "" + }, + { + "size": 19311578, + "digest": "sha256:37a60ef0aad40394224a94aaec8ea65edceb52cb8bc44c069cf931b656d94a66", + "architecture": "arm", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": "v7", + "features": "" + }, + { + "size": 22800737, + "digest": "sha256:91ba1e73b204bf281e2cd13a4ff673ce763303b5b1106824b3fd4becc357667f", + "architecture": "ppc64le", + "os": "linux", + "os_version": null, + "os_features": "", + "variant": null, + "features": "" + } + ], + "id": 5956306, + "repository": 6315, + "creator": 621950, + "last_updater": 1156886, + "last_updater_username": "doijanky", + "image_id": null, + "v2": true, + "last_updated": "2019-11-22T19:00:04.650932Z" + } + ] +} \ No newline at end of file