|
@@ -3,8 +3,10 @@ package docker
|
|
|
import (
|
|
|
"bytes"
|
|
|
"container/list"
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
+ "net/http"
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
"path/filepath"
|
|
@@ -12,6 +14,55 @@ import (
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
+// 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
|
|
|
+}
|
|
|
+
|
|
|
+// Reader with progress bar
|
|
|
+type progressReader struct {
|
|
|
+ reader io.ReadCloser // Stream to read from
|
|
|
+ output io.Writer // Where to send progress bar to
|
|
|
+ read_total int // Expected stream length (bytes)
|
|
|
+ read_progress int // How much has been read so far (bytes)
|
|
|
+ last_update int // How many bytes read at least update
|
|
|
+}
|
|
|
+
|
|
|
+func (r *progressReader) Read(p []byte) (n int, err error) {
|
|
|
+ read, err := io.ReadCloser(r.reader).Read(p)
|
|
|
+ r.read_progress += read
|
|
|
+
|
|
|
+ // Only update progress for every 1% read
|
|
|
+ update_every := int(0.01 * float64(r.read_total))
|
|
|
+ if r.read_progress-r.last_update > update_every || r.read_progress == r.read_total {
|
|
|
+ fmt.Fprintf(r.output, "%d/%d (%.0f%%)\r",
|
|
|
+ r.read_progress,
|
|
|
+ r.read_total,
|
|
|
+ float64(r.read_progress)/float64(r.read_total)*100)
|
|
|
+ r.last_update = r.read_progress
|
|
|
+ }
|
|
|
+ // Send newline when complete
|
|
|
+ if err == io.EOF {
|
|
|
+ fmt.Fprintf(r.output, "\n")
|
|
|
+ }
|
|
|
+
|
|
|
+ return read, err
|
|
|
+}
|
|
|
+func (r *progressReader) Close() error {
|
|
|
+ return io.ReadCloser(r.reader).Close()
|
|
|
+}
|
|
|
+func ProgressReader(r io.ReadCloser, size int, output io.Writer) *progressReader {
|
|
|
+ return &progressReader{r, output, size, 0, 0}
|
|
|
+}
|
|
|
+
|
|
|
// HumanDuration returns a human-readable approximation of a duration
|
|
|
// (eg. "About a minute", "4 hours ago", etc.)
|
|
|
func HumanDuration(d time.Duration) string {
|