@@ -3,6 +3,7 @@ package dockerhub
33import (
44 "context"
55 "fmt"
6+ "sort"
67
78 "golang.org/x/sync/errgroup"
89
@@ -32,6 +33,19 @@ type ImageSummary struct {
3233 Name string `json:"name"`
3334 FullSize int `json:"full_size"`
3435 LastUpdated string `json:"last_updated"`
36+ Images Images `json:"images"`
37+ }
38+
39+ type Images []Image
40+ type Image struct {
41+ Digest string `json:"digest"`
42+ Architecture string `json:"architecture"`
43+ }
44+
45+ func (t Images ) Len () int { return len (t ) }
46+ func (t Images ) Swap (i , j int ) { t [i ], t [j ] = t [j ], t [i ] }
47+ func (t Images ) Less (i , j int ) bool {
48+ return (t [i ].Digest ) > (t [j ].Digest )
3549}
3650
3751func (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 *
4660 if err != nil {
4761 return nil , err
4862 }
49- imageTags := p .convertResultToTag (tagResp .Results )
50- if reqOpt .MaxCount > 0 && len (imageTags ) > reqOpt .MaxCount {
51- return imageTags , nil
52- }
63+ // imageTags := p.convertResultToTag(tagResp.Results)
64+ // if reqOpt.MaxCount > 0 && len(imageTags) > reqOpt.MaxCount {
65+ // return imageTags, nil
66+ // }
67+
68+ // create all in one []ImageSummary
69+ totalTagSummary := tagResp .Results
5370
5471 lastPage := calcMaxRequestPage (tagResp .Count , reqOpt .MaxCount , filterOpt )
5572 // create ch (page - 1), already fetched first page,
56- tagsPerPage := make (chan types.ImageTags , lastPage - 1 )
73+ //tagsPerPage := make(chan types.ImageTags, lastPage-1)
74+ tagsPerPage := make (chan []ImageSummary , lastPage - 1 )
5775 eg := errgroup.Group {}
5876 for page := 2 ; page <= lastPage ; page ++ {
5977 page := page
@@ -62,7 +80,7 @@ func (p *DockerHub) Run(ctx context.Context, domain, repository string, reqOpt *
6280 if err != nil {
6381 return err
6482 }
65- tagsPerPage <- p . convertResultToTag ( tagResp .Results )
83+ tagsPerPage <- tagResp .Results
6684 return nil
6785 })
6886 }
@@ -73,32 +91,59 @@ func (p *DockerHub) Run(ctx context.Context, domain, repository string, reqOpt *
7391 for page := 2 ; page <= lastPage ; page ++ {
7492 select {
7593 case tags := <- tagsPerPage :
76- imageTags = append (imageTags , tags ... )
94+ totalTagSummary = append (totalTagSummary , tags ... )
7795 }
7896 }
79- return imageTags , nil
97+ return p . convertResultToTag ( totalTagSummary ) , nil
8098}
8199
82100func (p * DockerHub ) convertResultToTag (summaries []ImageSummary ) types.ImageTags {
83- tags := []types.ImageTag {}
84- for _ , detail := range summaries {
85- if detail .Name == "" {
101+ // TODO : refactor it
102+
103+ // create map : key is image hash
104+ pools := map [string ]types.ImageTag {}
105+ for _ , imageSummary := range summaries {
106+ if imageSummary .Name == "" {
107+ log .Logger .Debugf ("no tag data :%v" , imageSummary )
86108 continue
87109 }
88- createdAt , _ := time .Parse (time .RFC3339Nano , detail .LastUpdated )
89- tagNames := []string {detail .Name }
90- if ! utils .MatchConditionTags (p .filterOpt , tagNames ) {
110+ if len (imageSummary .Images ) == 0 {
111+ log .Logger .Debugf ("no image layer data :%v" , imageSummary )
91112 continue
92113 }
93- tags = append (tags , types.ImageTag {
94- Tags : tagNames ,
95- Byte : detail .FullSize ,
96- CreatedAt : createdAt ,
97- })
114+ sort .Sort (imageSummary .Images )
115+ firstHash := imageSummary .Images [0 ].Digest
116+ target , ok := pools [firstHash ]
117+ // create first one if not exist
118+ if ! ok {
119+ pools [firstHash ] = createImageTag (imageSummary )
120+ continue
121+ }
122+ // update exist ImageTag
123+ target .Tags = append (target .Tags , imageSummary .Name )
124+ pools [firstHash ] = target
125+ }
126+
127+ tags := []types.ImageTag {}
128+ for _ , imageTag := range pools {
129+ if ! utils .MatchConditionTags (p .filterOpt , imageTag .Tags ) {
130+ continue
131+ }
132+ tags = append (tags , imageTag )
98133 }
99134 return tags
100135}
101136
137+ func createImageTag (is ImageSummary ) types.ImageTag {
138+ createdAt , _ := time .Parse (time .RFC3339Nano , is .LastUpdated )
139+ tagNames := []string {is .Name }
140+ return types.ImageTag {
141+ Tags : tagNames ,
142+ Byte : is .FullSize ,
143+ CreatedAt : createdAt ,
144+ }
145+ }
146+
102147// getTagResponse returns the tags for a specific repository.
103148// curl 'https://registry.hub.docker.com/v2/repositories/library/debian/tags/'
104149func 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
113158}
114159
115160func calcMaxRequestPage (totalCnt , needCnt int , option * types.FilterOption ) int {
116- maxPage := totalCnt / types .ImagePerPage + 1
117- if needCnt == 0 || len (option .Contain ) != 0 {
118- return maxPage
119- }
120- needPage := needCnt / types .ImagePerPage + 1
121- if needPage >= maxPage {
122- return maxPage
123- }
124- return needPage
161+ // TODO : currently always fetch all pages for show alias
162+ return totalCnt / types .ImagePerPage + 1
163+ // maxPage := totalCnt/types.ImagePerPage + 1
164+ // if needCnt == 0 || len(option.Contain) != 0 {
165+ // return maxPage
166+ // }
167+ // needPage := needCnt/types.ImagePerPage + 1
168+ // if needPage >= maxPage {
169+ // return maxPage
170+ // }
171+ // return needPage
125172}
0 commit comments