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  }