mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Create a subpackage for utils
This commit is contained in:
parent
539c876727
commit
2e69e1727b
21 changed files with 632 additions and 613 deletions
11
api.go
11
api.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/shin-/cookiejar"
|
"github.com/shin-/cookiejar"
|
||||||
"io"
|
"io"
|
||||||
|
@ -116,7 +117,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
|
|
||||||
if err := srv.ContainerExport(name, w); err != nil {
|
if err := srv.ContainerExport(name, w); err != nil {
|
||||||
Debugf("%s", err.Error())
|
utils.Debugf("%s", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -239,7 +240,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st
|
||||||
}
|
}
|
||||||
config := &Config{}
|
config := &Config{}
|
||||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
||||||
Debugf("%s", err.Error())
|
utils.Debugf("%s", err.Error())
|
||||||
}
|
}
|
||||||
repo := r.Form.Get("repo")
|
repo := r.Form.Get("repo")
|
||||||
tag := r.Form.Get("tag")
|
tag := r.Form.Get("tag")
|
||||||
|
@ -602,20 +603,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
|
||||||
|
|
||||||
for method, routes := range m {
|
for method, routes := range m {
|
||||||
for route, fct := range routes {
|
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
|
// NOTE: scope issue, make sure the variables are local and won't be changed
|
||||||
localRoute := route
|
localRoute := route
|
||||||
localMethod := method
|
localMethod := method
|
||||||
localFct := fct
|
localFct := fct
|
||||||
r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
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 {
|
if logging {
|
||||||
log.Println(r.Method, r.RequestURI)
|
log.Println(r.Method, r.RequestURI)
|
||||||
}
|
}
|
||||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||||
if len(userAgent) == 2 && userAgent[1] != VERSION {
|
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 {
|
if err := localFct(srv, w, r, mux.Vars(r)); err != nil {
|
||||||
|
|
|
@ -811,7 +811,7 @@ func TestPostContainersCreate(t *testing.T) {
|
||||||
|
|
||||||
if _, err := os.Stat(path.Join(container.rwPath(), "test")); err != nil {
|
if _, err := os.Stat(path.Join(container.rwPath(), "test")); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
Debugf("Err: %s", err)
|
utils.Debugf("Err: %s", err)
|
||||||
t.Fatalf("The test file has not been created")
|
t.Fatalf("The test file has not been created")
|
||||||
}
|
}
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -161,11 +162,11 @@ func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
|
||||||
for c := range containers {
|
for c := range containers {
|
||||||
tmp := builder.runtime.Get(c)
|
tmp := builder.runtime.Get(c)
|
||||||
builder.runtime.Destroy(tmp)
|
builder.runtime.Destroy(tmp)
|
||||||
Debugf("Removing container %s", c)
|
utils.Debugf("Removing container %s", c)
|
||||||
}
|
}
|
||||||
for i := range images {
|
for i := range images {
|
||||||
builder.runtime.graph.Delete(i)
|
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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugf("Env -----> %v ------ %v\n", config.Env, env)
|
utils.Debugf("Env -----> %v ------ %v\n", config.Env, env)
|
||||||
|
|
||||||
// Create the container and start it
|
// Create the container and start it
|
||||||
c, err := builder.Create(config)
|
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], " ")
|
destPath := strings.Trim(tmp[1], " ")
|
||||||
fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId())
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
31
commands.go
31
commands.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
"github.com/dotcloud/docker/term"
|
"github.com/dotcloud/docker/term"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
@ -188,11 +189,11 @@ func CmdLogin(args ...string) error {
|
||||||
return readStringOnRawTerminal(stdin, stdout, false)
|
return readStringOnRawTerminal(stdin, stdout, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldState, err := SetRawTerminal()
|
oldState, err := term.SetRawTerminal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
defer RestoreTerminal(oldState)
|
defer term.RestoreTerminal(oldState)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := Subcmd("login", "", "Register or Login to the docker registry server")
|
cmd := Subcmd("login", "", "Register or Login to the docker registry server")
|
||||||
|
@ -252,7 +253,7 @@ func CmdLogin(args ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if out2.Status != "" {
|
if out2.Status != "" {
|
||||||
RestoreTerminal(oldState)
|
term.RestoreTerminal(oldState)
|
||||||
fmt.Print(out2.Status)
|
fmt.Print(out2.Status)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -303,7 +304,7 @@ func CmdVersion(args ...string) error {
|
||||||
var out ApiVersion
|
var out ApiVersion
|
||||||
err = json.Unmarshal(body, &out)
|
err = json.Unmarshal(body, &out)
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Version:", out.Version)
|
fmt.Println("Version:", out.Version)
|
||||||
|
@ -519,7 +520,7 @@ func CmdHistory(args ...string) error {
|
||||||
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
|
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
|
||||||
|
|
||||||
for _, out := range outs {
|
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()
|
w.Flush()
|
||||||
return nil
|
return nil
|
||||||
|
@ -742,14 +743,14 @@ func CmdImages(args ...string) error {
|
||||||
if *noTrunc {
|
if *noTrunc {
|
||||||
fmt.Fprintf(w, "%s\t", out.Id)
|
fmt.Fprintf(w, "%s\t", out.Id)
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
if *noTrunc {
|
if *noTrunc {
|
||||||
fmt.Fprintln(w, out.Id)
|
fmt.Fprintln(w, out.Id)
|
||||||
} else {
|
} 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 {
|
for _, out := range outs {
|
||||||
if !*quiet {
|
if !*quiet {
|
||||||
if *noTrunc {
|
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 {
|
} 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 {
|
} else {
|
||||||
if *noTrunc {
|
if *noTrunc {
|
||||||
fmt.Fprintln(w, out.Id)
|
fmt.Fprintln(w, out.Id)
|
||||||
} else {
|
} 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()
|
rwc, br := clientconn.Hijack()
|
||||||
defer rwc.Close()
|
defer rwc.Close()
|
||||||
|
|
||||||
receiveStdout := Go(func() error {
|
receiveStdout := utils.Go(func() error {
|
||||||
_, err := io.Copy(os.Stdout, br)
|
_, err := io.Copy(os.Stdout, br)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
|
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
|
return err
|
||||||
} else {
|
} else {
|
||||||
defer RestoreTerminal(oldState)
|
defer term.RestoreTerminal(oldState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendStdin := Go(func() error {
|
sendStdin := utils.Go(func() error {
|
||||||
_, err := io.Copy(rwc, os.Stdin)
|
_, err := io.Copy(rwc, os.Stdin)
|
||||||
if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
|
if err := rwc.(*net.TCPConn).CloseWrite(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err)
|
fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err)
|
||||||
|
|
71
container.go
71
container.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -39,8 +40,8 @@ type Container struct {
|
||||||
ResolvConfPath string
|
ResolvConfPath string
|
||||||
|
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
stdout *writeBroadcaster
|
stdout *utils.WriteBroadcaster
|
||||||
stderr *writeBroadcaster
|
stderr *utils.WriteBroadcaster
|
||||||
stdin io.ReadCloser
|
stdin io.ReadCloser
|
||||||
stdinPipe io.WriteCloser
|
stdinPipe io.WriteCloser
|
||||||
ptyMaster io.Closer
|
ptyMaster io.Closer
|
||||||
|
@ -251,9 +252,9 @@ func (container *Container) startPty() error {
|
||||||
// Copy the PTYs to our broadcasters
|
// Copy the PTYs to our broadcasters
|
||||||
go func() {
|
go func() {
|
||||||
defer container.stdout.CloseWriters()
|
defer container.stdout.CloseWriters()
|
||||||
Debugf("[startPty] Begin of stdout pipe")
|
utils.Debugf("[startPty] Begin of stdout pipe")
|
||||||
io.Copy(container.stdout, ptyMaster)
|
io.Copy(container.stdout, ptyMaster)
|
||||||
Debugf("[startPty] End of stdout pipe")
|
utils.Debugf("[startPty] End of stdout pipe")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// stdin
|
// stdin
|
||||||
|
@ -262,9 +263,9 @@ func (container *Container) startPty() error {
|
||||||
container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
|
container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
|
||||||
go func() {
|
go func() {
|
||||||
defer container.stdin.Close()
|
defer container.stdin.Close()
|
||||||
Debugf("[startPty] Begin of stdin pipe")
|
utils.Debugf("[startPty] Begin of stdin pipe")
|
||||||
io.Copy(ptyMaster, container.stdin)
|
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 {
|
if err := container.cmd.Start(); err != nil {
|
||||||
|
@ -284,9 +285,9 @@ func (container *Container) start() error {
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
defer stdin.Close()
|
defer stdin.Close()
|
||||||
Debugf("Begin of stdin pipe [start]")
|
utils.Debugf("Begin of stdin pipe [start]")
|
||||||
io.Copy(stdin, container.stdin)
|
io.Copy(stdin, container.stdin)
|
||||||
Debugf("End of stdin pipe [start]")
|
utils.Debugf("End of stdin pipe [start]")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return container.cmd.Start()
|
return container.cmd.Start()
|
||||||
|
@ -303,8 +304,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||||
errors <- err
|
errors <- err
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
Debugf("[start] attach stdin\n")
|
utils.Debugf("[start] attach stdin\n")
|
||||||
defer Debugf("[end] attach stdin\n")
|
defer utils.Debugf("[end] attach stdin\n")
|
||||||
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
|
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
|
||||||
if cStdout != nil {
|
if cStdout != nil {
|
||||||
defer cStdout.Close()
|
defer cStdout.Close()
|
||||||
|
@ -316,12 +317,12 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||||
defer cStdin.Close()
|
defer cStdin.Close()
|
||||||
}
|
}
|
||||||
if container.Config.Tty {
|
if container.Config.Tty {
|
||||||
_, err = CopyEscapable(cStdin, stdin)
|
_, err = utils.CopyEscapable(cStdin, stdin)
|
||||||
} else {
|
} else {
|
||||||
_, err = io.Copy(cStdin, stdin)
|
_, err = io.Copy(cStdin, stdin)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf("[error] attach stdin: %s\n", err)
|
utils.Debugf("[error] attach stdin: %s\n", err)
|
||||||
}
|
}
|
||||||
// Discard error, expecting pipe error
|
// Discard error, expecting pipe error
|
||||||
errors <- nil
|
errors <- nil
|
||||||
|
@ -335,8 +336,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||||
} else {
|
} else {
|
||||||
cStdout = p
|
cStdout = p
|
||||||
go func() {
|
go func() {
|
||||||
Debugf("[start] attach stdout\n")
|
utils.Debugf("[start] attach stdout\n")
|
||||||
defer Debugf("[end] attach stdout\n")
|
defer utils.Debugf("[end] attach stdout\n")
|
||||||
// If we are in StdinOnce mode, then close stdin
|
// If we are in StdinOnce mode, then close stdin
|
||||||
if container.Config.StdinOnce {
|
if container.Config.StdinOnce {
|
||||||
if stdin != nil {
|
if stdin != nil {
|
||||||
|
@ -348,7 +349,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||||
}
|
}
|
||||||
_, err := io.Copy(stdout, cStdout)
|
_, err := io.Copy(stdout, cStdout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf("[error] attach stdout: %s\n", err)
|
utils.Debugf("[error] attach stdout: %s\n", err)
|
||||||
}
|
}
|
||||||
errors <- err
|
errors <- err
|
||||||
}()
|
}()
|
||||||
|
@ -361,8 +362,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||||
} else {
|
} else {
|
||||||
cStderr = p
|
cStderr = p
|
||||||
go func() {
|
go func() {
|
||||||
Debugf("[start] attach stderr\n")
|
utils.Debugf("[start] attach stderr\n")
|
||||||
defer Debugf("[end] attach stderr\n")
|
defer utils.Debugf("[end] attach stderr\n")
|
||||||
// If we are in StdinOnce mode, then close stdin
|
// If we are in StdinOnce mode, then close stdin
|
||||||
if container.Config.StdinOnce {
|
if container.Config.StdinOnce {
|
||||||
if stdin != nil {
|
if stdin != nil {
|
||||||
|
@ -374,13 +375,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
|
||||||
}
|
}
|
||||||
_, err := io.Copy(stderr, cStderr)
|
_, err := io.Copy(stderr, cStderr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf("[error] attach stderr: %s\n", err)
|
utils.Debugf("[error] attach stderr: %s\n", err)
|
||||||
}
|
}
|
||||||
errors <- err
|
errors <- err
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Go(func() error {
|
return utils.Go(func() error {
|
||||||
if cStdout != nil {
|
if cStdout != nil {
|
||||||
defer cStdout.Close()
|
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
|
// FIXME: how do clean up the stdin goroutine without the unwanted side effect
|
||||||
// of closing the passed stdin? Add an intermediary io.Pipe?
|
// of closing the passed stdin? Add an intermediary io.Pipe?
|
||||||
for i := 0; i < nJobs; i += 1 {
|
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 {
|
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
|
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
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -555,13 +556,13 @@ func (container *Container) StdinPipe() (io.WriteCloser, error) {
|
||||||
func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
container.stdout.AddWriter(writer)
|
container.stdout.AddWriter(writer)
|
||||||
return newBufReader(reader), nil
|
return utils.NewBufReader(reader), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
container.stderr.AddWriter(writer)
|
container.stderr.AddWriter(writer)
|
||||||
return newBufReader(reader), nil
|
return utils.NewBufReader(reader), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) allocateNetwork() error {
|
func (container *Container) allocateNetwork() error {
|
||||||
|
@ -609,20 +610,20 @@ func (container *Container) waitLxc() error {
|
||||||
|
|
||||||
func (container *Container) monitor() {
|
func (container *Container) monitor() {
|
||||||
// Wait for the program to exit
|
// 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 the command does not exists, try to wait via lxc
|
||||||
if container.cmd == nil {
|
if container.cmd == nil {
|
||||||
if err := container.waitLxc(); err != nil {
|
if err := container.waitLxc(); err != nil {
|
||||||
Debugf("%s: Process: %s", container.Id, err)
|
utils.Debugf("%s: Process: %s", container.Id, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := container.cmd.Wait(); err != nil {
|
if err := container.cmd.Wait(); err != nil {
|
||||||
// Discard the error as any signals or non 0 returns will generate an error
|
// 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
|
var exitCode int = -1
|
||||||
if container.cmd != nil {
|
if container.cmd != nil {
|
||||||
|
@ -633,19 +634,19 @@ func (container *Container) monitor() {
|
||||||
container.releaseNetwork()
|
container.releaseNetwork()
|
||||||
if container.Config.OpenStdin {
|
if container.Config.OpenStdin {
|
||||||
if err := container.stdin.Close(); err != nil {
|
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 {
|
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 {
|
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 container.ptyMaster != nil {
|
||||||
if err := container.ptyMaster.Close(); err != 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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return HashData(rwData)
|
return utils.HashData(rwData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) Export() (Archive, error) {
|
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
|
// 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.
|
// will need to use a langer prefix, or the full-length container Id.
|
||||||
func (container *Container) ShortId() string {
|
func (container *Container) ShortId() string {
|
||||||
return TruncateId(container.Id)
|
return utils.TruncateId(container.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) logPath(name string) string {
|
func (container *Container) logPath(name string) string {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker"
|
"github.com/dotcloud/docker"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,7 +18,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if docker.SelfPath() == "/sbin/init" {
|
if utils.SelfPath() == "/sbin/init" {
|
||||||
// Running in init mode
|
// Running in init mode
|
||||||
docker.SysInit()
|
docker.SysInit()
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,8 +2,9 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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")
|
return nil, fmt.Errorf("Kernel version detection is not available on darwin")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,14 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getKernelVersion() (*KernelVersionInfo, error) {
|
// FIXME: Move this to utils package
|
||||||
|
func getKernelVersion() (*utils.KernelVersionInfo, error) {
|
||||||
var (
|
var (
|
||||||
uts syscall.Utsname
|
uts syscall.Utsname
|
||||||
flavor string
|
flavor string
|
||||||
|
@ -60,7 +62,7 @@ func getKernelVersion() (*KernelVersionInfo, error) {
|
||||||
flavor = ""
|
flavor = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return &KernelVersionInfo{
|
return &utils.KernelVersionInfo{
|
||||||
Kernel: kernel,
|
Kernel: kernel,
|
||||||
Major: major,
|
Major: major,
|
||||||
Minor: minor,
|
Minor: minor,
|
||||||
|
|
7
graph.go
7
graph.go
|
@ -3,6 +3,7 @@ package docker
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -17,7 +18,7 @@ import (
|
||||||
// A Graph is a store for versioned filesystem images and the relationship between them.
|
// A Graph is a store for versioned filesystem images and the relationship between them.
|
||||||
type Graph struct {
|
type Graph struct {
|
||||||
Root string
|
Root string
|
||||||
idIndex *TruncIndex
|
idIndex *utils.TruncIndex
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
checksumLock map[string]*sync.Mutex
|
checksumLock map[string]*sync.Mutex
|
||||||
lockSumFile *sync.Mutex
|
lockSumFile *sync.Mutex
|
||||||
|
@ -37,7 +38,7 @@ func NewGraph(root string) (*Graph, error) {
|
||||||
}
|
}
|
||||||
graph := &Graph{
|
graph := &Graph{
|
||||||
Root: abspath,
|
Root: abspath,
|
||||||
idIndex: NewTruncIndex(),
|
idIndex: utils.NewTruncIndex(),
|
||||||
checksumLock: make(map[string]*sync.Mutex),
|
checksumLock: make(map[string]*sync.Mutex),
|
||||||
lockSumFile: &sync.Mutex{},
|
lockSumFile: &sync.Mutex{},
|
||||||
lockSumMap: &sync.Mutex{},
|
lockSumMap: &sync.Mutex{},
|
||||||
|
@ -165,7 +166,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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.
|
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||||
|
|
3
image.go
3
image.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -180,7 +181,7 @@ func (image *Image) Changes(rw string) ([]Change, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (image *Image) ShortId() string {
|
func (image *Image) ShortId() string {
|
||||||
return TruncateId(image.Id)
|
return utils.TruncateId(image.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateId(id string) error {
|
func ValidateId(id string) error {
|
||||||
|
|
21
network.go
21
network.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -97,7 +98,7 @@ func checkRouteOverlaps(dockerNetwork *net.IPNet) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
Debugf("Routes:\n\n%s", output)
|
utils.Debugf("Routes:\n\n%s", output)
|
||||||
for _, line := range strings.Split(output, "\n") {
|
for _, line := range strings.Split(output, "\n") {
|
||||||
if strings.Trim(line, "\r\n\t ") == "" || strings.Contains(line, "default") {
|
if strings.Trim(line, "\r\n\t ") == "" || strings.Contains(line, "default") {
|
||||||
continue
|
continue
|
||||||
|
@ -126,13 +127,13 @@ func CreateBridgeIface(ifaceName string) error {
|
||||||
ifaceAddr = addr
|
ifaceAddr = addr
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
Debugf("%s: %s", addr, err)
|
utils.Debugf("%s: %s", addr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ifaceAddr == "" {
|
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)
|
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 {
|
} 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 {
|
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
|
// proxy listens for socket connections on `listener`, and forwards them unmodified
|
||||||
// to `proto:address`
|
// to `proto:address`
|
||||||
func proxy(listener net.Listener, proto, address string) error {
|
func proxy(listener net.Listener, proto, address string) error {
|
||||||
Debugf("proxying to %s:%s", proto, address)
|
utils.Debugf("proxying to %s:%s", proto, address)
|
||||||
defer Debugf("Done proxying to %s:%s", proto, address)
|
defer utils.Debugf("Done proxying to %s:%s", proto, address)
|
||||||
for {
|
for {
|
||||||
Debugf("Listening on %s", listener)
|
utils.Debugf("Listening on %s", listener)
|
||||||
src, err := listener.Accept()
|
src, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
Debugf("Connecting to %s:%s", proto, address)
|
utils.Debugf("Connecting to %s:%s", proto, address)
|
||||||
dst, err := net.Dial(proto, address)
|
dst, err := net.Dial(proto, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error connecting to %s:%s: %s", proto, address, err)
|
log.Printf("Error connecting to %s:%s: %s", proto, address, err)
|
||||||
src.Close()
|
src.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Debugf("Connected to backend, splicing")
|
utils.Debugf("Connected to backend, splicing")
|
||||||
splice(src, dst)
|
splice(src, dst)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -317,7 +318,7 @@ func (alloc *PortAllocator) runFountain() {
|
||||||
|
|
||||||
// FIXME: Release can no longer fail, change its prototype to reflect that.
|
// FIXME: Release can no longer fail, change its prototype to reflect that.
|
||||||
func (alloc *PortAllocator) Release(port int) error {
|
func (alloc *PortAllocator) Release(port int) error {
|
||||||
Debugf("Releasing %d", port)
|
utils.Debugf("Releasing %d", port)
|
||||||
alloc.lock.Lock()
|
alloc.lock.Lock()
|
||||||
delete(alloc.inUse, port)
|
delete(alloc.inUse, port)
|
||||||
alloc.lock.Unlock()
|
alloc.lock.Unlock()
|
||||||
|
@ -325,7 +326,7 @@ func (alloc *PortAllocator) Release(port int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alloc *PortAllocator) Acquire(port int) (int, error) {
|
func (alloc *PortAllocator) Acquire(port int) (int, error) {
|
||||||
Debugf("Acquiring %d", port)
|
utils.Debugf("Acquiring %d", port)
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
// Allocate a port from the fountain
|
// Allocate a port from the fountain
|
||||||
for port := range alloc.fountain {
|
for port := range alloc.fountain {
|
||||||
|
|
24
registry.go
24
registry.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"github.com/shin-/cookiejar"
|
"github.com/shin-/cookiejar"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -19,7 +20,7 @@ import (
|
||||||
func NewImgJson(src []byte) (*Image, error) {
|
func NewImgJson(src []byte) (*Image, error) {
|
||||||
ret := &Image{}
|
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?
|
// FIXME: Is there a cleaner way to "purify" the input json?
|
||||||
if err := json.Unmarshal(src, ret); err != nil {
|
if err := json.Unmarshal(src, ret); err != nil {
|
||||||
return nil, err
|
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)
|
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)
|
history := new([]string)
|
||||||
if err := json.Unmarshal(jsonString, history); err != nil {
|
if err := json.Unmarshal(jsonString, history); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -116,7 +117,7 @@ func (graph *Graph) getImagesInRepository(repository string, authConfig *auth.Au
|
||||||
|
|
||||||
err = json.Unmarshal(jsonData, &imageList)
|
err = json.Unmarshal(jsonData, &imageList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf("Body: %s (%s)\n", res.Body, u)
|
utils.Debugf("Body: %s (%s)\n", res.Body, u)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +167,7 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, tok
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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) {
|
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, ", "))
|
req.Header.Set("Authorization", "Token "+strings.Join(token, ", "))
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
defer res.Body.Close()
|
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) {
|
if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) {
|
||||||
continue
|
continue
|
||||||
} else if res.StatusCode == 404 {
|
} 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)
|
return fmt.Errorf("Error while retrieving checksum for %s: %v", img.Id, err)
|
||||||
}
|
}
|
||||||
req.Header.Set("X-Docker-Checksum", checksum)
|
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)
|
res, err := doWithCookies(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to upload metadata: %s", err)
|
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()}
|
layerData = &TempArchive{file, st.Size()}
|
||||||
}
|
}
|
||||||
|
|
||||||
req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer",
|
req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer", utils.ProgressReader(layerData, int(layerData.Size), stdout, ""))
|
||||||
ProgressReader(layerData, int(layerData.Size), stdout, ""))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ func (graph *Graph) pushTag(remote, revision, tag, registry string, token []stri
|
||||||
revision = "\"" + revision + "\""
|
revision = "\"" + revision + "\""
|
||||||
registry = "https://" + registry + "/v1"
|
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()
|
client := graph.getHttpClient()
|
||||||
req, err := http.NewRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision))
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugf("json sent: %s\n", imgListJson)
|
utils.Debugf("json sent: %s\n", imgListJson)
|
||||||
|
|
||||||
fmt.Fprintf(stdout, "Sending image list\n")
|
fmt.Fprintf(stdout, "Sending image list\n")
|
||||||
req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/", bytes.NewReader(imgListJson))
|
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()
|
defer res.Body.Close()
|
||||||
|
|
||||||
for res.StatusCode >= 300 && res.StatusCode < 400 {
|
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))
|
req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -669,7 +669,7 @@ func (graph *Graph) PushRepository(stdout io.Writer, remote string, localRepo Re
|
||||||
var token, endpoints []string
|
var token, endpoints []string
|
||||||
if res.Header.Get("X-Docker-Token") != "" {
|
if res.Header.Get("X-Docker-Token") != "" {
|
||||||
token = res.Header["X-Docker-Token"]
|
token = res.Header["X-Docker-Token"]
|
||||||
Debugf("Auth token: %v", token)
|
utils.Debugf("Auth token: %v", token)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Index response didn't contain an access token")
|
return fmt.Errorf("Index response didn't contain an access token")
|
||||||
}
|
}
|
||||||
|
|
31
runtime.go
31
runtime.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -27,9 +28,9 @@ type Runtime struct {
|
||||||
graph *Graph
|
graph *Graph
|
||||||
repositories *TagStore
|
repositories *TagStore
|
||||||
authConfig *auth.AuthConfig
|
authConfig *auth.AuthConfig
|
||||||
idIndex *TruncIndex
|
idIndex *utils.TruncIndex
|
||||||
capabilities *Capabilities
|
capabilities *Capabilities
|
||||||
kernelVersion *KernelVersionInfo
|
kernelVersion *utils.KernelVersionInfo
|
||||||
autoRestart bool
|
autoRestart bool
|
||||||
volumes *Graph
|
volumes *Graph
|
||||||
}
|
}
|
||||||
|
@ -37,7 +38,7 @@ type Runtime struct {
|
||||||
var sysInitPath string
|
var sysInitPath string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
sysInitPath = SelfPath()
|
sysInitPath = utils.SelfPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) List() []*Container {
|
func (runtime *Runtime) List() []*Container {
|
||||||
|
@ -113,13 +114,13 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
container.runtime = runtime
|
container.runtime = runtime
|
||||||
|
|
||||||
// Attach to stdout and stderr
|
// Attach to stdout and stderr
|
||||||
container.stderr = newWriteBroadcaster()
|
container.stderr = utils.NewWriteBroadcaster()
|
||||||
container.stdout = newWriteBroadcaster()
|
container.stdout = utils.NewWriteBroadcaster()
|
||||||
// Attach to stdin
|
// Attach to stdin
|
||||||
if container.Config.OpenStdin {
|
if container.Config.OpenStdin {
|
||||||
container.stdin, container.stdinPipe = io.Pipe()
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
} else {
|
} else {
|
||||||
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||||
}
|
}
|
||||||
// done
|
// done
|
||||||
runtime.containers.PushBack(container)
|
runtime.containers.PushBack(container)
|
||||||
|
@ -137,9 +138,9 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if !strings.Contains(string(output), "RUNNING") {
|
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 {
|
if runtime.autoRestart {
|
||||||
Debugf("Restarting")
|
utils.Debugf("Restarting")
|
||||||
container.State.Ghost = false
|
container.State.Ghost = false
|
||||||
container.State.setStopped(0)
|
container.State.setStopped(0)
|
||||||
if err := container.Start(); err != nil {
|
if err := container.Start(); err != nil {
|
||||||
|
@ -147,7 +148,7 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
}
|
}
|
||||||
nomonitor = true
|
nomonitor = true
|
||||||
} else {
|
} else {
|
||||||
Debugf("Marking as stopped")
|
utils.Debugf("Marking as stopped")
|
||||||
container.State.setStopped(-127)
|
container.State.setStopped(-127)
|
||||||
if err := container.ToDisk(); err != nil {
|
if err := container.ToDisk(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -168,7 +169,7 @@ func (runtime *Runtime) Register(container *Container) error {
|
||||||
return nil
|
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)
|
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -215,16 +216,16 @@ func (runtime *Runtime) restore() error {
|
||||||
id := v.Name()
|
id := v.Name()
|
||||||
container, err := runtime.Load(id)
|
container, err := runtime.Load(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf("Failed to load container %v: %v", id, err)
|
utils.Debugf("Failed to load container %v: %v", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Debugf("Loaded container %v", container.Id)
|
utils.Debugf("Loaded container %v", container.Id)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) UpdateCapabilities(quiet bool) {
|
func (runtime *Runtime) UpdateCapabilities(quiet bool) {
|
||||||
if cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory"); err != nil {
|
if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil {
|
||||||
if !quiet {
|
if !quiet {
|
||||||
log.Printf("WARNING: %s\n", err)
|
log.Printf("WARNING: %s\n", err)
|
||||||
}
|
}
|
||||||
|
@ -255,7 +256,7 @@ func NewRuntime(autoRestart bool) (*Runtime, error) {
|
||||||
log.Printf("WARNING: %s\n", err)
|
log.Printf("WARNING: %s\n", err)
|
||||||
} else {
|
} else {
|
||||||
runtime.kernelVersion = k
|
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())
|
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,
|
graph: g,
|
||||||
repositories: repositories,
|
repositories: repositories,
|
||||||
authConfig: authConfig,
|
authConfig: authConfig,
|
||||||
idIndex: NewTruncIndex(),
|
idIndex: utils.NewTruncIndex(),
|
||||||
capabilities: &Capabilities{},
|
capabilities: &Capabilities{},
|
||||||
autoRestart: autoRestart,
|
autoRestart: autoRestart,
|
||||||
volumes: volumes,
|
volumes: volumes,
|
||||||
|
|
27
server.go
27
server.go
|
@ -2,6 +2,7 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -54,7 +55,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) {
|
||||||
var out ApiSearch
|
var out ApiSearch
|
||||||
out.Description = repo["description"]
|
out.Description = repo["description"]
|
||||||
if len(out.Description) > 45 {
|
if len(out.Description) > 45 {
|
||||||
out.Description = Trunc(out.Description, 42) + "..."
|
out.Description = utils.Trunc(out.Description, 42) + "..."
|
||||||
}
|
}
|
||||||
out.Name = repo["name"]
|
out.Name = repo["name"]
|
||||||
outs = append(outs, out)
|
outs = append(outs, out)
|
||||||
|
@ -68,7 +69,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := Download(url, out)
|
file, err := utils.Download(url, out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error {
|
||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
// FIXME: Handle custom repo, tag comment, author
|
// 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 name, repository := range srv.runtime.repositories.Repositories {
|
||||||
for tag, id := range repository {
|
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()
|
out.GoVersion = runtime.Version()
|
||||||
if os.Getenv("DEBUG") != "" {
|
if os.Getenv("DEBUG") != "" {
|
||||||
out.Debug = true
|
out.Debug = true
|
||||||
out.NFd = getTotalUsedFds()
|
out.NFd = utils.GetTotalUsedFds()
|
||||||
out.NGoroutines = runtime.NumGoroutine()
|
out.NGoroutines = runtime.NumGoroutine()
|
||||||
}
|
}
|
||||||
return out
|
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 {
|
func (srv *Server) ImagePush(name, registry string, out io.Writer) error {
|
||||||
img, err := srv.runtime.graph.Get(name)
|
img, err := srv.runtime.graph.Get(name)
|
||||||
if err != nil {
|
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 it fails, try to get the repository
|
||||||
if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
|
if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
|
||||||
if err := srv.runtime.graph.PushRepository(out, name, localRepo, srv.runtime.authConfig); err != nil {
|
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)
|
fmt.Fprintln(out, "Downloading from", u)
|
||||||
// Download with curl (pretty progress bar)
|
// Download with curl (pretty progress bar)
|
||||||
// If curl is not available, fallback to http.Get()
|
// 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 {
|
if err != nil {
|
||||||
return err
|
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)
|
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -486,17 +487,17 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
|
||||||
if stdout {
|
if stdout {
|
||||||
cLog, err := container.ReadLog("stdout")
|
cLog, err := container.ReadLog("stdout")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf(err.Error())
|
utils.Debugf(err.Error())
|
||||||
} else if _, err := io.Copy(out, cLog); err != nil {
|
} else if _, err := io.Copy(out, cLog); err != nil {
|
||||||
Debugf(err.Error())
|
utils.Debugf(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if stderr {
|
if stderr {
|
||||||
cLog, err := container.ReadLog("stderr")
|
cLog, err := container.ReadLog("stderr")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Debugf(err.Error())
|
utils.Debugf(err.Error())
|
||||||
} else if _, err := io.Copy(out, cLog); err != nil {
|
} 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()
|
r, w := io.Pipe()
|
||||||
go func() {
|
go func() {
|
||||||
defer w.Close()
|
defer w.Close()
|
||||||
defer Debugf("Closing buffered stdin pipe")
|
defer utils.Debugf("Closing buffered stdin pipe")
|
||||||
io.Copy(w, in)
|
io.Copy(w, in)
|
||||||
}()
|
}()
|
||||||
cStdin = r
|
cStdin = r
|
||||||
|
|
3
state.go
3
state.go
|
@ -2,6 +2,7 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -21,7 +22,7 @@ func (s *State) String() string {
|
||||||
if s.Ghost {
|
if s.Ghost {
|
||||||
return fmt.Sprintf("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)
|
return fmt.Sprintf("Exit %d", s.ExitCode)
|
||||||
}
|
}
|
||||||
|
|
3
tags.go
3
tags.go
|
@ -3,6 +3,7 @@ package docker
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -106,7 +107,7 @@ func (store *TagStore) ImageName(id string) string {
|
||||||
if names, exists := store.ById()[id]; exists && len(names) > 0 {
|
if names, exists := store.ById()[id]; exists && len(names) > 0 {
|
||||||
return names[0]
|
return names[0]
|
||||||
}
|
}
|
||||||
return TruncateId(id)
|
return utils.TruncateId(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|
func (store *TagStore) Set(repoName, tag, imageName string, force bool) error {
|
||||||
|
|
21
term/term.go
21
term/term.go
|
@ -1,6 +1,8 @@
|
||||||
package term
|
package term
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"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)
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
|
||||||
return err
|
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)
|
||||||
|
}
|
||||||
|
|
497
utils.go
497
utils.go
|
@ -1,500 +1,9 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"github.com/dotcloud/docker/utils"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 = "<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)"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
|
||||||
// If OpenStdin is set, then it differs
|
// If OpenStdin is set, then it differs
|
||||||
func CompareConfig(a, b *Config) bool {
|
func CompareConfig(a, b *Config) bool {
|
||||||
|
@ -542,3 +51,7 @@ func CompareConfig(a, b *Config) bool {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetKernelVersion() (*utils.KernelVersionInfo, error) {
|
||||||
|
return getKernelVersion()
|
||||||
|
}
|
||||||
|
|
470
utils/utils.go
Normal file
470
utils/utils.go
Normal file
|
@ -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 = "<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)"
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
BIN
utils/utils.test
Executable file
BIN
utils/utils.test
Executable file
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
package docker
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
Loading…
Reference in a new issue