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  }