github.com/Cloud-Foundations/Dominator@v0.3.4/imagebuilder/rpcd/buildImage.go (about) 1 package rpcd 2 3 import ( 4 "bytes" 5 "io" 6 "sync" 7 "time" 8 9 "github.com/Cloud-Foundations/Dominator/lib/errors" 10 "github.com/Cloud-Foundations/Dominator/lib/srpc" 11 proto "github.com/Cloud-Foundations/Dominator/proto/imaginator" 12 ) 13 14 type flusher interface { 15 flush() error 16 } 17 18 type logWriterType struct { 19 conn *srpc.Conn 20 mutex sync.Mutex // Protect everything below. 21 err error 22 flushPending bool 23 } 24 25 func (t *srpcType) BuildImage(conn *srpc.Conn) error { 26 var request proto.BuildImageRequest 27 if err := conn.Decode(&request); err != nil { 28 _, err = conn.WriteString(err.Error() + "\n") 29 return err 30 } 31 if _, err := conn.WriteString("\n"); err != nil { 32 return err 33 } 34 buildLogBuffer := &bytes.Buffer{} 35 var logWriter io.Writer 36 if request.StreamBuildLog { 37 logWriter = &logWriterType{conn: conn} 38 } else { 39 logWriter = buildLogBuffer 40 } 41 image, name, err := t.builder.BuildImage(request, conn.GetAuthInformation(), 42 logWriter) 43 if f, ok := logWriter.(flusher); ok { 44 // Ensure all data are flushed and no background flush will happen. 45 if err := f.flush(); err != nil { 46 return err 47 } 48 } 49 reply := proto.BuildImageResponse{ 50 Image: image, 51 ImageName: name, 52 BuildLog: buildLogBuffer.Bytes(), 53 ErrorString: errors.ErrorToString(err), 54 } 55 return conn.Encode(reply) 56 } 57 58 func (w *logWriterType) delayedFlush() { 59 time.Sleep(time.Millisecond * 100) 60 w.flush() 61 } 62 63 func (w *logWriterType) flush() error { 64 w.mutex.Lock() 65 defer w.mutex.Unlock() 66 flushPending := w.flushPending 67 w.flushPending = false 68 if w.err != nil { 69 return w.err 70 } 71 if flushPending { 72 w.err = w.conn.Flush() 73 return w.err 74 } 75 return nil 76 } 77 78 func (w *logWriterType) lockAndScheduleFlush() { 79 w.mutex.Lock() 80 if w.flushPending { 81 return 82 } 83 w.flushPending = true 84 go w.delayedFlush() 85 } 86 87 func (w *logWriterType) Write(p []byte) (int, error) { 88 w.lockAndScheduleFlush() 89 defer w.mutex.Unlock() 90 if w.err != nil { 91 return 0, w.err 92 } 93 reply := proto.BuildImageResponse{BuildLog: p} 94 if err := w.conn.Encode(reply); err != nil { 95 return 0, err 96 } 97 return len(p), nil 98 }