github.com/SupersunnySea/draft@v0.16.0/pkg/builder/docker/builder.go (about) 1 package docker 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/Azure/draft/pkg/builder" 9 "github.com/docker/cli/cli/command" 10 "github.com/docker/docker/api/types" 11 dockerclient "github.com/docker/docker/client" 12 "github.com/docker/docker/pkg/jsonmessage" 13 "github.com/docker/docker/pkg/term" 14 "golang.org/x/net/context" 15 ) 16 17 // Builder contains information about the build environment 18 type Builder struct { 19 DockerClient command.Cli 20 } 21 22 // Build builds the docker image. 23 func (b *Builder) Build(ctx context.Context, app *builder.AppContext, out chan<- *builder.Summary) (err error) { 24 const stageDesc = "Building Docker Image" 25 26 defer builder.Complete(app.ID, stageDesc, out, &err) 27 summary := builder.Summarize(app.ID, stageDesc, out) 28 29 // notify that particular stage has started. 30 summary("started", builder.SummaryStarted) 31 32 msgc := make(chan string) 33 errc := make(chan error) 34 go func() { 35 args := make(map[string]*string) 36 for k := range app.Ctx.Env.ImageBuildArgs { 37 v := app.Ctx.Env.ImageBuildArgs[k] 38 args[k] = &v 39 } 40 41 buildopts := types.ImageBuildOptions{ 42 Tags: app.Images, 43 Dockerfile: app.Ctx.Env.Dockerfile, 44 BuildArgs: args, 45 } 46 47 resp, err := b.DockerClient.Client().ImageBuild(ctx, app.Buf, buildopts) 48 if err != nil { 49 errc <- err 50 return 51 } 52 defer func() { 53 resp.Body.Close() 54 close(msgc) 55 close(errc) 56 }() 57 outFd, isTerm := term.GetFdInfo(app.Buf) 58 if err := jsonmessage.DisplayJSONMessagesStream(resp.Body, app.Log, outFd, isTerm, nil); err != nil { 59 errc <- err 60 return 61 } 62 if _, _, err = b.DockerClient.Client().ImageInspectWithRaw(ctx, app.MainImage); err != nil { 63 if dockerclient.IsErrNotFound(err) { 64 errc <- fmt.Errorf("Could not locate image for %s: %v", app.Ctx.Env.Name, err) 65 return 66 } 67 errc <- fmt.Errorf("ImageInspectWithRaw error: %v", err) 68 return 69 } 70 }() 71 for msgc != nil || errc != nil { 72 select { 73 case msg, ok := <-msgc: 74 if !ok { 75 msgc = nil 76 continue 77 } 78 summary(msg, builder.SummaryLogging) 79 case err, ok := <-errc: 80 if !ok { 81 errc = nil 82 continue 83 } 84 return err 85 default: 86 summary("ongoing", builder.SummaryOngoing) 87 time.Sleep(time.Second) 88 } 89 } 90 return nil 91 } 92 93 // Push pushes the results of Build to the image repository. 94 func (b *Builder) Push(ctx context.Context, app *builder.AppContext, out chan<- *builder.Summary) (err error) { 95 if app.Ctx.Env.Registry == "" { 96 return 97 } 98 99 const stageDesc = "Pushing Docker Image" 100 101 defer builder.Complete(app.ID, stageDesc, out, &err) 102 summary := builder.Summarize(app.ID, stageDesc, out) 103 104 // notify that particular stage has started. 105 summary("started", builder.SummaryStarted) 106 107 msgc := make(chan string, 1) 108 errc := make(chan error, 1) 109 110 var wg sync.WaitGroup 111 wg.Add(len(app.Images)) 112 113 go func() { 114 registryAuth, err := command.RetrieveAuthTokenFromImage(ctx, b.DockerClient, app.MainImage) 115 if err != nil { 116 errc <- err 117 return 118 } 119 120 for _, tag := range app.Images { 121 122 go func(tag string) { 123 defer wg.Done() 124 125 resp, err := b.DockerClient.Client().ImagePush(ctx, tag, types.ImagePushOptions{RegistryAuth: registryAuth}) 126 if err != nil { 127 errc <- err 128 return 129 } 130 131 defer resp.Close() 132 outFd, isTerm := term.GetFdInfo(app.Log) 133 if err := jsonmessage.DisplayJSONMessagesStream(resp, app.Log, outFd, isTerm, nil); err != nil { 134 errc <- err 135 return 136 } 137 }(tag) 138 } 139 140 defer func() { 141 close(errc) 142 close(msgc) 143 }() 144 145 }() 146 for msgc != nil || errc != nil { 147 select { 148 case msg, ok := <-msgc: 149 if !ok { 150 msgc = nil 151 continue 152 } 153 summary(msg, builder.SummaryLogging) 154 case err, ok := <-errc: 155 if !ok { 156 errc = nil 157 continue 158 } 159 return err 160 default: 161 summary("ongoing", builder.SummaryOngoing) 162 time.Sleep(time.Second) 163 } 164 } 165 wg.Wait() 166 return nil 167 } 168 169 // AuthToken retrieves the auth token for the given image. 170 func (b *Builder) AuthToken(ctx context.Context, app *builder.AppContext) (string, error) { 171 return command.RetrieveAuthTokenFromImage(ctx, b.DockerClient, app.MainImage) 172 }