utils.go
a27b4b8c
 package docker
 
 import (
 	"bytes"
d7c5d060
 	"errors"
299d0b27
 	"fmt"
6e507b94
 	"github.com/dotcloud/docker/rcli"
0b9a3c86
 	"index/suffixarray"
a27b4b8c
 	"io"
99f9b697
 	"io/ioutil"
d7c5d060
 	"net/http"
e6adfa2b
 	"os"
37035363
 	"os/exec"
e6adfa2b
 	"path/filepath"
6576b19a
 	"runtime"
 	"strings"
333abbf8
 	"sync"
299d0b27
 	"time"
a27b4b8c
 )
 
623e91e2
 // 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
 }
 
d7c5d060
 // Request a given URL and return an io.Reader
 func Download(url string, stderr io.Writer) (*http.Response, error) {
 	var resp *http.Response
 	var err error = nil
 	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
 }
 
6e507b94
 // 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{}) {
76663079
 	if os.Getenv("DEBUG") != "" {
6576b19a
 
 		// 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...)
6e507b94
 		if rcli.CLIENT_SOCKET != nil {
6576b19a
 			fmt.Fprintf(rcli.CLIENT_SOCKET, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...)
6e507b94
 		}
 	}
 }
 
d7c5d060
 // Reader with progress bar
 type progressReader struct {
a6da7f13
 	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
965e8a02
 	template     string        // Template to print. Default "%v/%v (%v)"
d7c5d060
 }
 
 func (r *progressReader) Read(p []byte) (n int, err error) {
 	read, err := io.ReadCloser(r.reader).Read(p)
a6da7f13
 	r.readProgress += read
d7c5d060
 
965e8a02
 	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 {
 			fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
 		} else {
 			fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a")
 		}
a6da7f13
 		r.lastUpdate = r.readProgress
d7c5d060
 	}
 	// Send newline when complete
965e8a02
 	if err != nil {
d7c5d060
 		fmt.Fprintf(r.output, "\n")
 	}
 
 	return read, err
 }
 func (r *progressReader) Close() error {
 	return io.ReadCloser(r.reader).Close()
 }
965e8a02
 func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
 	if template == "" {
 		template = "%v/%v (%v)"
 	}
 	return &progressReader{r, output, size, 0, 0, template}
d7c5d060
 }
 
299d0b27
 // 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)
 }
 
bcfe2aa2
 func Trunc(s string, maxlen int) string {
 	if len(s) <= maxlen {
 		return s
 	}
 	return s[:maxlen]
 }
 
e6adfa2b
 // 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
 }
 
5d6dd22f
 type nopWriteCloser struct {
 	io.Writer
 }
 
 func (w *nopWriteCloser) Close() error { return nil }
 
 func NopWriteCloser(w io.Writer) io.WriteCloser {
 	return &nopWriteCloser{w}
 }
 
a27b4b8c
 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)
d52451bc
 		r.l.Lock()
a27b4b8c
 		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) {
d52451bc
 	r.l.Lock()
 	defer r.l.Unlock()
a27b4b8c
 	for {
 		n, err = r.buf.Read(p)
 		if n > 0 {
 			return n, err
 		}
 		if r.err != nil {
 			return 0, r.err
 		}
 		r.wait.Wait()
 	}
22f1cc95
 	panic("unreachable")
a27b4b8c
 }
 
 func (r *bufReader) Close() error {
 	closer, ok := r.reader.(io.ReadCloser)
 	if !ok {
 		return nil
 	}
 	return closer.Close()
 }
 
 type writeBroadcaster struct {
0bdfcfaa
 	mu      sync.Mutex
75ba07cb
 	writers map[io.WriteCloser]struct{}
a27b4b8c
 }
 
 func (w *writeBroadcaster) AddWriter(writer io.WriteCloser) {
0bdfcfaa
 	w.mu.Lock()
75ba07cb
 	w.writers[writer] = struct{}{}
0bdfcfaa
 	w.mu.Unlock()
a27b4b8c
 }
 
0b2eee8b
 // FIXME: Is that function used?
0bdfcfaa
 // FIXME: This relies on the concrete writer type used having equality operator
a27b4b8c
 func (w *writeBroadcaster) RemoveWriter(writer io.WriteCloser) {
0bdfcfaa
 	w.mu.Lock()
75ba07cb
 	delete(w.writers, writer)
 	w.mu.Unlock()
a27b4b8c
 }
 
 func (w *writeBroadcaster) Write(p []byte) (n int, err error) {
0bdfcfaa
 	w.mu.Lock()
 	defer w.mu.Unlock()
75ba07cb
 	for writer := range w.writers {
a27b4b8c
 		if n, err := writer.Write(p); err != nil || n != len(p) {
 			// On error, evict the writer
75ba07cb
 			delete(w.writers, writer)
a27b4b8c
 		}
 	}
 	return len(p), nil
 }
 
a83d87ab
 func (w *writeBroadcaster) CloseWriters() error {
0bdfcfaa
 	w.mu.Lock()
 	defer w.mu.Unlock()
75ba07cb
 	for writer := range w.writers {
a27b4b8c
 		writer.Close()
 	}
75ba07cb
 	w.writers = make(map[io.WriteCloser]struct{})
a27b4b8c
 	return nil
 }
 
 func newWriteBroadcaster() *writeBroadcaster {
75ba07cb
 	return &writeBroadcaster{writers: make(map[io.WriteCloser]struct{})}
a27b4b8c
 }
99f9b697
 
 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
 }
0b9a3c86
 
 // 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
 }
1632566e
 
 // TruncateId returns a shorthand version of a string identifier for convenience.
 // 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.
 func TruncateId(id string) string {
 	shortLen := 12
 	if len(id) < shortLen {
 		shortLen = len(id)
 	}
 	return id[:shortLen]
 }
1f70b1e1
 
 // 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
626bfd87
 			// char 16 is C-p
 			if nr == 1 && buf[0] == 16 {
1f70b1e1
 				nr, er = src.Read(buf)
626bfd87
 				// char 17 is C-q
 				if nr == 1 && buf[0] == 17 {
1f70b1e1
 					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
 }
003622c8
 
 type KernelVersionInfo struct {
1f65c6bf
 	Kernel int
 	Major  int
 	Minor  int
 	Flavor string
003622c8
 }
 
965e8a02
 // FIXME: this doens't build on Darwin
003622c8
 func GetKernelVersion() (*KernelVersionInfo, error) {
16aeb77d
 	return getKernelVersion()
003622c8
 }
 
 func (k *KernelVersionInfo) String() string {
1f65c6bf
 	return fmt.Sprintf("%d.%d.%d-%s", k.Kernel, k.Major, k.Minor, k.Flavor)
003622c8
 }
 
 // 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
 }
f3e89fae
 
 func FindCgroupMountpoint(cgroupType string) (string, error) {
c6119da3
 	output, err := ioutil.ReadFile("/proc/mounts")
f3e89fae
 	if err != nil {
 		return "", err
 	}
 
c6119da3
 	// /proc/mounts has 6 fields per line, one mount per line, e.g.
 	// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
f3e89fae
 	for _, line := range strings.Split(string(output), "\n") {
c6119da3
 		parts := strings.Split(line, " ")
 		if parts[2] == "cgroup" {
 			for _, opt := range strings.Split(parts[3], ",") {
 				if opt == cgroupType {
 					return parts[1], nil
 				}
 			}
f3e89fae
 		}
 	}
c6119da3
 
71b58066
 	return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
f3e89fae
 }