github.com/grahambrereton-form3/tilt@v0.10.18/internal/synclet/synclet.go (about) 1 package synclet 2 3 import ( 4 "bytes" 5 "context" 6 "log" 7 "strings" 8 9 "github.com/pkg/errors" 10 11 "github.com/windmilleng/tilt/internal/build" 12 "github.com/windmilleng/tilt/internal/container" 13 "github.com/windmilleng/tilt/pkg/logger" 14 15 "github.com/opentracing/opentracing-go" 16 17 "github.com/windmilleng/tilt/internal/docker" 18 "github.com/windmilleng/tilt/pkg/model" 19 ) 20 21 const Port = 23551 22 23 type Synclet struct { 24 dCli docker.Client 25 } 26 27 func NewSynclet(dCli docker.Client) *Synclet { 28 return &Synclet{dCli: dCli} 29 } 30 31 func (s Synclet) writeFiles(ctx context.Context, containerId container.ID, tarArchive []byte) error { 32 span, ctx := opentracing.StartSpanFromContext(ctx, "Synclet-writeFiles") 33 defer span.Finish() 34 35 if tarArchive == nil { 36 return nil 37 } 38 39 return s.dCli.CopyToContainerRoot(ctx, containerId.String(), bytes.NewBuffer(tarArchive)) 40 } 41 42 func (s Synclet) rmFiles(ctx context.Context, containerId container.ID, filesToDelete []string) error { 43 span, ctx := opentracing.StartSpanFromContext(ctx, "Synclet-rmFiles") 44 defer span.Finish() 45 46 if len(filesToDelete) == 0 { 47 return nil 48 } 49 50 cmd := model.Cmd{Argv: append([]string{"rm", "-rf"}, filesToDelete...)} 51 52 out := bytes.NewBuffer(nil) 53 err := s.dCli.ExecInContainer(ctx, containerId, cmd, out) 54 if err != nil { 55 dockerExitErr, ok := err.(docker.ExitError) 56 if ok { 57 return errors.Wrapf(err, "Error deleting files. exit code %d, output '%s'", dockerExitErr.ExitCode, out.String()) 58 } 59 return errors.Wrap(err, "Error deleting files") 60 } 61 return nil 62 } 63 64 func (s Synclet) execCmds(ctx context.Context, containerId container.ID, cmds []model.Cmd) error { 65 span, ctx := opentracing.StartSpanFromContext(ctx, "Synclet-execCommands") 66 defer span.Finish() 67 68 for i, c := range cmds { 69 // TODO: instrument this 70 log.Printf("[CMD %d/%d] %s", i+1, len(cmds), strings.Join(c.Argv, " ")) 71 // TODO(matt) - plumb PipelineState through 72 l := logger.Get(ctx) 73 err := s.dCli.ExecInContainer(ctx, containerId, c, l.Writer(logger.InfoLvl)) 74 if err != nil { 75 return build.WrapContainerExecError(err, containerId, c) 76 } 77 } 78 return nil 79 } 80 81 func (s Synclet) restartContainer(ctx context.Context, containerId container.ID) error { 82 span, ctx := opentracing.StartSpanFromContext(ctx, "Synclet-restartContainer") 83 defer span.Finish() 84 85 return s.dCli.ContainerRestartNoWait(ctx, containerId.String()) 86 } 87 88 func (s Synclet) UpdateContainer( 89 ctx context.Context, 90 containerId container.ID, 91 tarArchive []byte, 92 filesToDelete []string, 93 commands []model.Cmd, 94 hotReload bool) error { 95 96 span, ctx := opentracing.StartSpanFromContext(ctx, "Synclet-UpdateContainer") 97 defer span.Finish() 98 99 err := s.rmFiles(ctx, containerId, filesToDelete) 100 if err != nil { 101 return errors.Wrapf(err, "error removing files while updating container %s", 102 containerId.ShortStr()) 103 } 104 105 err = s.writeFiles(ctx, containerId, tarArchive) 106 if err != nil { 107 return errors.Wrapf(err, "error writing files while updating container %s", 108 containerId.ShortStr()) 109 } 110 111 err = s.execCmds(ctx, containerId, commands) 112 if err != nil { 113 return errors.Wrapf(err, "error exec'ing commands while updating container %s", 114 containerId.ShortStr()) 115 } 116 117 if !hotReload { 118 err = s.restartContainer(ctx, containerId) 119 if err != nil { 120 return errors.Wrapf(err, "error restarting container %s", 121 containerId.ShortStr()) 122 } 123 } 124 125 return nil 126 }