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"
 | 
			
		||||
	"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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								commands.go
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										71
									
								
								container.go
									
										
									
									
									
								
							
							
						
						
									
										71
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										7
									
								
								graph.go
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								image.go
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								network.go
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								registry.go
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								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")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								runtime.go
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										27
									
								
								server.go
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								state.go
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								tags.go
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								term/term.go
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								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)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										497
									
								
								utils.go
									
										
									
									
									
								
							
							
						
						
									
										497
									
								
								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 = "<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
 | 
			
		||||
// 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()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 (
 | 
			
		||||
	"bytes"
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue