dotcloud/docker

View on GitHub
pkg/progress/progressreader.go

Summary

Maintainability
A
0 mins
Test Coverage
package progress // import "github.com/docker/docker/pkg/progress"

import (
    "io"
    "time"

    "golang.org/x/time/rate"
)

// Reader is a Reader with progress bar.
type Reader struct {
    in          io.ReadCloser // Stream to read from
    out         Output        // Where to send progress bar to
    size        int64
    current     int64
    lastUpdate  int64
    id          string
    action      string
    rateLimiter *rate.Limiter
}

// NewProgressReader creates a new ProgressReader.
func NewProgressReader(in io.ReadCloser, out Output, size int64, id, action string) *Reader {
    return &Reader{
        in:          in,
        out:         out,
        size:        size,
        id:          id,
        action:      action,
        rateLimiter: rate.NewLimiter(rate.Every(100*time.Millisecond), 1),
    }
}

func (p *Reader) Read(buf []byte) (n int, err error) {
    read, err := p.in.Read(buf)
    p.current += int64(read)
    updateEvery := int64(1024 * 512) // 512kB
    if p.size > 0 {
        // Update progress for every 1% read if 1% < 512kB
        if increment := int64(0.01 * float64(p.size)); increment < updateEvery {
            updateEvery = increment
        }
    }
    if p.current-p.lastUpdate > updateEvery || err != nil {
        p.updateProgress(err != nil && read == 0)
        p.lastUpdate = p.current
    }

    return read, err
}

// Close closes the progress reader and its underlying reader.
func (p *Reader) Close() error {
    if p.current < p.size {
        // print a full progress bar when closing prematurely
        p.current = p.size
        p.updateProgress(false)
    }
    return p.in.Close()
}

func (p *Reader) updateProgress(last bool) {
    if last || p.current == p.size || p.rateLimiter.Allow() {
        p.out.WriteProgress(Progress{ID: p.id, Action: p.action, Current: p.current, Total: p.size, LastUpdate: last})
    }
}