diff --git a/api.go b/api.go index 8df3291357..a009ae5549 100644 --- a/api.go +++ b/api.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/dotcloud/docker/auth" + "github.com/dotcloud/docker/utils" "github.com/gorilla/mux" "github.com/shin-/cookiejar" "io" @@ -116,7 +117,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va name := vars["name"] if err := srv.ContainerExport(name, w); err != nil { - Debugf("%s", err.Error()) + utils.Debugf("%s", err.Error()) return err } return nil @@ -239,7 +240,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st } config := &Config{} if err := json.NewDecoder(r.Body).Decode(config); err != nil { - Debugf("%s", err.Error()) + utils.Debugf("%s", err.Error()) } repo := r.Form.Get("repo") tag := r.Form.Get("tag") @@ -602,20 +603,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { for method, routes := range m { for route, fct := range routes { - Debugf("Registering %s, %s", method, route) + utils.Debugf("Registering %s, %s", method, route) // NOTE: scope issue, make sure the variables are local and won't be changed localRoute := route localMethod := method localFct := fct r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - Debugf("Calling %s %s", localMethod, localRoute) + utils.Debugf("Calling %s %s", localMethod, localRoute) if logging { log.Println(r.Method, r.RequestURI) } if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { userAgent := strings.Split(r.Header.Get("User-Agent"), "/") if len(userAgent) == 2 && userAgent[1] != VERSION { - Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION) + utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION) } } if err := localFct(srv, w, r, mux.Vars(r)); err != nil { diff --git a/api_test.go b/api_test.go index 5096b05212..0151c98b66 100644 --- a/api_test.go +++ b/api_test.go @@ -811,7 +811,7 @@ func TestPostContainersCreate(t *testing.T) { if _, err := os.Stat(path.Join(container.rwPath(), "test")); err != nil { if os.IsNotExist(err) { - Debugf("Err: %s", err) + utils.Debugf("Err: %s", err) t.Fatalf("The test file has not been created") } t.Fatal(err) diff --git a/builder.go b/builder.go index 5c51d62b9e..e9c4812c67 100644 --- a/builder.go +++ b/builder.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/json" "fmt" + "github.com/dotcloud/docker/utils" "io" "os" "path" @@ -161,11 +162,11 @@ func (builder *Builder) clearTmp(containers, images map[string]struct{}) { for c := range containers { tmp := builder.runtime.Get(c) builder.runtime.Destroy(tmp) - Debugf("Removing container %s", c) + utils.Debugf("Removing container %s", c) } for i := range images { builder.runtime.graph.Delete(i) - Debugf("Removing image %s", i) + utils.Debugf("Removing image %s", i) } } @@ -286,7 +287,7 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e break } - Debugf("Env -----> %v ------ %v\n", config.Env, env) + utils.Debugf("Env -----> %v ------ %v\n", config.Env, env) // Create the container and start it c, err := builder.Create(config) @@ -410,7 +411,7 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e destPath := strings.Trim(tmp[1], " ") fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId()) - file, err := Download(sourceUrl, stdout) + file, err := utils.Download(sourceUrl, stdout) if err != nil { return nil, err } diff --git a/commands.go b/commands.go index 9e26790f5d..7cb3511cda 100644 --- a/commands.go +++ b/commands.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/term" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "net" @@ -188,11 +189,11 @@ func CmdLogin(args ...string) error { return readStringOnRawTerminal(stdin, stdout, false) } - oldState, err := SetRawTerminal() + oldState, err := term.SetRawTerminal() if err != nil { return err } else { - defer RestoreTerminal(oldState) + defer term.RestoreTerminal(oldState) } cmd := Subcmd("login", "", "Register or Login to the docker registry server") @@ -252,7 +253,7 @@ func CmdLogin(args ...string) error { return err } if out2.Status != "" { - RestoreTerminal(oldState) + term.RestoreTerminal(oldState) fmt.Print(out2.Status) } return nil @@ -303,7 +304,7 @@ func CmdVersion(args ...string) error { var out ApiVersion err = json.Unmarshal(body, &out) if err != nil { - Debugf("Error unmarshal: body: %s, err: %s\n", body, err) + utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err) return err } fmt.Println("Version:", out.Version) @@ -519,7 +520,7 @@ func CmdHistory(args ...string) error { fmt.Fprintln(w, "ID\tCREATED\tCREATED BY") for _, out := range outs { - fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) + fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) } w.Flush() return nil @@ -742,14 +743,14 @@ func CmdImages(args ...string) error { if *noTrunc { fmt.Fprintf(w, "%s\t", out.Id) } else { - fmt.Fprintf(w, "%s\t", TruncateId(out.Id)) + fmt.Fprintf(w, "%s\t", utils.TruncateId(out.Id)) } - fmt.Fprintf(w, "%s ago\n", HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) + fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) } else { if *noTrunc { fmt.Fprintln(w, out.Id) } else { - fmt.Fprintln(w, TruncateId(out.Id)) + fmt.Fprintln(w, utils.TruncateId(out.Id)) } } } @@ -809,15 +810,15 @@ func CmdPs(args ...string) error { for _, out := range outs { if !*quiet { if *noTrunc { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", out.Id, out.Image, out.Command, out.Status, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", out.Id, out.Image, out.Command, out.Status, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) } else { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", TruncateId(out.Id), out.Image, Trunc(out.Command, 20), out.Status, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", utils.TruncateId(out.Id), out.Image, utils.Trunc(out.Command, 20), out.Status, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) } } else { if *noTrunc { fmt.Fprintln(w, out.Id) } else { - fmt.Fprintln(w, TruncateId(out.Id)) + fmt.Fprintln(w, utils.TruncateId(out.Id)) } } } @@ -1244,20 +1245,20 @@ func hijack(method, path string, setRawTerminal bool) error { rwc, br := clientconn.Hijack() defer rwc.Close() - receiveStdout := Go(func() error { + receiveStdout := utils.Go(func() error { _, err := io.Copy(os.Stdout, br) return err }) if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { - if oldState, err := SetRawTerminal(); err != nil { + if oldState, err := term.SetRawTerminal(); err != nil { return err } else { - defer RestoreTerminal(oldState) + defer term.RestoreTerminal(oldState) } } - sendStdin := Go(func() error { + sendStdin := utils.Go(func() error { _, err := io.Copy(rwc, os.Stdin) if err := rwc.(*net.TCPConn).CloseWrite(); err != nil { fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err) diff --git a/container.go b/container.go index d4ebc60c8b..a82ce0291a 100644 --- a/container.go +++ b/container.go @@ -4,6 +4,7 @@ import ( "encoding/json" "flag" "fmt" + "github.com/dotcloud/docker/utils" "github.com/kr/pty" "io" "io/ioutil" @@ -39,8 +40,8 @@ type Container struct { ResolvConfPath string cmd *exec.Cmd - stdout *writeBroadcaster - stderr *writeBroadcaster + stdout *utils.WriteBroadcaster + stderr *utils.WriteBroadcaster stdin io.ReadCloser stdinPipe io.WriteCloser ptyMaster io.Closer @@ -251,9 +252,9 @@ func (container *Container) startPty() error { // Copy the PTYs to our broadcasters go func() { defer container.stdout.CloseWriters() - Debugf("[startPty] Begin of stdout pipe") + utils.Debugf("[startPty] Begin of stdout pipe") io.Copy(container.stdout, ptyMaster) - Debugf("[startPty] End of stdout pipe") + utils.Debugf("[startPty] End of stdout pipe") }() // stdin @@ -262,9 +263,9 @@ func (container *Container) startPty() error { container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} go func() { defer container.stdin.Close() - Debugf("[startPty] Begin of stdin pipe") + utils.Debugf("[startPty] Begin of stdin pipe") io.Copy(ptyMaster, container.stdin) - Debugf("[startPty] End of stdin pipe") + utils.Debugf("[startPty] End of stdin pipe") }() } if err := container.cmd.Start(); err != nil { @@ -284,9 +285,9 @@ func (container *Container) start() error { } go func() { defer stdin.Close() - Debugf("Begin of stdin pipe [start]") + utils.Debugf("Begin of stdin pipe [start]") io.Copy(stdin, container.stdin) - Debugf("End of stdin pipe [start]") + utils.Debugf("End of stdin pipe [start]") }() } return container.cmd.Start() @@ -303,8 +304,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s errors <- err } else { go func() { - Debugf("[start] attach stdin\n") - defer Debugf("[end] attach stdin\n") + utils.Debugf("[start] attach stdin\n") + defer utils.Debugf("[end] attach stdin\n") // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr if cStdout != nil { defer cStdout.Close() @@ -316,12 +317,12 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s defer cStdin.Close() } if container.Config.Tty { - _, err = CopyEscapable(cStdin, stdin) + _, err = utils.CopyEscapable(cStdin, stdin) } else { _, err = io.Copy(cStdin, stdin) } if err != nil { - Debugf("[error] attach stdin: %s\n", err) + utils.Debugf("[error] attach stdin: %s\n", err) } // Discard error, expecting pipe error errors <- nil @@ -335,8 +336,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s } else { cStdout = p go func() { - Debugf("[start] attach stdout\n") - defer Debugf("[end] attach stdout\n") + utils.Debugf("[start] attach stdout\n") + defer utils.Debugf("[end] attach stdout\n") // If we are in StdinOnce mode, then close stdin if container.Config.StdinOnce { if stdin != nil { @@ -348,7 +349,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s } _, err := io.Copy(stdout, cStdout) if err != nil { - Debugf("[error] attach stdout: %s\n", err) + utils.Debugf("[error] attach stdout: %s\n", err) } errors <- err }() @@ -361,8 +362,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s } else { cStderr = p go func() { - Debugf("[start] attach stderr\n") - defer Debugf("[end] attach stderr\n") + utils.Debugf("[start] attach stderr\n") + defer utils.Debugf("[end] attach stderr\n") // If we are in StdinOnce mode, then close stdin if container.Config.StdinOnce { if stdin != nil { @@ -374,13 +375,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s } _, err := io.Copy(stderr, cStderr) if err != nil { - Debugf("[error] attach stderr: %s\n", err) + utils.Debugf("[error] attach stderr: %s\n", err) } errors <- err }() } } - return Go(func() error { + return utils.Go(func() error { if cStdout != nil { defer cStdout.Close() } @@ -390,14 +391,14 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s // FIXME: how do clean up the stdin goroutine without the unwanted side effect // of closing the passed stdin? Add an intermediary io.Pipe? for i := 0; i < nJobs; i += 1 { - Debugf("Waiting for job %d/%d\n", i+1, nJobs) + utils.Debugf("Waiting for job %d/%d\n", i+1, nJobs) if err := <-errors; err != nil { - Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err) + utils.Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err) return err } - Debugf("Job %d completed successfully\n", i+1) + utils.Debugf("Job %d completed successfully\n", i+1) } - Debugf("All jobs completed successfully\n") + utils.Debugf("All jobs completed successfully\n") return nil }) } @@ -555,13 +556,13 @@ func (container *Container) StdinPipe() (io.WriteCloser, error) { func (container *Container) StdoutPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() container.stdout.AddWriter(writer) - return newBufReader(reader), nil + return utils.NewBufReader(reader), nil } func (container *Container) StderrPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() container.stderr.AddWriter(writer) - return newBufReader(reader), nil + return utils.NewBufReader(reader), nil } func (container *Container) allocateNetwork() error { @@ -609,20 +610,20 @@ func (container *Container) waitLxc() error { func (container *Container) monitor() { // Wait for the program to exit - Debugf("Waiting for process") + utils.Debugf("Waiting for process") // If the command does not exists, try to wait via lxc if container.cmd == nil { if err := container.waitLxc(); err != nil { - Debugf("%s: Process: %s", container.Id, err) + utils.Debugf("%s: Process: %s", container.Id, err) } } else { if err := container.cmd.Wait(); err != nil { // Discard the error as any signals or non 0 returns will generate an error - Debugf("%s: Process: %s", container.Id, err) + utils.Debugf("%s: Process: %s", container.Id, err) } } - Debugf("Process finished") + utils.Debugf("Process finished") var exitCode int = -1 if container.cmd != nil { @@ -633,19 +634,19 @@ func (container *Container) monitor() { container.releaseNetwork() if container.Config.OpenStdin { if err := container.stdin.Close(); err != nil { - Debugf("%s: Error close stdin: %s", container.Id, err) + utils.Debugf("%s: Error close stdin: %s", container.Id, err) } } if err := container.stdout.CloseWriters(); err != nil { - Debugf("%s: Error close stdout: %s", container.Id, err) + utils.Debugf("%s: Error close stdout: %s", container.Id, err) } if err := container.stderr.CloseWriters(); err != nil { - Debugf("%s: Error close stderr: %s", container.Id, err) + utils.Debugf("%s: Error close stderr: %s", container.Id, err) } if container.ptyMaster != nil { if err := container.ptyMaster.Close(); err != nil { - Debugf("%s: Error closing Pty master: %s", container.Id, err) + utils.Debugf("%s: Error closing Pty master: %s", container.Id, err) } } @@ -762,7 +763,7 @@ func (container *Container) RwChecksum() (string, error) { if err != nil { return "", err } - return HashData(rwData) + return utils.HashData(rwData) } func (container *Container) Export() (Archive, error) { @@ -833,7 +834,7 @@ func (container *Container) Unmount() error { // In case of a collision a lookup with Runtime.Get() will fail, and the caller // will need to use a langer prefix, or the full-length container Id. func (container *Container) ShortId() string { - return TruncateId(container.Id) + return utils.TruncateId(container.Id) } func (container *Container) logPath(name string) string { diff --git a/docker/docker.go b/docker/docker.go index 778326a810..c8c1a65603 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "github.com/dotcloud/docker" + "github.com/dotcloud/docker/utils" "io/ioutil" "log" "os" @@ -17,7 +18,7 @@ var ( ) func main() { - if docker.SelfPath() == "/sbin/init" { + if utils.SelfPath() == "/sbin/init" { // Running in init mode docker.SysInit() return diff --git a/getKernelVersion_darwin.go b/getKernelVersion_darwin.go index be3b733b68..2fce282716 100644 --- a/getKernelVersion_darwin.go +++ b/getKernelVersion_darwin.go @@ -2,8 +2,9 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/utils" ) -func getKernelVersion() (*KernelVersionInfo, error) { +func getKernelVersion() (*utils.KernelVersionInfo, error) { return nil, fmt.Errorf("Kernel version detection is not available on darwin") } diff --git a/getKernelVersion_linux.go b/getKernelVersion_linux.go index 04bb1edcb8..4f9c7db70c 100644 --- a/getKernelVersion_linux.go +++ b/getKernelVersion_linux.go @@ -2,12 +2,14 @@ package docker import ( "bytes" + "github.com/dotcloud/docker/utils" "strconv" "strings" "syscall" ) -func getKernelVersion() (*KernelVersionInfo, error) { +// FIXME: Move this to utils package +func getKernelVersion() (*utils.KernelVersionInfo, error) { var ( uts syscall.Utsname flavor string @@ -60,7 +62,7 @@ func getKernelVersion() (*KernelVersionInfo, error) { flavor = "" } - return &KernelVersionInfo{ + return &utils.KernelVersionInfo{ Kernel: kernel, Major: major, Minor: minor, diff --git a/graph.go b/graph.go index 14731622e6..dbb9b463ab 100644 --- a/graph.go +++ b/graph.go @@ -3,6 +3,7 @@ package docker import ( "encoding/json" "fmt" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "net/http" @@ -17,7 +18,7 @@ import ( // A Graph is a store for versioned filesystem images and the relationship between them. type Graph struct { Root string - idIndex *TruncIndex + idIndex *utils.TruncIndex httpClient *http.Client checksumLock map[string]*sync.Mutex lockSumFile *sync.Mutex @@ -37,7 +38,7 @@ func NewGraph(root string) (*Graph, error) { } graph := &Graph{ Root: abspath, - idIndex: NewTruncIndex(), + idIndex: utils.NewTruncIndex(), checksumLock: make(map[string]*sync.Mutex), lockSumFile: &sync.Mutex{}, lockSumMap: &sync.Mutex{}, @@ -165,7 +166,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output if err != nil { return nil, err } - return NewTempArchive(ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root) + return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root) } // Mktemp creates a temporary sub-directory inside the graph's filesystem. diff --git a/image.go b/image.go index 413d95673b..89a3c66552 100644 --- a/image.go +++ b/image.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "log" @@ -180,7 +181,7 @@ func (image *Image) Changes(rw string) ([]Change, error) { } func (image *Image) ShortId() string { - return TruncateId(image.Id) + return utils.TruncateId(image.Id) } func ValidateId(id string) error { diff --git a/network.go b/network.go index 373625d59c..77a82ed9f0 100644 --- a/network.go +++ b/network.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/dotcloud/docker/utils" "io" "log" "net" @@ -97,7 +98,7 @@ func checkRouteOverlaps(dockerNetwork *net.IPNet) error { if err != nil { return err } - Debugf("Routes:\n\n%s", output) + utils.Debugf("Routes:\n\n%s", output) for _, line := range strings.Split(output, "\n") { if strings.Trim(line, "\r\n\t ") == "" || strings.Contains(line, "default") { continue @@ -126,13 +127,13 @@ func CreateBridgeIface(ifaceName string) error { ifaceAddr = addr break } else { - Debugf("%s: %s", addr, err) + utils.Debugf("%s: %s", addr, err) } } if ifaceAddr == "" { return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName) } else { - Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr) + utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr) } if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil { @@ -239,22 +240,22 @@ func (mapper *PortMapper) Map(port int, dest net.TCPAddr) error { // proxy listens for socket connections on `listener`, and forwards them unmodified // to `proto:address` func proxy(listener net.Listener, proto, address string) error { - Debugf("proxying to %s:%s", proto, address) - defer Debugf("Done proxying to %s:%s", proto, address) + utils.Debugf("proxying to %s:%s", proto, address) + defer utils.Debugf("Done proxying to %s:%s", proto, address) for { - Debugf("Listening on %s", listener) + utils.Debugf("Listening on %s", listener) src, err := listener.Accept() if err != nil { return err } - Debugf("Connecting to %s:%s", proto, address) + utils.Debugf("Connecting to %s:%s", proto, address) dst, err := net.Dial(proto, address) if err != nil { log.Printf("Error connecting to %s:%s: %s", proto, address, err) src.Close() continue } - Debugf("Connected to backend, splicing") + utils.Debugf("Connected to backend, splicing") splice(src, dst) } return nil @@ -317,7 +318,7 @@ func (alloc *PortAllocator) runFountain() { // FIXME: Release can no longer fail, change its prototype to reflect that. func (alloc *PortAllocator) Release(port int) error { - Debugf("Releasing %d", port) + utils.Debugf("Releasing %d", port) alloc.lock.Lock() delete(alloc.inUse, port) alloc.lock.Unlock() @@ -325,7 +326,7 @@ func (alloc *PortAllocator) Release(port int) error { } func (alloc *PortAllocator) Acquire(port int) (int, error) { - Debugf("Acquiring %d", port) + utils.Debugf("Acquiring %d", port) if port == 0 { // Allocate a port from the fountain for port := range alloc.fountain { diff --git a/registry.go b/registry.go index d451551297..feb5d20993 100644 --- a/registry.go +++ b/registry.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/dotcloud/docker/auth" + "github.com/dotcloud/docker/utils" "github.com/shin-/cookiejar" "io" "io/ioutil" @@ -19,7 +20,7 @@ import ( func NewImgJson(src []byte) (*Image, error) { ret := &Image{} - Debugf("Json string: {%s}\n", src) + utils.Debugf("Json string: {%s}\n", src) // FIXME: Is there a cleaner way to "purify" the input json? if err := json.Unmarshal(src, ret); err != nil { return nil, err @@ -58,7 +59,7 @@ func (graph *Graph) getRemoteHistory(imgId, registry string, token []string) ([] return nil, fmt.Errorf("Error while reading the http response: %s\n", err) } - Debugf("Ancestry: %s", jsonString) + utils.Debugf("Ancestry: %s", jsonString) history := new([]string) if err := json.Unmarshal(jsonString, history); err != nil { return nil, err @@ -116,7 +117,7 @@ func (graph *Graph) getImagesInRepository(repository string, authConfig *auth.Au err = json.Unmarshal(jsonData, &imageList) if err != nil { - Debugf("Body: %s (%s)\n", res.Body, u) + utils.Debugf("Body: %s (%s)\n", res.Body, u) return nil, err } @@ -166,7 +167,7 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, tok if err != nil { return nil, nil, err } - return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil + return img, utils.ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil } func (graph *Graph) getRemoteTags(stdout io.Writer, registries []string, repository string, token []string) (map[string]string, error) { @@ -185,7 +186,7 @@ func (graph *Graph) getRemoteTags(stdout io.Writer, registries []string, reposit req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) res, err := client.Do(req) defer res.Body.Close() - Debugf("Got status code %d from %s", res.StatusCode, endpoint) + utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint) if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) { continue } else if res.StatusCode == 404 { @@ -416,7 +417,7 @@ func (graph *Graph) PushImage(stdout io.Writer, img *Image, registry string, tok return fmt.Errorf("Error while retrieving checksum for %s: %v", img.Id, err) } req.Header.Set("X-Docker-Checksum", checksum) - Debugf("Setting checksum for %s: %s", img.ShortId(), checksum) + utils.Debugf("Setting checksum for %s: %s", img.ShortId(), checksum) res, err := doWithCookies(client, req) if err != nil { return fmt.Errorf("Failed to upload metadata: %s", err) @@ -469,8 +470,7 @@ func (graph *Graph) PushImage(stdout io.Writer, img *Image, registry string, tok layerData = &TempArchive{file, st.Size()} } - req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer", - ProgressReader(layerData, int(layerData.Size), stdout, "")) + req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer", utils.ProgressReader(layerData, int(layerData.Size), stdout, "")) if err != nil { return err } @@ -502,7 +502,7 @@ func (graph *Graph) pushTag(remote, revision, tag, registry string, token []stri revision = "\"" + revision + "\"" registry = "https://" + registry + "/v1" - Debugf("Pushing tags for rev [%s] on {%s}\n", revision, registry+"/users/"+remote+"/"+tag) + utils.Debugf("Pushing tags for rev [%s] on {%s}\n", revision, registry+"/users/"+remote+"/"+tag) client := graph.getHttpClient() req, err := http.NewRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision)) @@ -624,7 +624,7 @@ func (graph *Graph) PushRepository(stdout io.Writer, remote string, localRepo Re return err } - Debugf("json sent: %s\n", imgListJson) + utils.Debugf("json sent: %s\n", imgListJson) fmt.Fprintf(stdout, "Sending image list\n") req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/", bytes.NewReader(imgListJson)) @@ -642,7 +642,7 @@ func (graph *Graph) PushRepository(stdout io.Writer, remote string, localRepo Re defer res.Body.Close() for res.StatusCode >= 300 && res.StatusCode < 400 { - Debugf("Redirected to %s\n", res.Header.Get("Location")) + utils.Debugf("Redirected to %s\n", res.Header.Get("Location")) req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson)) if err != nil { return err @@ -669,7 +669,7 @@ func (graph *Graph) PushRepository(stdout io.Writer, remote string, localRepo Re var token, endpoints []string if res.Header.Get("X-Docker-Token") != "" { token = res.Header["X-Docker-Token"] - Debugf("Auth token: %v", token) + utils.Debugf("Auth token: %v", token) } else { return fmt.Errorf("Index response didn't contain an access token") } diff --git a/runtime.go b/runtime.go index f2914dba21..de350f2692 100644 --- a/runtime.go +++ b/runtime.go @@ -4,6 +4,7 @@ import ( "container/list" "fmt" "github.com/dotcloud/docker/auth" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "log" @@ -27,9 +28,9 @@ type Runtime struct { graph *Graph repositories *TagStore authConfig *auth.AuthConfig - idIndex *TruncIndex + idIndex *utils.TruncIndex capabilities *Capabilities - kernelVersion *KernelVersionInfo + kernelVersion *utils.KernelVersionInfo autoRestart bool volumes *Graph } @@ -37,7 +38,7 @@ type Runtime struct { var sysInitPath string func init() { - sysInitPath = SelfPath() + sysInitPath = utils.SelfPath() } func (runtime *Runtime) List() []*Container { @@ -113,13 +114,13 @@ func (runtime *Runtime) Register(container *Container) error { container.runtime = runtime // Attach to stdout and stderr - container.stderr = newWriteBroadcaster() - container.stdout = newWriteBroadcaster() + container.stderr = utils.NewWriteBroadcaster() + container.stdout = utils.NewWriteBroadcaster() // Attach to stdin if container.Config.OpenStdin { container.stdin, container.stdinPipe = io.Pipe() } else { - container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin + container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin } // done runtime.containers.PushBack(container) @@ -137,9 +138,9 @@ func (runtime *Runtime) Register(container *Container) error { return err } else { if !strings.Contains(string(output), "RUNNING") { - Debugf("Container %s was supposed to be running be is not.", container.Id) + utils.Debugf("Container %s was supposed to be running be is not.", container.Id) if runtime.autoRestart { - Debugf("Restarting") + utils.Debugf("Restarting") container.State.Ghost = false container.State.setStopped(0) if err := container.Start(); err != nil { @@ -147,7 +148,7 @@ func (runtime *Runtime) Register(container *Container) error { } nomonitor = true } else { - Debugf("Marking as stopped") + utils.Debugf("Marking as stopped") container.State.setStopped(-127) if err := container.ToDisk(); err != nil { return err @@ -168,7 +169,7 @@ func (runtime *Runtime) Register(container *Container) error { return nil } -func (runtime *Runtime) LogToDisk(src *writeBroadcaster, dst string) error { +func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst string) error { log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) if err != nil { return err @@ -215,16 +216,16 @@ func (runtime *Runtime) restore() error { id := v.Name() container, err := runtime.Load(id) if err != nil { - Debugf("Failed to load container %v: %v", id, err) + utils.Debugf("Failed to load container %v: %v", id, err) continue } - Debugf("Loaded container %v", container.Id) + utils.Debugf("Loaded container %v", container.Id) } return nil } func (runtime *Runtime) UpdateCapabilities(quiet bool) { - if cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory"); err != nil { + if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil { if !quiet { log.Printf("WARNING: %s\n", err) } @@ -255,7 +256,7 @@ func NewRuntime(autoRestart bool) (*Runtime, error) { log.Printf("WARNING: %s\n", err) } else { runtime.kernelVersion = k - if CompareKernelVersion(k, &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { + if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { log.Printf("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String()) } } @@ -302,7 +303,7 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) { graph: g, repositories: repositories, authConfig: authConfig, - idIndex: NewTruncIndex(), + idIndex: utils.NewTruncIndex(), capabilities: &Capabilities{}, autoRestart: autoRestart, volumes: volumes, diff --git a/server.go b/server.go index 453574946d..0d34c60a35 100644 --- a/server.go +++ b/server.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/utils" "io" "log" "net/http" @@ -54,7 +55,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) { var out ApiSearch out.Description = repo["description"] if len(out.Description) > 45 { - out.Description = Trunc(out.Description, 42) + "..." + out.Description = utils.Trunc(out.Description, 42) + "..." } out.Name = repo["name"] outs = append(outs, out) @@ -68,7 +69,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { return err } - file, err := Download(url, out) + file, err := utils.Download(url, out) if err != nil { return err } @@ -85,7 +86,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { return err } - if err := c.Inject(ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil { + if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil { return err } // FIXME: Handle custom repo, tag comment, author @@ -124,7 +125,7 @@ func (srv *Server) ImagesViz(out io.Writer) error { for name, repository := range srv.runtime.repositories.Repositories { for tag, id := range repository { - reporefs[TruncateId(id)] = append(reporefs[TruncateId(id)], fmt.Sprintf("%s:%s", name, tag)) + reporefs[utils.TruncateId(id)] = append(reporefs[utils.TruncateId(id)], fmt.Sprintf("%s:%s", name, tag)) } } @@ -193,7 +194,7 @@ func (srv *Server) DockerInfo() ApiInfo { out.GoVersion = runtime.Version() if os.Getenv("DEBUG") != "" { out.Debug = true - out.NFd = getTotalUsedFds() + out.NFd = utils.GetTotalUsedFds() out.NGoroutines = runtime.NumGoroutine() } return out @@ -299,7 +300,7 @@ func (srv *Server) ImagePull(name, tag, registry string, out io.Writer) error { func (srv *Server) ImagePush(name, registry string, out io.Writer) error { img, err := srv.runtime.graph.Get(name) if err != nil { - Debugf("The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) + utils.Debugf("The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) // If it fails, try to get the repository if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists { if err := srv.runtime.graph.PushRepository(out, name, localRepo, srv.runtime.authConfig); err != nil { @@ -336,11 +337,11 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write fmt.Fprintln(out, "Downloading from", u) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() - resp, err = Download(u.String(), out) + resp, err = utils.Download(u.String(), out) if err != nil { return err } - archive = ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)") + archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)") } img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil) if err != nil { @@ -486,17 +487,17 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std if stdout { cLog, err := container.ReadLog("stdout") if err != nil { - Debugf(err.Error()) + utils.Debugf(err.Error()) } else if _, err := io.Copy(out, cLog); err != nil { - Debugf(err.Error()) + utils.Debugf(err.Error()) } } if stderr { cLog, err := container.ReadLog("stderr") if err != nil { - Debugf(err.Error()) + utils.Debugf(err.Error()) } else if _, err := io.Copy(out, cLog); err != nil { - Debugf(err.Error()) + utils.Debugf(err.Error()) } } } @@ -517,7 +518,7 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std r, w := io.Pipe() go func() { defer w.Close() - defer Debugf("Closing buffered stdin pipe") + defer utils.Debugf("Closing buffered stdin pipe") io.Copy(w, in) }() cStdin = r diff --git a/state.go b/state.go index f51a06b01a..a972e376a2 100644 --- a/state.go +++ b/state.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/utils" "sync" "time" ) @@ -21,7 +22,7 @@ func (s *State) String() string { if s.Ghost { return fmt.Sprintf("Ghost") } - return fmt.Sprintf("Up %s", HumanDuration(time.Now().Sub(s.StartedAt))) + return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().Sub(s.StartedAt))) } return fmt.Sprintf("Exit %d", s.ExitCode) } diff --git a/tags.go b/tags.go index 1b9cd19c83..5bc2978e09 100644 --- a/tags.go +++ b/tags.go @@ -3,6 +3,7 @@ package docker import ( "encoding/json" "fmt" + "github.com/dotcloud/docker/utils" "io/ioutil" "os" "path/filepath" @@ -106,7 +107,7 @@ func (store *TagStore) ImageName(id string) string { if names, exists := store.ById()[id]; exists && len(names) > 0 { return names[0] } - return TruncateId(id) + return utils.TruncateId(id) } func (store *TagStore) Set(repoName, tag, imageName string, force bool) error { diff --git a/term/term.go b/term/term.go index 39b77dcc59..8c07b93356 100644 --- a/term/term.go +++ b/term/term.go @@ -1,6 +1,8 @@ package term import ( + "os" + "os/signal" "syscall" "unsafe" ) @@ -120,3 +122,22 @@ func Restore(fd int, state *State) error { _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) return err } + +func SetRawTerminal() (*State, error) { + oldState, err := MakeRaw(int(os.Stdin.Fd())) + if err != nil { + return nil, err + } + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + _ = <-c + Restore(int(os.Stdin.Fd()), oldState) + os.Exit(0) + }() + return oldState, err +} + +func RestoreTerminal(state *State) { + Restore(int(os.Stdin.Fd()), state) +} diff --git a/utils.go b/utils.go index e7f7d319d9..980dfd4c72 100644 --- a/utils.go +++ b/utils.go @@ -1,500 +1,9 @@ package docker import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "github.com/dotcloud/docker/term" - "index/suffixarray" - "io" - "io/ioutil" - "net/http" - "os" - "os/exec" - "os/signal" - "path/filepath" - "runtime" - "strings" - "sync" - "time" + "github.com/dotcloud/docker/utils" ) -// 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 - 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 -} - -// 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 = "" - 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)" -} - -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 { - 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") - } - r.lastUpdate = r.readProgress - } - // Send newline when complete - if err != nil { - 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, template string) *progressReader { - if template == "" { - template = "%v/%v (%v)" - } - return &progressReader{r, output, size, 0, 0, template} -} - -// 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) -} - -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 -} - -type nopWriter struct { -} - -func (w *nopWriter) Write(buf []byte) (int, error) { - 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 -} - -// 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] -} - -// 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 SetRawTerminal() (*term.State, error) { - oldState, err := term.MakeRaw(int(os.Stdin.Fd())) - if err != nil { - return nil, err - } - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - go func() { - _ = <-c - term.Restore(int(os.Stdin.Fd()), oldState) - os.Exit(0) - }() - return oldState, err -} - -func RestoreTerminal(state *term.State) { - term.Restore(int(os.Stdin.Fd()), state) -} - -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 -} - -// FIXME: this doens't build on Darwin -func GetKernelVersion() (*KernelVersionInfo, error) { - return getKernelVersion() -} - -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) -} - // Compare two Config struct. Do not compare the "Image" nor "Hostname" fields // If OpenStdin is set, then it differs func CompareConfig(a, b *Config) bool { @@ -542,3 +51,7 @@ func CompareConfig(a, b *Config) bool { return true } + +func GetKernelVersion() (*utils.KernelVersionInfo, error) { + return getKernelVersion() +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000000..25c40807c4 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,470 @@ +package utils + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "index/suffixarray" + "io" + "io/ioutil" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "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 + 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 +} + +// 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 = "" + 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)" +} + +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 { + 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") + } + r.lastUpdate = r.readProgress + } + // Send newline when complete + if err != nil { + 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, template string) *progressReader { + if template == "" { + template = "%v/%v (%v)" + } + return &progressReader{r, output, size, 0, 0, template} +} + +// 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) +} + +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 +} + +type nopWriter struct { +} + +func (w *nopWriter) Write(buf []byte) (int, error) { + 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 +} + +// 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] +} + +// 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) +} diff --git a/utils/utils.test b/utils/utils.test new file mode 100755 index 0000000000..4ef427e8fc Binary files /dev/null and b/utils/utils.test differ diff --git a/utils_test.go b/utils/utils_test.go similarity index 99% rename from utils_test.go rename to utils/utils_test.go index aa2a1b9682..4604d716de 100644 --- a/utils_test.go +++ b/utils/utils_test.go @@ -1,4 +1,4 @@ -package docker +package utils import ( "bytes"