utils/utils.go
2e69e172
 package utils
 
 import (
 	"bytes"
 	"crypto/sha256"
 	"encoding/hex"
14212930
 	"encoding/json"
2e69e172
 	"errors"
 	"fmt"
 	"index/suffixarray"
 	"io"
 	"io/ioutil"
dede1585
 	"log"
2e69e172
 	"net/http"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
f3bab52d
 	"strconv"
2e69e172
 	"strings"
 	"sync"
 	"time"
 )
 
 // Go is a basic promise implementation: it wraps calls a function in a goroutine,
 // and returns a channel which will later return the function's return value.
 func Go(f func() error) chan error {
 	ch := make(chan error)
 	go func() {
 		ch <- f()
 	}()
 	return ch
 }
 
 // Request a given URL and return an io.Reader
 func Download(url string, stderr io.Writer) (*http.Response, error) {
 	var resp *http.Response
86ada2fa
 	var err error
2e69e172
 	if resp, err = http.Get(url); err != nil {
 		return nil, err
 	}
 	if resp.StatusCode >= 400 {
 		return nil, errors.New("Got HTTP status code >= 400: " + resp.Status)
 	}
 	return resp, nil
 }
 
 // Debug function, if the debug flag is set, then display. Do nothing otherwise
 // If Docker is in damon mode, also send the debug info on the socket
 func Debugf(format string, a ...interface{}) {
 	if os.Getenv("DEBUG") != "" {
 
 		// Retrieve the stack infos
 		_, file, line, ok := runtime.Caller(1)
 		if !ok {
 			file = "<unknown>"
 			line = -1
 		} else {
 			file = file[strings.LastIndex(file, "/")+1:]
 		}
 
 		fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...)
 	}
 }
 
 // Reader with progress bar
 type progressReader struct {
 	reader       io.ReadCloser // Stream to read from
 	output       io.Writer     // Where to send progress bar to
 	readTotal    int           // Expected stream length (bytes)
 	readProgress int           // How much has been read so far (bytes)
 	lastUpdate   int           // How many bytes read at least update
 	template     string        // Template to print. Default "%v/%v (%v)"
84d68007
 	sf           *StreamFormatter
2e69e172
 }
 
 func (r *progressReader) Read(p []byte) (n int, err error) {
 	read, err := io.ReadCloser(r.reader).Read(p)
 	r.readProgress += read
 
 	updateEvery := 4096
 	if r.readTotal > 0 {
 		// Only update progress for every 1% read
 		if increment := int(0.01 * float64(r.readTotal)); increment > updateEvery {
 			updateEvery = increment
 		}
 	}
 	if r.readProgress-r.lastUpdate > updateEvery || err != nil {
 		if r.readTotal > 0 {
76a568fc
 			fmt.Fprintf(r.output, r.template, HumanSize(int64(r.readProgress)), HumanSize(int64(r.readTotal)), fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
2e69e172
 		} else {
3c7bca7a
 			fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a")
2e69e172
 		}
 		r.lastUpdate = r.readProgress
 	}
 	// Send newline when complete
 	if err != nil {
5a36efb6
 		r.output.Write(r.sf.FormatStatus(""))
2e69e172
 	}
 
 	return read, err
 }
 func (r *progressReader) Close() error {
 	return io.ReadCloser(r.reader).Close()
 }
5a36efb6
 func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader {
84d68007
 	tpl := string(template)
5a36efb6
 	if tpl == "" {
cd002a4d
 		tpl = string(sf.FormatProgress("", "%v/%v (%v)"))
2e69e172
 	}
5a36efb6
 	return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf}
2e69e172
 }
 
 // HumanDuration returns a human-readable approximation of a duration
 // (eg. "About a minute", "4 hours ago", etc.)
 func HumanDuration(d time.Duration) string {
 	if seconds := int(d.Seconds()); seconds < 1 {
 		return "Less than a second"
 	} else if seconds < 60 {
 		return fmt.Sprintf("%d seconds", seconds)
 	} else if minutes := int(d.Minutes()); minutes == 1 {
 		return "About a minute"
 	} else if minutes < 60 {
 		return fmt.Sprintf("%d minutes", minutes)
 	} else if hours := int(d.Hours()); hours == 1 {
 		return "About an hour"
 	} else if hours < 48 {
 		return fmt.Sprintf("%d hours", hours)
 	} else if hours < 24*7*2 {
 		return fmt.Sprintf("%d days", hours/24)
 	} else if hours < 24*30*3 {
 		return fmt.Sprintf("%d weeks", hours/24/7)
 	} else if hours < 24*365*2 {
 		return fmt.Sprintf("%d months", hours/24/30)
 	}
 	return fmt.Sprintf("%d years", d.Hours()/24/365)
 }
 
ed56b6a9
 // HumanSize returns a human-readable approximation of a size
b45143da
 // using SI standard (eg. "44kB", "17MB")
6fce89e6
 func HumanSize(size int64) string {
 	i := 0
 	var sizef float64
 	sizef = float64(size)
 	units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
b45143da
 	for sizef >= 1000.0 {
 		sizef = sizef / 1000.0
6fce89e6
 		i++
 	}
b45143da
 	return fmt.Sprintf("%.4g %s", sizef, units[i])
6fce89e6
 }
 
2e69e172
 func Trunc(s string, maxlen int) string {
 	if len(s) <= maxlen {
 		return s
 	}
 	return s[:maxlen]
 }
 
 // Figure out the absolute path of our own binary
 func SelfPath() string {
 	path, err := exec.LookPath(os.Args[0])
 	if err != nil {
 		panic(err)
 	}
 	path, err = filepath.Abs(path)
 	if err != nil {
 		panic(err)
 	}
 	return path
 }
 
e7077320
 type NopWriter struct {
2e69e172
 }
 
e7077320
 func (w *NopWriter) Write(buf []byte) (int, error) {
2e69e172
 	return len(buf), nil
 }
 
 type nopWriteCloser struct {
 	io.Writer
 }
 
 func (w *nopWriteCloser) Close() error { return nil }
 
 func NopWriteCloser(w io.Writer) io.WriteCloser {
 	return &nopWriteCloser{w}
 }
 
 type bufReader struct {
 	buf    *bytes.Buffer
 	reader io.Reader
 	err    error
 	l      sync.Mutex
 	wait   sync.Cond
 }
 
 func NewBufReader(r io.Reader) *bufReader {
 	reader := &bufReader{
 		buf:    &bytes.Buffer{},
 		reader: r,
 	}
 	reader.wait.L = &reader.l
 	go reader.drain()
 	return reader
 }
 
 func (r *bufReader) drain() {
 	buf := make([]byte, 1024)
 	for {
 		n, err := r.reader.Read(buf)
 		r.l.Lock()
 		if err != nil {
 			r.err = err
 		} else {
 			r.buf.Write(buf[0:n])
 		}
 		r.wait.Signal()
 		r.l.Unlock()
 		if err != nil {
 			break
 		}
 	}
 }
 
 func (r *bufReader) Read(p []byte) (n int, err error) {
 	r.l.Lock()
 	defer r.l.Unlock()
 	for {
 		n, err = r.buf.Read(p)
 		if n > 0 {
 			return n, err
 		}
 		if r.err != nil {
 			return 0, r.err
 		}
 		r.wait.Wait()
 	}
 	panic("unreachable")
 }
 
 func (r *bufReader) Close() error {
 	closer, ok := r.reader.(io.ReadCloser)
 	if !ok {
 		return nil
 	}
 	return closer.Close()
 }
 
 type WriteBroadcaster struct {
 	mu      sync.Mutex
 	writers map[io.WriteCloser]struct{}
 }
 
 func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser) {
 	w.mu.Lock()
 	w.writers[writer] = struct{}{}
 	w.mu.Unlock()
 }
 
 // FIXME: Is that function used?
 // FIXME: This relies on the concrete writer type used having equality operator
 func (w *WriteBroadcaster) RemoveWriter(writer io.WriteCloser) {
 	w.mu.Lock()
 	delete(w.writers, writer)
 	w.mu.Unlock()
 }
 
 func (w *WriteBroadcaster) Write(p []byte) (n int, err error) {
 	w.mu.Lock()
 	defer w.mu.Unlock()
 	for writer := range w.writers {
 		if n, err := writer.Write(p); err != nil || n != len(p) {
 			// On error, evict the writer
 			delete(w.writers, writer)
 		}
 	}
 	return len(p), nil
 }
 
 func (w *WriteBroadcaster) CloseWriters() error {
 	w.mu.Lock()
 	defer w.mu.Unlock()
 	for writer := range w.writers {
 		writer.Close()
 	}
 	w.writers = make(map[io.WriteCloser]struct{})
 	return nil
 }
 
 func NewWriteBroadcaster() *WriteBroadcaster {
 	return &WriteBroadcaster{writers: make(map[io.WriteCloser]struct{})}
 }
 
 func GetTotalUsedFds() int {
 	if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
 		Debugf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
 	} else {
 		return len(fds)
 	}
 	return -1
 }
 
 // TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
 // This is used to retrieve image and container IDs by more convenient shorthand prefixes.
 type TruncIndex struct {
 	index *suffixarray.Index
 	ids   map[string]bool
 	bytes []byte
 }
 
 func NewTruncIndex() *TruncIndex {
 	return &TruncIndex{
 		index: suffixarray.New([]byte{' '}),
 		ids:   make(map[string]bool),
 		bytes: []byte{' '},
 	}
 }
 
 func (idx *TruncIndex) Add(id string) error {
 	if strings.Contains(id, " ") {
 		return fmt.Errorf("Illegal character: ' '")
 	}
 	if _, exists := idx.ids[id]; exists {
 		return fmt.Errorf("Id already exists: %s", id)
 	}
 	idx.ids[id] = true
 	idx.bytes = append(idx.bytes, []byte(id+" ")...)
 	idx.index = suffixarray.New(idx.bytes)
 	return nil
 }
 
 func (idx *TruncIndex) Delete(id string) error {
 	if _, exists := idx.ids[id]; !exists {
 		return fmt.Errorf("No such id: %s", id)
 	}
 	before, after, err := idx.lookup(id)
 	if err != nil {
 		return err
 	}
 	delete(idx.ids, id)
 	idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...)
 	idx.index = suffixarray.New(idx.bytes)
 	return nil
 }
 
 func (idx *TruncIndex) lookup(s string) (int, int, error) {
 	offsets := idx.index.Lookup([]byte(" "+s), -1)
 	//log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
 	if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
 		return -1, -1, fmt.Errorf("No such id: %s", s)
 	}
 	offsetBefore := offsets[0] + 1
 	offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ")
 	return offsetBefore, offsetAfter, nil
 }
 
 func (idx *TruncIndex) Get(s string) (string, error) {
 	before, after, err := idx.lookup(s)
 	//log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
 	if err != nil {
 		return "", err
 	}
 	return string(idx.bytes[before:after]), err
 }
 
fd224ee5
 // TruncateID returns a shorthand version of a string identifier for convenience.
2e69e172
 // A collision with other shorthands is very unlikely, but possible.
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
 // will need to use a langer prefix, or the full-length Id.
fd224ee5
 func TruncateID(id string) string {
2e69e172
 	shortLen := 12
 	if len(id) < shortLen {
 		shortLen = len(id)
 	}
 	return id[:shortLen]
 }
 
 // Code c/c from io.Copy() modified to handle escape sequence
 func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) {
 	buf := make([]byte, 32*1024)
 	for {
 		nr, er := src.Read(buf)
 		if nr > 0 {
 			// ---- Docker addition
 			// char 16 is C-p
 			if nr == 1 && buf[0] == 16 {
 				nr, er = src.Read(buf)
 				// char 17 is C-q
 				if nr == 1 && buf[0] == 17 {
 					if err := src.Close(); err != nil {
 						return 0, err
 					}
 					return 0, io.EOF
 				}
 			}
 			// ---- End of docker
 			nw, ew := dst.Write(buf[0:nr])
 			if nw > 0 {
 				written += int64(nw)
 			}
 			if ew != nil {
 				err = ew
 				break
 			}
 			if nr != nw {
 				err = io.ErrShortWrite
 				break
 			}
 		}
 		if er == io.EOF {
 			break
 		}
 		if er != nil {
 			err = er
 			break
 		}
 	}
 	return written, err
 }
 
 func HashData(src io.Reader) (string, error) {
 	h := sha256.New()
 	if _, err := io.Copy(h, src); err != nil {
 		return "", err
 	}
 	return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
 }
 
 type KernelVersionInfo struct {
 	Kernel int
 	Major  int
 	Minor  int
 	Flavor string
 }
 
 func (k *KernelVersionInfo) String() string {
 	flavor := ""
 	if len(k.Flavor) > 0 {
 		flavor = fmt.Sprintf("-%s", k.Flavor)
 	}
 	return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, flavor)
 }
 
 // Compare two KernelVersionInfo struct.
 // Returns -1 if a < b, = if a == b, 1 it a > b
 func CompareKernelVersion(a, b *KernelVersionInfo) int {
 	if a.Kernel < b.Kernel {
 		return -1
 	} else if a.Kernel > b.Kernel {
 		return 1
 	}
 
 	if a.Major < b.Major {
 		return -1
 	} else if a.Major > b.Major {
 		return 1
 	}
 
 	if a.Minor < b.Minor {
 		return -1
 	} else if a.Minor > b.Minor {
 		return 1
 	}
 
 	return 0
 }
 
 func FindCgroupMountpoint(cgroupType string) (string, error) {
 	output, err := ioutil.ReadFile("/proc/mounts")
 	if err != nil {
 		return "", err
 	}
 
 	// /proc/mounts has 6 fields per line, one mount per line, e.g.
 	// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
 	for _, line := range strings.Split(string(output), "\n") {
 		parts := strings.Split(line, " ")
 		if len(parts) == 6 && parts[2] == "cgroup" {
 			for _, opt := range strings.Split(parts[3], ",") {
 				if opt == cgroupType {
 					return parts[1], nil
 				}
 			}
 		}
 	}
 
 	return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
 }
f3bab52d
 
 func GetKernelVersion() (*KernelVersionInfo, error) {
 	var (
 		flavor               string
 		kernel, major, minor int
 		err                  error
 	)
 
 	uts, err := uname()
 	if err != nil {
 		return nil, err
 	}
 
 	release := make([]byte, len(uts.Release))
 
 	i := 0
 	for _, c := range uts.Release {
 		release[i] = byte(c)
 		i++
 	}
 
 	// Remove the \x00 from the release for Atoi to parse correctly
 	release = release[:bytes.IndexByte(release, 0)]
 
 	tmp := strings.SplitN(string(release), "-", 2)
 	tmp2 := strings.SplitN(tmp[0], ".", 3)
 
 	if len(tmp2) > 0 {
 		kernel, err = strconv.Atoi(tmp2[0])
 		if err != nil {
 			return nil, err
 		}
 	}
 
 	if len(tmp2) > 1 {
 		major, err = strconv.Atoi(tmp2[1])
 		if err != nil {
 			return nil, err
 		}
 	}
 
 	if len(tmp2) > 2 {
 		minor, err = strconv.Atoi(tmp2[2])
 		if err != nil {
 			return nil, err
 		}
 	}
 
 	if len(tmp) == 2 {
 		flavor = tmp[1]
 	} else {
 		flavor = ""
 	}
 
 	return &KernelVersionInfo{
 		Kernel: kernel,
 		Major:  major,
 		Minor:  minor,
 		Flavor: flavor,
 	}, nil
 }
0143be42
 
5b828761
 // FIXME: this is deprecated by CopyWithTar in archive.go
6ae38001
 func CopyDirectory(source, dest string) error {
2897cb04
 	if output, err := exec.Command("cp", "-ra", source, dest).CombinedOutput(); err != nil {
 		return fmt.Errorf("Error copy: %s (%s)", err, output)
6ae38001
 	}
 	return nil
 }
 
ae9d7a51
 type NopFlusher struct{}
 
 func (f *NopFlusher) Flush() {}
 
98b0fd17
 type WriteFlusher struct {
ae9d7a51
 	w       io.Writer
 	flusher http.Flusher
98b0fd17
 }
0143be42
 
98b0fd17
 func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
ae9d7a51
 	n, err = wf.w.Write(b)
 	wf.flusher.Flush()
0143be42
 	return n, err
98b0fd17
 }
ae9d7a51
 
 func NewWriteFlusher(w io.Writer) *WriteFlusher {
 	var flusher http.Flusher
 	if f, ok := w.(http.Flusher); ok {
 		flusher = f
 	} else {
 		flusher = &NopFlusher{}
 	}
 	return &WriteFlusher{w: w, flusher: flusher}
 }
3c7bca7a
 
fd224ee5
 type JSONMessage struct {
14212930
 	Status   string `json:"status,omitempty"`
 	Progress string `json:"progress,omitempty"`
 	Error    string `json:"error,omitempty"`
 }
 
c8c7094b
 type StreamFormatter struct {
 	json bool
 	used bool
 }
 
 func NewStreamFormatter(json bool) *StreamFormatter {
 	return &StreamFormatter{json, false}
 }
 
5a36efb6
 func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte {
c8c7094b
 	sf.used = true
5a36efb6
 	str := fmt.Sprintf(format, a...)
c8c7094b
 	if sf.json {
84d68007
 		b, err := json.Marshal(&JSONMessage{Status: str})
14212930
 		if err != nil {
 			return sf.FormatError(err)
 		}
5a36efb6
 		return b
3c7bca7a
 	}
5a36efb6
 	return []byte(str + "\r\n")
3c7bca7a
 }
 
5a36efb6
 func (sf *StreamFormatter) FormatError(err error) []byte {
c8c7094b
 	sf.used = true
 	if sf.json {
84d68007
 		if b, err := json.Marshal(&JSONMessage{Error: err.Error()}); err == nil {
5a36efb6
 			return b
14212930
 		}
5a36efb6
 		return []byte("{\"error\":\"format error\"}")
3c7bca7a
 	}
5a36efb6
 	return []byte("Error: " + err.Error() + "\r\n")
cb0bc4ad
 }
 
5a36efb6
 func (sf *StreamFormatter) FormatProgress(action, str string) []byte {
c8c7094b
 	sf.used = true
 	if sf.json {
84d68007
 		b, err := json.Marshal(&JSONMessage{Status: action, Progress: str})
14212930
 		if err != nil {
84d68007
 			return nil
 		}
5a36efb6
 		return b
3c7bca7a
 	}
5a36efb6
 	return []byte(action + " " + str + "\r")
c8c7094b
 }
 
 func (sf *StreamFormatter) Used() bool {
 	return sf.used
3c7bca7a
 }
a3f6054f
 
 func CheckLocalDns() bool {
 	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
 	if err != nil {
 		Debugf("Error openning resolv.conf: %s", err)
 		return false
 	}
 	for _, ip := range []string{
 		"127.0.0.1",
 		"127.0.1.1",
 	} {
 		if strings.Contains(string(resolv), ip) {
 			return true
 		}
 	}
 	return false
 }
3adf9ce0
 
dede1585
 func ParseHost(host string, port int, addr string) string {
 	if strings.HasPrefix(addr, "unix://") {
 		return addr
 	}
 	if strings.HasPrefix(addr, "tcp://") {
 		addr = strings.TrimPrefix(addr, "tcp://")
 	}
 	if strings.Contains(addr, ":") {
 		hostParts := strings.Split(addr, ":")
 		if len(hostParts) != 2 {
 			log.Fatal("Invalid bind address format.")
 			os.Exit(-1)
 		}
 		if hostParts[0] != "" {
 			host = hostParts[0]
 		}
 		if p, err := strconv.Atoi(hostParts[1]); err == nil {
 			port = p
 		}
 	} else {
 		host = addr
 	}
 	return fmt.Sprintf("tcp://%s:%d", host, port)
 }
 
3adf9ce0