From 3ac51325cebbf40dc1cc45c8d4245b45051d68ed Mon Sep 17 00:00:00 2001 From: Aaron Schlesinger Date: Wed, 8 Jun 2016 17:04:37 -0700 Subject: [PATCH] fix(actions,docker): make the retag operation concurrent --- actions/docker/retag.go | 27 +++++++++++++++--- docker/retag.go | 61 +++++++++++++++++++++++++++++------------ 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/actions/docker/retag.go b/actions/docker/retag.go index 3b11cd3..5439889 100644 --- a/actions/docker/retag.go +++ b/actions/docker/retag.go @@ -62,12 +62,31 @@ func retag(ghClient *github.Client, dockerCl *dlib.Client) func(c *cli.Context) } } fmt.Println("Re-tagging images...") - newTags, err := docker.Retag(dockerCl, allReposAndShas, newTag) - if err != nil { - log.Fatalf("Error re-tagging images (%s)", err) + taggedCh, errCh, doneCh := docker.Retag(dockerCl, allReposAndShas, newTag) + errFound := false + newImages := []*docker.Image{} + for { + shouldBreak := false + select { + case img := <-taggedCh: + fmt.Printf("Tagged image %s\n", *img) + newImages = append(newImages, img) + case retErr := <-errCh: + fmt.Printf("Error tagging image: %s", retErr) + errFound = true + case <-doneCh: + shouldBreak = true + } + if shouldBreak { + break + } } + if errFound { + log.Fatalf("Error re-tagging images") + } + if push { - if err := docker.Push(dockerCl, newTags); err != nil { + if err := docker.Push(dockerCl, newImages); err != nil { log.Fatalf("Error pushing new images (%s)", err) } } diff --git a/docker/retag.go b/docker/retag.go index b2f435b..6e19da4 100644 --- a/docker/retag.go +++ b/docker/retag.go @@ -3,6 +3,7 @@ package docker import ( "fmt" "io/ioutil" + "sync" "github.com/deis/deisrel/git" dlib "github.com/fsouza/go-dockerclient" @@ -44,24 +45,50 @@ func fullImageName(ras git.RepoAndSha) string { return fmt.Sprintf("quay.io/deisci/%s:git-%s", ras.Name, ras.SHA) } -// Retag re-tags images from tags based on items in rasl to target -func Retag(dockerCl *dlib.Client, rasl []git.RepoAndSha, target string) ([]*Image, error) { - ret := make([]*Image, len(rasl)) +// Retag re-tags images from tags based on items in rasl, pulling existing images if necessary, to target. It returns a channel indicating the progress of the tagging (which may be out of order) and a channel of errors which indicates any errors encountered during the entire process. Some images may be successfully tagged and some may not, but at the end of execution, the final channel (<-chan struct{}) will be closed +func Retag(dockerCl *dlib.Client, rasl []git.RepoAndSha, target string) (<-chan *Image, <-chan error, <-chan struct{}) { + imagesCh := make(chan *Image) + errCh := make(chan error) + doneCh := make(chan struct{}) + + var wg sync.WaitGroup for i, ras := range rasl { - fullName := fullImageName(ras) - img, err := ParseImageFromName(fullName, true) - if err != nil { - return nil, err - } - if err := pullIfMissing(dockerCl, img); err != nil { - return nil, err - } - newImg, err := retagOne(dockerCl, img, target) - if err != nil { - return nil, err - } - ret[i] = newImg + wg.Add(1) + iCh := make(chan *Image) + eCh := make(chan error) + go func(i int, ras git.RepoAndSha) { + fullName := fullImageName(ras) + img, err := ParseImageFromName(fullName, true) + if err != nil { + eCh <- err + return + } + if pullErr := pullIfMissing(dockerCl, img); pullErr != nil { + eCh <- pullErr + return + } + newImg, err := retagOne(dockerCl, img, target) + if err != nil { + eCh <- err + return + } + iCh <- newImg + }(i, ras) + go func() { + defer wg.Done() + select { + case img := <-iCh: + imagesCh <- img + case err := <-eCh: + errCh <- err + } + }() } - return ret, nil + go func() { + wg.Wait() + close(doneCh) + }() + + return imagesCh, errCh, doneCh }