... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"fmt" |
6 | 6 |
"github.com/dotcloud/docker/auth" |
7 |
+ "github.com/dotcloud/docker/utils" |
|
7 | 8 |
"github.com/gorilla/mux" |
8 | 9 |
"github.com/shin-/cookiejar" |
9 | 10 |
"io" |
... | ... |
@@ -116,7 +117,7 @@ func getContainersExport(srv *Server, w http.ResponseWriter, r *http.Request, va |
116 | 116 |
name := vars["name"] |
117 | 117 |
|
118 | 118 |
if err := srv.ContainerExport(name, w); err != nil { |
119 |
- Debugf("%s", err.Error()) |
|
119 |
+ utils.Debugf("%s", err.Error()) |
|
120 | 120 |
return err |
121 | 121 |
} |
122 | 122 |
return nil |
... | ... |
@@ -239,7 +240,7 @@ func postCommit(srv *Server, w http.ResponseWriter, r *http.Request, vars map[st |
239 | 239 |
} |
240 | 240 |
config := &Config{} |
241 | 241 |
if err := json.NewDecoder(r.Body).Decode(config); err != nil { |
242 |
- Debugf("%s", err.Error()) |
|
242 |
+ utils.Debugf("%s", err.Error()) |
|
243 | 243 |
} |
244 | 244 |
repo := r.Form.Get("repo") |
245 | 245 |
tag := r.Form.Get("tag") |
... | ... |
@@ -602,20 +603,20 @@ func ListenAndServe(addr string, srv *Server, logging bool) error { |
602 | 602 |
|
603 | 603 |
for method, routes := range m { |
604 | 604 |
for route, fct := range routes { |
605 |
- Debugf("Registering %s, %s", method, route) |
|
605 |
+ utils.Debugf("Registering %s, %s", method, route) |
|
606 | 606 |
// NOTE: scope issue, make sure the variables are local and won't be changed |
607 | 607 |
localRoute := route |
608 | 608 |
localMethod := method |
609 | 609 |
localFct := fct |
610 | 610 |
r.Path(localRoute).Methods(localMethod).HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
611 |
- Debugf("Calling %s %s", localMethod, localRoute) |
|
611 |
+ utils.Debugf("Calling %s %s", localMethod, localRoute) |
|
612 | 612 |
if logging { |
613 | 613 |
log.Println(r.Method, r.RequestURI) |
614 | 614 |
} |
615 | 615 |
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { |
616 | 616 |
userAgent := strings.Split(r.Header.Get("User-Agent"), "/") |
617 | 617 |
if len(userAgent) == 2 && userAgent[1] != VERSION { |
618 |
- Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION) |
|
618 |
+ utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION) |
|
619 | 619 |
} |
620 | 620 |
} |
621 | 621 |
if err := localFct(srv, w, r, mux.Vars(r)); err != nil { |
... | ... |
@@ -811,7 +811,7 @@ func TestPostContainersCreate(t *testing.T) { |
811 | 811 |
|
812 | 812 |
if _, err := os.Stat(path.Join(container.rwPath(), "test")); err != nil { |
813 | 813 |
if os.IsNotExist(err) { |
814 |
- Debugf("Err: %s", err) |
|
814 |
+ utils.Debugf("Err: %s", err) |
|
815 | 815 |
t.Fatalf("The test file has not been created") |
816 | 816 |
} |
817 | 817 |
t.Fatal(err) |
... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"bufio" |
5 | 5 |
"encoding/json" |
6 | 6 |
"fmt" |
7 |
+ "github.com/dotcloud/docker/utils" |
|
7 | 8 |
"io" |
8 | 9 |
"os" |
9 | 10 |
"path" |
... | ... |
@@ -161,11 +162,11 @@ func (builder *Builder) clearTmp(containers, images map[string]struct{}) { |
161 | 161 |
for c := range containers { |
162 | 162 |
tmp := builder.runtime.Get(c) |
163 | 163 |
builder.runtime.Destroy(tmp) |
164 |
- Debugf("Removing container %s", c) |
|
164 |
+ utils.Debugf("Removing container %s", c) |
|
165 | 165 |
} |
166 | 166 |
for i := range images { |
167 | 167 |
builder.runtime.graph.Delete(i) |
168 |
- Debugf("Removing image %s", i) |
|
168 |
+ utils.Debugf("Removing image %s", i) |
|
169 | 169 |
} |
170 | 170 |
} |
171 | 171 |
|
... | ... |
@@ -286,7 +287,7 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e |
286 | 286 |
break |
287 | 287 |
} |
288 | 288 |
|
289 |
- Debugf("Env -----> %v ------ %v\n", config.Env, env) |
|
289 |
+ utils.Debugf("Env -----> %v ------ %v\n", config.Env, env) |
|
290 | 290 |
|
291 | 291 |
// Create the container and start it |
292 | 292 |
c, err := builder.Create(config) |
... | ... |
@@ -410,7 +411,7 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e |
410 | 410 |
destPath := strings.Trim(tmp[1], " ") |
411 | 411 |
fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId()) |
412 | 412 |
|
413 |
- file, err := Download(sourceUrl, stdout) |
|
413 |
+ file, err := utils.Download(sourceUrl, stdout) |
|
414 | 414 |
if err != nil { |
415 | 415 |
return nil, err |
416 | 416 |
} |
... | ... |
@@ -7,6 +7,7 @@ import ( |
7 | 7 |
"fmt" |
8 | 8 |
"github.com/dotcloud/docker/auth" |
9 | 9 |
"github.com/dotcloud/docker/term" |
10 |
+ "github.com/dotcloud/docker/utils" |
|
10 | 11 |
"io" |
11 | 12 |
"io/ioutil" |
12 | 13 |
"net" |
... | ... |
@@ -188,11 +189,11 @@ func CmdLogin(args ...string) error { |
188 | 188 |
return readStringOnRawTerminal(stdin, stdout, false) |
189 | 189 |
} |
190 | 190 |
|
191 |
- oldState, err := SetRawTerminal() |
|
191 |
+ oldState, err := term.SetRawTerminal() |
|
192 | 192 |
if err != nil { |
193 | 193 |
return err |
194 | 194 |
} else { |
195 |
- defer RestoreTerminal(oldState) |
|
195 |
+ defer term.RestoreTerminal(oldState) |
|
196 | 196 |
} |
197 | 197 |
|
198 | 198 |
cmd := Subcmd("login", "", "Register or Login to the docker registry server") |
... | ... |
@@ -252,7 +253,7 @@ func CmdLogin(args ...string) error { |
252 | 252 |
return err |
253 | 253 |
} |
254 | 254 |
if out2.Status != "" { |
255 |
- RestoreTerminal(oldState) |
|
255 |
+ term.RestoreTerminal(oldState) |
|
256 | 256 |
fmt.Print(out2.Status) |
257 | 257 |
} |
258 | 258 |
return nil |
... | ... |
@@ -303,7 +304,7 @@ func CmdVersion(args ...string) error { |
303 | 303 |
var out ApiVersion |
304 | 304 |
err = json.Unmarshal(body, &out) |
305 | 305 |
if err != nil { |
306 |
- Debugf("Error unmarshal: body: %s, err: %s\n", body, err) |
|
306 |
+ utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err) |
|
307 | 307 |
return err |
308 | 308 |
} |
309 | 309 |
fmt.Println("Version:", out.Version) |
... | ... |
@@ -519,7 +520,7 @@ func CmdHistory(args ...string) error { |
519 | 519 |
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY") |
520 | 520 |
|
521 | 521 |
for _, out := range outs { |
522 |
- fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) |
|
522 |
+ fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) |
|
523 | 523 |
} |
524 | 524 |
w.Flush() |
525 | 525 |
return nil |
... | ... |
@@ -742,14 +743,14 @@ func CmdImages(args ...string) error { |
742 | 742 |
if *noTrunc { |
743 | 743 |
fmt.Fprintf(w, "%s\t", out.Id) |
744 | 744 |
} else { |
745 |
- fmt.Fprintf(w, "%s\t", TruncateId(out.Id)) |
|
745 |
+ fmt.Fprintf(w, "%s\t", utils.TruncateId(out.Id)) |
|
746 | 746 |
} |
747 |
- fmt.Fprintf(w, "%s ago\n", HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) |
|
747 |
+ fmt.Fprintf(w, "%s ago\n", utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) |
|
748 | 748 |
} else { |
749 | 749 |
if *noTrunc { |
750 | 750 |
fmt.Fprintln(w, out.Id) |
751 | 751 |
} else { |
752 |
- fmt.Fprintln(w, TruncateId(out.Id)) |
|
752 |
+ fmt.Fprintln(w, utils.TruncateId(out.Id)) |
|
753 | 753 |
} |
754 | 754 |
} |
755 | 755 |
} |
... | ... |
@@ -809,15 +810,15 @@ func CmdPs(args ...string) error { |
809 | 809 |
for _, out := range outs { |
810 | 810 |
if !*quiet { |
811 | 811 |
if *noTrunc { |
812 |
- 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) |
|
812 |
+ 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) |
|
813 | 813 |
} else { |
814 |
- 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) |
|
814 |
+ 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) |
|
815 | 815 |
} |
816 | 816 |
} else { |
817 | 817 |
if *noTrunc { |
818 | 818 |
fmt.Fprintln(w, out.Id) |
819 | 819 |
} else { |
820 |
- fmt.Fprintln(w, TruncateId(out.Id)) |
|
820 |
+ fmt.Fprintln(w, utils.TruncateId(out.Id)) |
|
821 | 821 |
} |
822 | 822 |
} |
823 | 823 |
} |
... | ... |
@@ -1244,20 +1245,20 @@ func hijack(method, path string, setRawTerminal bool) error { |
1244 | 1244 |
rwc, br := clientconn.Hijack() |
1245 | 1245 |
defer rwc.Close() |
1246 | 1246 |
|
1247 |
- receiveStdout := Go(func() error { |
|
1247 |
+ receiveStdout := utils.Go(func() error { |
|
1248 | 1248 |
_, err := io.Copy(os.Stdout, br) |
1249 | 1249 |
return err |
1250 | 1250 |
}) |
1251 | 1251 |
|
1252 | 1252 |
if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { |
1253 |
- if oldState, err := SetRawTerminal(); err != nil { |
|
1253 |
+ if oldState, err := term.SetRawTerminal(); err != nil { |
|
1254 | 1254 |
return err |
1255 | 1255 |
} else { |
1256 |
- defer RestoreTerminal(oldState) |
|
1256 |
+ defer term.RestoreTerminal(oldState) |
|
1257 | 1257 |
} |
1258 | 1258 |
} |
1259 | 1259 |
|
1260 |
- sendStdin := Go(func() error { |
|
1260 |
+ sendStdin := utils.Go(func() error { |
|
1261 | 1261 |
_, err := io.Copy(rwc, os.Stdin) |
1262 | 1262 |
if err := rwc.(*net.TCPConn).CloseWrite(); err != nil { |
1263 | 1263 |
fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err) |
... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"flag" |
6 | 6 |
"fmt" |
7 |
+ "github.com/dotcloud/docker/utils" |
|
7 | 8 |
"github.com/kr/pty" |
8 | 9 |
"io" |
9 | 10 |
"io/ioutil" |
... | ... |
@@ -39,8 +40,8 @@ type Container struct { |
39 | 39 |
ResolvConfPath string |
40 | 40 |
|
41 | 41 |
cmd *exec.Cmd |
42 |
- stdout *writeBroadcaster |
|
43 |
- stderr *writeBroadcaster |
|
42 |
+ stdout *utils.WriteBroadcaster |
|
43 |
+ stderr *utils.WriteBroadcaster |
|
44 | 44 |
stdin io.ReadCloser |
45 | 45 |
stdinPipe io.WriteCloser |
46 | 46 |
ptyMaster io.Closer |
... | ... |
@@ -251,9 +252,9 @@ func (container *Container) startPty() error { |
251 | 251 |
// Copy the PTYs to our broadcasters |
252 | 252 |
go func() { |
253 | 253 |
defer container.stdout.CloseWriters() |
254 |
- Debugf("[startPty] Begin of stdout pipe") |
|
254 |
+ utils.Debugf("[startPty] Begin of stdout pipe") |
|
255 | 255 |
io.Copy(container.stdout, ptyMaster) |
256 |
- Debugf("[startPty] End of stdout pipe") |
|
256 |
+ utils.Debugf("[startPty] End of stdout pipe") |
|
257 | 257 |
}() |
258 | 258 |
|
259 | 259 |
// stdin |
... | ... |
@@ -262,9 +263,9 @@ func (container *Container) startPty() error { |
262 | 262 |
container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} |
263 | 263 |
go func() { |
264 | 264 |
defer container.stdin.Close() |
265 |
- Debugf("[startPty] Begin of stdin pipe") |
|
265 |
+ utils.Debugf("[startPty] Begin of stdin pipe") |
|
266 | 266 |
io.Copy(ptyMaster, container.stdin) |
267 |
- Debugf("[startPty] End of stdin pipe") |
|
267 |
+ utils.Debugf("[startPty] End of stdin pipe") |
|
268 | 268 |
}() |
269 | 269 |
} |
270 | 270 |
if err := container.cmd.Start(); err != nil { |
... | ... |
@@ -284,9 +285,9 @@ func (container *Container) start() error { |
284 | 284 |
} |
285 | 285 |
go func() { |
286 | 286 |
defer stdin.Close() |
287 |
- Debugf("Begin of stdin pipe [start]") |
|
287 |
+ utils.Debugf("Begin of stdin pipe [start]") |
|
288 | 288 |
io.Copy(stdin, container.stdin) |
289 |
- Debugf("End of stdin pipe [start]") |
|
289 |
+ utils.Debugf("End of stdin pipe [start]") |
|
290 | 290 |
}() |
291 | 291 |
} |
292 | 292 |
return container.cmd.Start() |
... | ... |
@@ -303,8 +304,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
303 | 303 |
errors <- err |
304 | 304 |
} else { |
305 | 305 |
go func() { |
306 |
- Debugf("[start] attach stdin\n") |
|
307 |
- defer Debugf("[end] attach stdin\n") |
|
306 |
+ utils.Debugf("[start] attach stdin\n") |
|
307 |
+ defer utils.Debugf("[end] attach stdin\n") |
|
308 | 308 |
// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr |
309 | 309 |
if cStdout != nil { |
310 | 310 |
defer cStdout.Close() |
... | ... |
@@ -316,12 +317,12 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
316 | 316 |
defer cStdin.Close() |
317 | 317 |
} |
318 | 318 |
if container.Config.Tty { |
319 |
- _, err = CopyEscapable(cStdin, stdin) |
|
319 |
+ _, err = utils.CopyEscapable(cStdin, stdin) |
|
320 | 320 |
} else { |
321 | 321 |
_, err = io.Copy(cStdin, stdin) |
322 | 322 |
} |
323 | 323 |
if err != nil { |
324 |
- Debugf("[error] attach stdin: %s\n", err) |
|
324 |
+ utils.Debugf("[error] attach stdin: %s\n", err) |
|
325 | 325 |
} |
326 | 326 |
// Discard error, expecting pipe error |
327 | 327 |
errors <- nil |
... | ... |
@@ -335,8 +336,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
335 | 335 |
} else { |
336 | 336 |
cStdout = p |
337 | 337 |
go func() { |
338 |
- Debugf("[start] attach stdout\n") |
|
339 |
- defer Debugf("[end] attach stdout\n") |
|
338 |
+ utils.Debugf("[start] attach stdout\n") |
|
339 |
+ defer utils.Debugf("[end] attach stdout\n") |
|
340 | 340 |
// If we are in StdinOnce mode, then close stdin |
341 | 341 |
if container.Config.StdinOnce { |
342 | 342 |
if stdin != nil { |
... | ... |
@@ -348,7 +349,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
348 | 348 |
} |
349 | 349 |
_, err := io.Copy(stdout, cStdout) |
350 | 350 |
if err != nil { |
351 |
- Debugf("[error] attach stdout: %s\n", err) |
|
351 |
+ utils.Debugf("[error] attach stdout: %s\n", err) |
|
352 | 352 |
} |
353 | 353 |
errors <- err |
354 | 354 |
}() |
... | ... |
@@ -361,8 +362,8 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
361 | 361 |
} else { |
362 | 362 |
cStderr = p |
363 | 363 |
go func() { |
364 |
- Debugf("[start] attach stderr\n") |
|
365 |
- defer Debugf("[end] attach stderr\n") |
|
364 |
+ utils.Debugf("[start] attach stderr\n") |
|
365 |
+ defer utils.Debugf("[end] attach stderr\n") |
|
366 | 366 |
// If we are in StdinOnce mode, then close stdin |
367 | 367 |
if container.Config.StdinOnce { |
368 | 368 |
if stdin != nil { |
... | ... |
@@ -374,13 +375,13 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
374 | 374 |
} |
375 | 375 |
_, err := io.Copy(stderr, cStderr) |
376 | 376 |
if err != nil { |
377 |
- Debugf("[error] attach stderr: %s\n", err) |
|
377 |
+ utils.Debugf("[error] attach stderr: %s\n", err) |
|
378 | 378 |
} |
379 | 379 |
errors <- err |
380 | 380 |
}() |
381 | 381 |
} |
382 | 382 |
} |
383 |
- return Go(func() error { |
|
383 |
+ return utils.Go(func() error { |
|
384 | 384 |
if cStdout != nil { |
385 | 385 |
defer cStdout.Close() |
386 | 386 |
} |
... | ... |
@@ -390,14 +391,14 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s |
390 | 390 |
// FIXME: how do clean up the stdin goroutine without the unwanted side effect |
391 | 391 |
// of closing the passed stdin? Add an intermediary io.Pipe? |
392 | 392 |
for i := 0; i < nJobs; i += 1 { |
393 |
- Debugf("Waiting for job %d/%d\n", i+1, nJobs) |
|
393 |
+ utils.Debugf("Waiting for job %d/%d\n", i+1, nJobs) |
|
394 | 394 |
if err := <-errors; err != nil { |
395 |
- Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err) |
|
395 |
+ utils.Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err) |
|
396 | 396 |
return err |
397 | 397 |
} |
398 |
- Debugf("Job %d completed successfully\n", i+1) |
|
398 |
+ utils.Debugf("Job %d completed successfully\n", i+1) |
|
399 | 399 |
} |
400 |
- Debugf("All jobs completed successfully\n") |
|
400 |
+ utils.Debugf("All jobs completed successfully\n") |
|
401 | 401 |
return nil |
402 | 402 |
}) |
403 | 403 |
} |
... | ... |
@@ -555,13 +556,13 @@ func (container *Container) StdinPipe() (io.WriteCloser, error) { |
555 | 555 |
func (container *Container) StdoutPipe() (io.ReadCloser, error) { |
556 | 556 |
reader, writer := io.Pipe() |
557 | 557 |
container.stdout.AddWriter(writer) |
558 |
- return newBufReader(reader), nil |
|
558 |
+ return utils.NewBufReader(reader), nil |
|
559 | 559 |
} |
560 | 560 |
|
561 | 561 |
func (container *Container) StderrPipe() (io.ReadCloser, error) { |
562 | 562 |
reader, writer := io.Pipe() |
563 | 563 |
container.stderr.AddWriter(writer) |
564 |
- return newBufReader(reader), nil |
|
564 |
+ return utils.NewBufReader(reader), nil |
|
565 | 565 |
} |
566 | 566 |
|
567 | 567 |
func (container *Container) allocateNetwork() error { |
... | ... |
@@ -609,20 +610,20 @@ func (container *Container) waitLxc() error { |
609 | 609 |
|
610 | 610 |
func (container *Container) monitor() { |
611 | 611 |
// Wait for the program to exit |
612 |
- Debugf("Waiting for process") |
|
612 |
+ utils.Debugf("Waiting for process") |
|
613 | 613 |
|
614 | 614 |
// If the command does not exists, try to wait via lxc |
615 | 615 |
if container.cmd == nil { |
616 | 616 |
if err := container.waitLxc(); err != nil { |
617 |
- Debugf("%s: Process: %s", container.Id, err) |
|
617 |
+ utils.Debugf("%s: Process: %s", container.Id, err) |
|
618 | 618 |
} |
619 | 619 |
} else { |
620 | 620 |
if err := container.cmd.Wait(); err != nil { |
621 | 621 |
// Discard the error as any signals or non 0 returns will generate an error |
622 |
- Debugf("%s: Process: %s", container.Id, err) |
|
622 |
+ utils.Debugf("%s: Process: %s", container.Id, err) |
|
623 | 623 |
} |
624 | 624 |
} |
625 |
- Debugf("Process finished") |
|
625 |
+ utils.Debugf("Process finished") |
|
626 | 626 |
|
627 | 627 |
var exitCode int = -1 |
628 | 628 |
if container.cmd != nil { |
... | ... |
@@ -633,19 +634,19 @@ func (container *Container) monitor() { |
633 | 633 |
container.releaseNetwork() |
634 | 634 |
if container.Config.OpenStdin { |
635 | 635 |
if err := container.stdin.Close(); err != nil { |
636 |
- Debugf("%s: Error close stdin: %s", container.Id, err) |
|
636 |
+ utils.Debugf("%s: Error close stdin: %s", container.Id, err) |
|
637 | 637 |
} |
638 | 638 |
} |
639 | 639 |
if err := container.stdout.CloseWriters(); err != nil { |
640 |
- Debugf("%s: Error close stdout: %s", container.Id, err) |
|
640 |
+ utils.Debugf("%s: Error close stdout: %s", container.Id, err) |
|
641 | 641 |
} |
642 | 642 |
if err := container.stderr.CloseWriters(); err != nil { |
643 |
- Debugf("%s: Error close stderr: %s", container.Id, err) |
|
643 |
+ utils.Debugf("%s: Error close stderr: %s", container.Id, err) |
|
644 | 644 |
} |
645 | 645 |
|
646 | 646 |
if container.ptyMaster != nil { |
647 | 647 |
if err := container.ptyMaster.Close(); err != nil { |
648 |
- Debugf("%s: Error closing Pty master: %s", container.Id, err) |
|
648 |
+ utils.Debugf("%s: Error closing Pty master: %s", container.Id, err) |
|
649 | 649 |
} |
650 | 650 |
} |
651 | 651 |
|
... | ... |
@@ -762,7 +763,7 @@ func (container *Container) RwChecksum() (string, error) { |
762 | 762 |
if err != nil { |
763 | 763 |
return "", err |
764 | 764 |
} |
765 |
- return HashData(rwData) |
|
765 |
+ return utils.HashData(rwData) |
|
766 | 766 |
} |
767 | 767 |
|
768 | 768 |
func (container *Container) Export() (Archive, error) { |
... | ... |
@@ -833,7 +834,7 @@ func (container *Container) Unmount() error { |
833 | 833 |
// In case of a collision a lookup with Runtime.Get() will fail, and the caller |
834 | 834 |
// will need to use a langer prefix, or the full-length container Id. |
835 | 835 |
func (container *Container) ShortId() string { |
836 |
- return TruncateId(container.Id) |
|
836 |
+ return utils.TruncateId(container.Id) |
|
837 | 837 |
} |
838 | 838 |
|
839 | 839 |
func (container *Container) logPath(name string) string { |
... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"flag" |
5 | 5 |
"fmt" |
6 | 6 |
"github.com/dotcloud/docker" |
7 |
+ "github.com/dotcloud/docker/utils" |
|
7 | 8 |
"io/ioutil" |
8 | 9 |
"log" |
9 | 10 |
"os" |
... | ... |
@@ -17,7 +18,7 @@ var ( |
17 | 17 |
) |
18 | 18 |
|
19 | 19 |
func main() { |
20 |
- if docker.SelfPath() == "/sbin/init" { |
|
20 |
+ if utils.SelfPath() == "/sbin/init" { |
|
21 | 21 |
// Running in init mode |
22 | 22 |
docker.SysInit() |
23 | 23 |
return |
... | ... |
@@ -2,8 +2,9 @@ package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 |
+ "github.com/dotcloud/docker/utils" |
|
5 | 6 |
) |
6 | 7 |
|
7 |
-func getKernelVersion() (*KernelVersionInfo, error) { |
|
8 |
+func getKernelVersion() (*utils.KernelVersionInfo, error) { |
|
8 | 9 |
return nil, fmt.Errorf("Kernel version detection is not available on darwin") |
9 | 10 |
} |
... | ... |
@@ -2,12 +2,14 @@ package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"bytes" |
5 |
+ "github.com/dotcloud/docker/utils" |
|
5 | 6 |
"strconv" |
6 | 7 |
"strings" |
7 | 8 |
"syscall" |
8 | 9 |
) |
9 | 10 |
|
10 |
-func getKernelVersion() (*KernelVersionInfo, error) { |
|
11 |
+// FIXME: Move this to utils package |
|
12 |
+func getKernelVersion() (*utils.KernelVersionInfo, error) { |
|
11 | 13 |
var ( |
12 | 14 |
uts syscall.Utsname |
13 | 15 |
flavor string |
... | ... |
@@ -60,7 +62,7 @@ func getKernelVersion() (*KernelVersionInfo, error) { |
60 | 60 |
flavor = "" |
61 | 61 |
} |
62 | 62 |
|
63 |
- return &KernelVersionInfo{ |
|
63 |
+ return &utils.KernelVersionInfo{ |
|
64 | 64 |
Kernel: kernel, |
65 | 65 |
Major: major, |
66 | 66 |
Minor: minor, |
... | ... |
@@ -3,6 +3,7 @@ package docker |
3 | 3 |
import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"fmt" |
6 |
+ "github.com/dotcloud/docker/utils" |
|
6 | 7 |
"io" |
7 | 8 |
"io/ioutil" |
8 | 9 |
"net/http" |
... | ... |
@@ -17,7 +18,7 @@ import ( |
17 | 17 |
// A Graph is a store for versioned filesystem images and the relationship between them. |
18 | 18 |
type Graph struct { |
19 | 19 |
Root string |
20 |
- idIndex *TruncIndex |
|
20 |
+ idIndex *utils.TruncIndex |
|
21 | 21 |
httpClient *http.Client |
22 | 22 |
checksumLock map[string]*sync.Mutex |
23 | 23 |
lockSumFile *sync.Mutex |
... | ... |
@@ -37,7 +38,7 @@ func NewGraph(root string) (*Graph, error) { |
37 | 37 |
} |
38 | 38 |
graph := &Graph{ |
39 | 39 |
Root: abspath, |
40 |
- idIndex: NewTruncIndex(), |
|
40 |
+ idIndex: utils.NewTruncIndex(), |
|
41 | 41 |
checksumLock: make(map[string]*sync.Mutex), |
42 | 42 |
lockSumFile: &sync.Mutex{}, |
43 | 43 |
lockSumMap: &sync.Mutex{}, |
... | ... |
@@ -165,7 +166,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression, output |
165 | 165 |
if err != nil { |
166 | 166 |
return nil, err |
167 | 167 |
} |
168 |
- return NewTempArchive(ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root) |
|
168 |
+ return NewTempArchive(utils.ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root) |
|
169 | 169 |
} |
170 | 170 |
|
171 | 171 |
// Mktemp creates a temporary sub-directory inside the graph's filesystem. |
... | ... |
@@ -6,6 +6,7 @@ import ( |
6 | 6 |
"encoding/hex" |
7 | 7 |
"encoding/json" |
8 | 8 |
"fmt" |
9 |
+ "github.com/dotcloud/docker/utils" |
|
9 | 10 |
"io" |
10 | 11 |
"io/ioutil" |
11 | 12 |
"log" |
... | ... |
@@ -180,7 +181,7 @@ func (image *Image) Changes(rw string) ([]Change, error) { |
180 | 180 |
} |
181 | 181 |
|
182 | 182 |
func (image *Image) ShortId() string { |
183 |
- return TruncateId(image.Id) |
|
183 |
+ return utils.TruncateId(image.Id) |
|
184 | 184 |
} |
185 | 185 |
|
186 | 186 |
func ValidateId(id string) error { |
... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"encoding/binary" |
5 | 5 |
"errors" |
6 | 6 |
"fmt" |
7 |
+ "github.com/dotcloud/docker/utils" |
|
7 | 8 |
"io" |
8 | 9 |
"log" |
9 | 10 |
"net" |
... | ... |
@@ -97,7 +98,7 @@ func checkRouteOverlaps(dockerNetwork *net.IPNet) error { |
97 | 97 |
if err != nil { |
98 | 98 |
return err |
99 | 99 |
} |
100 |
- Debugf("Routes:\n\n%s", output) |
|
100 |
+ utils.Debugf("Routes:\n\n%s", output) |
|
101 | 101 |
for _, line := range strings.Split(output, "\n") { |
102 | 102 |
if strings.Trim(line, "\r\n\t ") == "" || strings.Contains(line, "default") { |
103 | 103 |
continue |
... | ... |
@@ -126,13 +127,13 @@ func CreateBridgeIface(ifaceName string) error { |
126 | 126 |
ifaceAddr = addr |
127 | 127 |
break |
128 | 128 |
} else { |
129 |
- Debugf("%s: %s", addr, err) |
|
129 |
+ utils.Debugf("%s: %s", addr, err) |
|
130 | 130 |
} |
131 | 131 |
} |
132 | 132 |
if ifaceAddr == "" { |
133 | 133 |
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) |
134 | 134 |
} else { |
135 |
- Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr) |
|
135 |
+ utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr) |
|
136 | 136 |
} |
137 | 137 |
|
138 | 138 |
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 { |
239 | 239 |
// proxy listens for socket connections on `listener`, and forwards them unmodified |
240 | 240 |
// to `proto:address` |
241 | 241 |
func proxy(listener net.Listener, proto, address string) error { |
242 |
- Debugf("proxying to %s:%s", proto, address) |
|
243 |
- defer Debugf("Done proxying to %s:%s", proto, address) |
|
242 |
+ utils.Debugf("proxying to %s:%s", proto, address) |
|
243 |
+ defer utils.Debugf("Done proxying to %s:%s", proto, address) |
|
244 | 244 |
for { |
245 |
- Debugf("Listening on %s", listener) |
|
245 |
+ utils.Debugf("Listening on %s", listener) |
|
246 | 246 |
src, err := listener.Accept() |
247 | 247 |
if err != nil { |
248 | 248 |
return err |
249 | 249 |
} |
250 |
- Debugf("Connecting to %s:%s", proto, address) |
|
250 |
+ utils.Debugf("Connecting to %s:%s", proto, address) |
|
251 | 251 |
dst, err := net.Dial(proto, address) |
252 | 252 |
if err != nil { |
253 | 253 |
log.Printf("Error connecting to %s:%s: %s", proto, address, err) |
254 | 254 |
src.Close() |
255 | 255 |
continue |
256 | 256 |
} |
257 |
- Debugf("Connected to backend, splicing") |
|
257 |
+ utils.Debugf("Connected to backend, splicing") |
|
258 | 258 |
splice(src, dst) |
259 | 259 |
} |
260 | 260 |
return nil |
... | ... |
@@ -317,7 +318,7 @@ func (alloc *PortAllocator) runFountain() { |
317 | 317 |
|
318 | 318 |
// FIXME: Release can no longer fail, change its prototype to reflect that. |
319 | 319 |
func (alloc *PortAllocator) Release(port int) error { |
320 |
- Debugf("Releasing %d", port) |
|
320 |
+ utils.Debugf("Releasing %d", port) |
|
321 | 321 |
alloc.lock.Lock() |
322 | 322 |
delete(alloc.inUse, port) |
323 | 323 |
alloc.lock.Unlock() |
... | ... |
@@ -325,7 +326,7 @@ func (alloc *PortAllocator) Release(port int) error { |
325 | 325 |
} |
326 | 326 |
|
327 | 327 |
func (alloc *PortAllocator) Acquire(port int) (int, error) { |
328 |
- Debugf("Acquiring %d", port) |
|
328 |
+ utils.Debugf("Acquiring %d", port) |
|
329 | 329 |
if port == 0 { |
330 | 330 |
// Allocate a port from the fountain |
331 | 331 |
for port := range alloc.fountain { |
... | ... |
@@ -5,6 +5,7 @@ import ( |
5 | 5 |
"encoding/json" |
6 | 6 |
"fmt" |
7 | 7 |
"github.com/dotcloud/docker/auth" |
8 |
+ "github.com/dotcloud/docker/utils" |
|
8 | 9 |
"github.com/shin-/cookiejar" |
9 | 10 |
"io" |
10 | 11 |
"io/ioutil" |
... | ... |
@@ -19,7 +20,7 @@ import ( |
19 | 19 |
func NewImgJson(src []byte) (*Image, error) { |
20 | 20 |
ret := &Image{} |
21 | 21 |
|
22 |
- Debugf("Json string: {%s}\n", src) |
|
22 |
+ utils.Debugf("Json string: {%s}\n", src) |
|
23 | 23 |
// FIXME: Is there a cleaner way to "purify" the input json? |
24 | 24 |
if err := json.Unmarshal(src, ret); err != nil { |
25 | 25 |
return nil, err |
... | ... |
@@ -58,7 +59,7 @@ func (graph *Graph) getRemoteHistory(imgId, registry string, token []string) ([] |
58 | 58 |
return nil, fmt.Errorf("Error while reading the http response: %s\n", err) |
59 | 59 |
} |
60 | 60 |
|
61 |
- Debugf("Ancestry: %s", jsonString) |
|
61 |
+ utils.Debugf("Ancestry: %s", jsonString) |
|
62 | 62 |
history := new([]string) |
63 | 63 |
if err := json.Unmarshal(jsonString, history); err != nil { |
64 | 64 |
return nil, err |
... | ... |
@@ -116,7 +117,7 @@ func (graph *Graph) getImagesInRepository(repository string, authConfig *auth.Au |
116 | 116 |
|
117 | 117 |
err = json.Unmarshal(jsonData, &imageList) |
118 | 118 |
if err != nil { |
119 |
- Debugf("Body: %s (%s)\n", res.Body, u) |
|
119 |
+ utils.Debugf("Body: %s (%s)\n", res.Body, u) |
|
120 | 120 |
return nil, err |
121 | 121 |
} |
122 | 122 |
|
... | ... |
@@ -166,7 +167,7 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, tok |
166 | 166 |
if err != nil { |
167 | 167 |
return nil, nil, err |
168 | 168 |
} |
169 |
- return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil |
|
169 |
+ return img, utils.ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil |
|
170 | 170 |
} |
171 | 171 |
|
172 | 172 |
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 |
185 | 185 |
req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) |
186 | 186 |
res, err := client.Do(req) |
187 | 187 |
defer res.Body.Close() |
188 |
- Debugf("Got status code %d from %s", res.StatusCode, endpoint) |
|
188 |
+ utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint) |
|
189 | 189 |
if err != nil || (res.StatusCode != 200 && res.StatusCode != 404) { |
190 | 190 |
continue |
191 | 191 |
} else if res.StatusCode == 404 { |
... | ... |
@@ -416,7 +417,7 @@ func (graph *Graph) PushImage(stdout io.Writer, img *Image, registry string, tok |
416 | 416 |
return fmt.Errorf("Error while retrieving checksum for %s: %v", img.Id, err) |
417 | 417 |
} |
418 | 418 |
req.Header.Set("X-Docker-Checksum", checksum) |
419 |
- Debugf("Setting checksum for %s: %s", img.ShortId(), checksum) |
|
419 |
+ utils.Debugf("Setting checksum for %s: %s", img.ShortId(), checksum) |
|
420 | 420 |
res, err := doWithCookies(client, req) |
421 | 421 |
if err != nil { |
422 | 422 |
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 |
469 | 469 |
layerData = &TempArchive{file, st.Size()} |
470 | 470 |
} |
471 | 471 |
|
472 |
- req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer", |
|
473 |
- ProgressReader(layerData, int(layerData.Size), stdout, "")) |
|
472 |
+ req3, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer", utils.ProgressReader(layerData, int(layerData.Size), stdout, "")) |
|
474 | 473 |
if err != nil { |
475 | 474 |
return err |
476 | 475 |
} |
... | ... |
@@ -502,7 +502,7 @@ func (graph *Graph) pushTag(remote, revision, tag, registry string, token []stri |
502 | 502 |
revision = "\"" + revision + "\"" |
503 | 503 |
registry = "https://" + registry + "/v1" |
504 | 504 |
|
505 |
- Debugf("Pushing tags for rev [%s] on {%s}\n", revision, registry+"/users/"+remote+"/"+tag) |
|
505 |
+ utils.Debugf("Pushing tags for rev [%s] on {%s}\n", revision, registry+"/users/"+remote+"/"+tag) |
|
506 | 506 |
|
507 | 507 |
client := graph.getHttpClient() |
508 | 508 |
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 |
624 | 624 |
return err |
625 | 625 |
} |
626 | 626 |
|
627 |
- Debugf("json sent: %s\n", imgListJson) |
|
627 |
+ utils.Debugf("json sent: %s\n", imgListJson) |
|
628 | 628 |
|
629 | 629 |
fmt.Fprintf(stdout, "Sending image list\n") |
630 | 630 |
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 |
642 | 642 |
defer res.Body.Close() |
643 | 643 |
|
644 | 644 |
for res.StatusCode >= 300 && res.StatusCode < 400 { |
645 |
- Debugf("Redirected to %s\n", res.Header.Get("Location")) |
|
645 |
+ utils.Debugf("Redirected to %s\n", res.Header.Get("Location")) |
|
646 | 646 |
req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJson)) |
647 | 647 |
if err != nil { |
648 | 648 |
return err |
... | ... |
@@ -669,7 +669,7 @@ func (graph *Graph) PushRepository(stdout io.Writer, remote string, localRepo Re |
669 | 669 |
var token, endpoints []string |
670 | 670 |
if res.Header.Get("X-Docker-Token") != "" { |
671 | 671 |
token = res.Header["X-Docker-Token"] |
672 |
- Debugf("Auth token: %v", token) |
|
672 |
+ utils.Debugf("Auth token: %v", token) |
|
673 | 673 |
} else { |
674 | 674 |
return fmt.Errorf("Index response didn't contain an access token") |
675 | 675 |
} |
... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"container/list" |
5 | 5 |
"fmt" |
6 | 6 |
"github.com/dotcloud/docker/auth" |
7 |
+ "github.com/dotcloud/docker/utils" |
|
7 | 8 |
"io" |
8 | 9 |
"io/ioutil" |
9 | 10 |
"log" |
... | ... |
@@ -27,9 +28,9 @@ type Runtime struct { |
27 | 27 |
graph *Graph |
28 | 28 |
repositories *TagStore |
29 | 29 |
authConfig *auth.AuthConfig |
30 |
- idIndex *TruncIndex |
|
30 |
+ idIndex *utils.TruncIndex |
|
31 | 31 |
capabilities *Capabilities |
32 |
- kernelVersion *KernelVersionInfo |
|
32 |
+ kernelVersion *utils.KernelVersionInfo |
|
33 | 33 |
autoRestart bool |
34 | 34 |
volumes *Graph |
35 | 35 |
} |
... | ... |
@@ -37,7 +38,7 @@ type Runtime struct { |
37 | 37 |
var sysInitPath string |
38 | 38 |
|
39 | 39 |
func init() { |
40 |
- sysInitPath = SelfPath() |
|
40 |
+ sysInitPath = utils.SelfPath() |
|
41 | 41 |
} |
42 | 42 |
|
43 | 43 |
func (runtime *Runtime) List() []*Container { |
... | ... |
@@ -113,13 +114,13 @@ func (runtime *Runtime) Register(container *Container) error { |
113 | 113 |
container.runtime = runtime |
114 | 114 |
|
115 | 115 |
// Attach to stdout and stderr |
116 |
- container.stderr = newWriteBroadcaster() |
|
117 |
- container.stdout = newWriteBroadcaster() |
|
116 |
+ container.stderr = utils.NewWriteBroadcaster() |
|
117 |
+ container.stdout = utils.NewWriteBroadcaster() |
|
118 | 118 |
// Attach to stdin |
119 | 119 |
if container.Config.OpenStdin { |
120 | 120 |
container.stdin, container.stdinPipe = io.Pipe() |
121 | 121 |
} else { |
122 |
- container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
122 |
+ container.stdinPipe = utils.NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
123 | 123 |
} |
124 | 124 |
// done |
125 | 125 |
runtime.containers.PushBack(container) |
... | ... |
@@ -137,9 +138,9 @@ func (runtime *Runtime) Register(container *Container) error { |
137 | 137 |
return err |
138 | 138 |
} else { |
139 | 139 |
if !strings.Contains(string(output), "RUNNING") { |
140 |
- Debugf("Container %s was supposed to be running be is not.", container.Id) |
|
140 |
+ utils.Debugf("Container %s was supposed to be running be is not.", container.Id) |
|
141 | 141 |
if runtime.autoRestart { |
142 |
- Debugf("Restarting") |
|
142 |
+ utils.Debugf("Restarting") |
|
143 | 143 |
container.State.Ghost = false |
144 | 144 |
container.State.setStopped(0) |
145 | 145 |
if err := container.Start(); err != nil { |
... | ... |
@@ -147,7 +148,7 @@ func (runtime *Runtime) Register(container *Container) error { |
147 | 147 |
} |
148 | 148 |
nomonitor = true |
149 | 149 |
} else { |
150 |
- Debugf("Marking as stopped") |
|
150 |
+ utils.Debugf("Marking as stopped") |
|
151 | 151 |
container.State.setStopped(-127) |
152 | 152 |
if err := container.ToDisk(); err != nil { |
153 | 153 |
return err |
... | ... |
@@ -168,7 +169,7 @@ func (runtime *Runtime) Register(container *Container) error { |
168 | 168 |
return nil |
169 | 169 |
} |
170 | 170 |
|
171 |
-func (runtime *Runtime) LogToDisk(src *writeBroadcaster, dst string) error { |
|
171 |
+func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst string) error { |
|
172 | 172 |
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) |
173 | 173 |
if err != nil { |
174 | 174 |
return err |
... | ... |
@@ -215,16 +216,16 @@ func (runtime *Runtime) restore() error { |
215 | 215 |
id := v.Name() |
216 | 216 |
container, err := runtime.Load(id) |
217 | 217 |
if err != nil { |
218 |
- Debugf("Failed to load container %v: %v", id, err) |
|
218 |
+ utils.Debugf("Failed to load container %v: %v", id, err) |
|
219 | 219 |
continue |
220 | 220 |
} |
221 |
- Debugf("Loaded container %v", container.Id) |
|
221 |
+ utils.Debugf("Loaded container %v", container.Id) |
|
222 | 222 |
} |
223 | 223 |
return nil |
224 | 224 |
} |
225 | 225 |
|
226 | 226 |
func (runtime *Runtime) UpdateCapabilities(quiet bool) { |
227 |
- if cgroupMemoryMountpoint, err := FindCgroupMountpoint("memory"); err != nil { |
|
227 |
+ if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil { |
|
228 | 228 |
if !quiet { |
229 | 229 |
log.Printf("WARNING: %s\n", err) |
230 | 230 |
} |
... | ... |
@@ -255,7 +256,7 @@ func NewRuntime(autoRestart bool) (*Runtime, error) { |
255 | 255 |
log.Printf("WARNING: %s\n", err) |
256 | 256 |
} else { |
257 | 257 |
runtime.kernelVersion = k |
258 |
- if CompareKernelVersion(k, &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { |
|
258 |
+ if utils.CompareKernelVersion(k, &utils.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { |
|
259 | 259 |
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()) |
260 | 260 |
} |
261 | 261 |
} |
... | ... |
@@ -302,7 +303,7 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) { |
302 | 302 |
graph: g, |
303 | 303 |
repositories: repositories, |
304 | 304 |
authConfig: authConfig, |
305 |
- idIndex: NewTruncIndex(), |
|
305 |
+ idIndex: utils.NewTruncIndex(), |
|
306 | 306 |
capabilities: &Capabilities{}, |
307 | 307 |
autoRestart: autoRestart, |
308 | 308 |
volumes: volumes, |
... | ... |
@@ -2,6 +2,7 @@ package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 |
+ "github.com/dotcloud/docker/utils" |
|
5 | 6 |
"io" |
6 | 7 |
"log" |
7 | 8 |
"net/http" |
... | ... |
@@ -54,7 +55,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) { |
54 | 54 |
var out ApiSearch |
55 | 55 |
out.Description = repo["description"] |
56 | 56 |
if len(out.Description) > 45 { |
57 |
- out.Description = Trunc(out.Description, 42) + "..." |
|
57 |
+ out.Description = utils.Trunc(out.Description, 42) + "..." |
|
58 | 58 |
} |
59 | 59 |
out.Name = repo["name"] |
60 | 60 |
outs = append(outs, out) |
... | ... |
@@ -68,7 +69,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { |
68 | 68 |
return err |
69 | 69 |
} |
70 | 70 |
|
71 |
- file, err := Download(url, out) |
|
71 |
+ file, err := utils.Download(url, out) |
|
72 | 72 |
if err != nil { |
73 | 73 |
return err |
74 | 74 |
} |
... | ... |
@@ -85,7 +86,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { |
85 | 85 |
return err |
86 | 86 |
} |
87 | 87 |
|
88 |
- if err := c.Inject(ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil { |
|
88 |
+ if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)"), path); err != nil { |
|
89 | 89 |
return err |
90 | 90 |
} |
91 | 91 |
// FIXME: Handle custom repo, tag comment, author |
... | ... |
@@ -124,7 +125,7 @@ func (srv *Server) ImagesViz(out io.Writer) error { |
124 | 124 |
|
125 | 125 |
for name, repository := range srv.runtime.repositories.Repositories { |
126 | 126 |
for tag, id := range repository { |
127 |
- reporefs[TruncateId(id)] = append(reporefs[TruncateId(id)], fmt.Sprintf("%s:%s", name, tag)) |
|
127 |
+ reporefs[utils.TruncateId(id)] = append(reporefs[utils.TruncateId(id)], fmt.Sprintf("%s:%s", name, tag)) |
|
128 | 128 |
} |
129 | 129 |
} |
130 | 130 |
|
... | ... |
@@ -193,7 +194,7 @@ func (srv *Server) DockerInfo() ApiInfo { |
193 | 193 |
out.GoVersion = runtime.Version() |
194 | 194 |
if os.Getenv("DEBUG") != "" { |
195 | 195 |
out.Debug = true |
196 |
- out.NFd = getTotalUsedFds() |
|
196 |
+ out.NFd = utils.GetTotalUsedFds() |
|
197 | 197 |
out.NGoroutines = runtime.NumGoroutine() |
198 | 198 |
} |
199 | 199 |
return out |
... | ... |
@@ -299,7 +300,7 @@ func (srv *Server) ImagePull(name, tag, registry string, out io.Writer) error { |
299 | 299 |
func (srv *Server) ImagePush(name, registry string, out io.Writer) error { |
300 | 300 |
img, err := srv.runtime.graph.Get(name) |
301 | 301 |
if err != nil { |
302 |
- Debugf("The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) |
|
302 |
+ utils.Debugf("The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) |
|
303 | 303 |
// If it fails, try to get the repository |
304 | 304 |
if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists { |
305 | 305 |
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 |
336 | 336 |
fmt.Fprintln(out, "Downloading from", u) |
337 | 337 |
// Download with curl (pretty progress bar) |
338 | 338 |
// If curl is not available, fallback to http.Get() |
339 |
- resp, err = Download(u.String(), out) |
|
339 |
+ resp, err = utils.Download(u.String(), out) |
|
340 | 340 |
if err != nil { |
341 | 341 |
return err |
342 | 342 |
} |
343 |
- archive = ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)") |
|
343 |
+ archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, "Importing %v/%v (%v)") |
|
344 | 344 |
} |
345 | 345 |
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil) |
346 | 346 |
if err != nil { |
... | ... |
@@ -486,17 +487,17 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std |
486 | 486 |
if stdout { |
487 | 487 |
cLog, err := container.ReadLog("stdout") |
488 | 488 |
if err != nil { |
489 |
- Debugf(err.Error()) |
|
489 |
+ utils.Debugf(err.Error()) |
|
490 | 490 |
} else if _, err := io.Copy(out, cLog); err != nil { |
491 |
- Debugf(err.Error()) |
|
491 |
+ utils.Debugf(err.Error()) |
|
492 | 492 |
} |
493 | 493 |
} |
494 | 494 |
if stderr { |
495 | 495 |
cLog, err := container.ReadLog("stderr") |
496 | 496 |
if err != nil { |
497 |
- Debugf(err.Error()) |
|
497 |
+ utils.Debugf(err.Error()) |
|
498 | 498 |
} else if _, err := io.Copy(out, cLog); err != nil { |
499 |
- Debugf(err.Error()) |
|
499 |
+ utils.Debugf(err.Error()) |
|
500 | 500 |
} |
501 | 501 |
} |
502 | 502 |
} |
... | ... |
@@ -517,7 +518,7 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std |
517 | 517 |
r, w := io.Pipe() |
518 | 518 |
go func() { |
519 | 519 |
defer w.Close() |
520 |
- defer Debugf("Closing buffered stdin pipe") |
|
520 |
+ defer utils.Debugf("Closing buffered stdin pipe") |
|
521 | 521 |
io.Copy(w, in) |
522 | 522 |
}() |
523 | 523 |
cStdin = r |
... | ... |
@@ -2,6 +2,7 @@ package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 |
+ "github.com/dotcloud/docker/utils" |
|
5 | 6 |
"sync" |
6 | 7 |
"time" |
7 | 8 |
) |
... | ... |
@@ -21,7 +22,7 @@ func (s *State) String() string { |
21 | 21 |
if s.Ghost { |
22 | 22 |
return fmt.Sprintf("Ghost") |
23 | 23 |
} |
24 |
- return fmt.Sprintf("Up %s", HumanDuration(time.Now().Sub(s.StartedAt))) |
|
24 |
+ return fmt.Sprintf("Up %s", utils.HumanDuration(time.Now().Sub(s.StartedAt))) |
|
25 | 25 |
} |
26 | 26 |
return fmt.Sprintf("Exit %d", s.ExitCode) |
27 | 27 |
} |
... | ... |
@@ -3,6 +3,7 @@ package docker |
3 | 3 |
import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"fmt" |
6 |
+ "github.com/dotcloud/docker/utils" |
|
6 | 7 |
"io/ioutil" |
7 | 8 |
"os" |
8 | 9 |
"path/filepath" |
... | ... |
@@ -106,7 +107,7 @@ func (store *TagStore) ImageName(id string) string { |
106 | 106 |
if names, exists := store.ById()[id]; exists && len(names) > 0 { |
107 | 107 |
return names[0] |
108 | 108 |
} |
109 |
- return TruncateId(id) |
|
109 |
+ return utils.TruncateId(id) |
|
110 | 110 |
} |
111 | 111 |
|
112 | 112 |
func (store *TagStore) Set(repoName, tag, imageName string, force bool) error { |
... | ... |
@@ -1,6 +1,8 @@ |
1 | 1 |
package term |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "os" |
|
5 |
+ "os/signal" |
|
4 | 6 |
"syscall" |
5 | 7 |
"unsafe" |
6 | 8 |
) |
... | ... |
@@ -120,3 +122,22 @@ func Restore(fd int, state *State) error { |
120 | 120 |
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) |
121 | 121 |
return err |
122 | 122 |
} |
123 |
+ |
|
124 |
+func SetRawTerminal() (*State, error) { |
|
125 |
+ oldState, err := MakeRaw(int(os.Stdin.Fd())) |
|
126 |
+ if err != nil { |
|
127 |
+ return nil, err |
|
128 |
+ } |
|
129 |
+ c := make(chan os.Signal, 1) |
|
130 |
+ signal.Notify(c, os.Interrupt) |
|
131 |
+ go func() { |
|
132 |
+ _ = <-c |
|
133 |
+ Restore(int(os.Stdin.Fd()), oldState) |
|
134 |
+ os.Exit(0) |
|
135 |
+ }() |
|
136 |
+ return oldState, err |
|
137 |
+} |
|
138 |
+ |
|
139 |
+func RestoreTerminal(state *State) { |
|
140 |
+ Restore(int(os.Stdin.Fd()), state) |
|
141 |
+} |
... | ... |
@@ -1,500 +1,9 @@ |
1 | 1 |
package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "bytes" |
|
5 |
- "crypto/sha256" |
|
6 |
- "encoding/hex" |
|
7 |
- "errors" |
|
8 |
- "fmt" |
|
9 |
- "github.com/dotcloud/docker/term" |
|
10 |
- "index/suffixarray" |
|
11 |
- "io" |
|
12 |
- "io/ioutil" |
|
13 |
- "net/http" |
|
14 |
- "os" |
|
15 |
- "os/exec" |
|
16 |
- "os/signal" |
|
17 |
- "path/filepath" |
|
18 |
- "runtime" |
|
19 |
- "strings" |
|
20 |
- "sync" |
|
21 |
- "time" |
|
4 |
+ "github.com/dotcloud/docker/utils" |
|
22 | 5 |
) |
23 | 6 |
|
24 |
-// Go is a basic promise implementation: it wraps calls a function in a goroutine, |
|
25 |
-// and returns a channel which will later return the function's return value. |
|
26 |
-func Go(f func() error) chan error { |
|
27 |
- ch := make(chan error) |
|
28 |
- go func() { |
|
29 |
- ch <- f() |
|
30 |
- }() |
|
31 |
- return ch |
|
32 |
-} |
|
33 |
- |
|
34 |
-// Request a given URL and return an io.Reader |
|
35 |
-func Download(url string, stderr io.Writer) (*http.Response, error) { |
|
36 |
- var resp *http.Response |
|
37 |
- var err error = nil |
|
38 |
- if resp, err = http.Get(url); err != nil { |
|
39 |
- return nil, err |
|
40 |
- } |
|
41 |
- if resp.StatusCode >= 400 { |
|
42 |
- return nil, errors.New("Got HTTP status code >= 400: " + resp.Status) |
|
43 |
- } |
|
44 |
- return resp, nil |
|
45 |
-} |
|
46 |
- |
|
47 |
-// Debug function, if the debug flag is set, then display. Do nothing otherwise |
|
48 |
-// If Docker is in damon mode, also send the debug info on the socket |
|
49 |
-func Debugf(format string, a ...interface{}) { |
|
50 |
- if os.Getenv("DEBUG") != "" { |
|
51 |
- |
|
52 |
- // Retrieve the stack infos |
|
53 |
- _, file, line, ok := runtime.Caller(1) |
|
54 |
- if !ok { |
|
55 |
- file = "<unknown>" |
|
56 |
- line = -1 |
|
57 |
- } else { |
|
58 |
- file = file[strings.LastIndex(file, "/")+1:] |
|
59 |
- } |
|
60 |
- |
|
61 |
- fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...) |
|
62 |
- } |
|
63 |
-} |
|
64 |
- |
|
65 |
-// Reader with progress bar |
|
66 |
-type progressReader struct { |
|
67 |
- reader io.ReadCloser // Stream to read from |
|
68 |
- output io.Writer // Where to send progress bar to |
|
69 |
- readTotal int // Expected stream length (bytes) |
|
70 |
- readProgress int // How much has been read so far (bytes) |
|
71 |
- lastUpdate int // How many bytes read at least update |
|
72 |
- template string // Template to print. Default "%v/%v (%v)" |
|
73 |
-} |
|
74 |
- |
|
75 |
-func (r *progressReader) Read(p []byte) (n int, err error) { |
|
76 |
- read, err := io.ReadCloser(r.reader).Read(p) |
|
77 |
- r.readProgress += read |
|
78 |
- |
|
79 |
- updateEvery := 4096 |
|
80 |
- if r.readTotal > 0 { |
|
81 |
- // Only update progress for every 1% read |
|
82 |
- if increment := int(0.01 * float64(r.readTotal)); increment > updateEvery { |
|
83 |
- updateEvery = increment |
|
84 |
- } |
|
85 |
- } |
|
86 |
- if r.readProgress-r.lastUpdate > updateEvery || err != nil { |
|
87 |
- if r.readTotal > 0 { |
|
88 |
- fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) |
|
89 |
- } else { |
|
90 |
- fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a") |
|
91 |
- } |
|
92 |
- r.lastUpdate = r.readProgress |
|
93 |
- } |
|
94 |
- // Send newline when complete |
|
95 |
- if err != nil { |
|
96 |
- fmt.Fprintf(r.output, "\n") |
|
97 |
- } |
|
98 |
- |
|
99 |
- return read, err |
|
100 |
-} |
|
101 |
-func (r *progressReader) Close() error { |
|
102 |
- return io.ReadCloser(r.reader).Close() |
|
103 |
-} |
|
104 |
-func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader { |
|
105 |
- if template == "" { |
|
106 |
- template = "%v/%v (%v)" |
|
107 |
- } |
|
108 |
- return &progressReader{r, output, size, 0, 0, template} |
|
109 |
-} |
|
110 |
- |
|
111 |
-// HumanDuration returns a human-readable approximation of a duration |
|
112 |
-// (eg. "About a minute", "4 hours ago", etc.) |
|
113 |
-func HumanDuration(d time.Duration) string { |
|
114 |
- if seconds := int(d.Seconds()); seconds < 1 { |
|
115 |
- return "Less than a second" |
|
116 |
- } else if seconds < 60 { |
|
117 |
- return fmt.Sprintf("%d seconds", seconds) |
|
118 |
- } else if minutes := int(d.Minutes()); minutes == 1 { |
|
119 |
- return "About a minute" |
|
120 |
- } else if minutes < 60 { |
|
121 |
- return fmt.Sprintf("%d minutes", minutes) |
|
122 |
- } else if hours := int(d.Hours()); hours == 1 { |
|
123 |
- return "About an hour" |
|
124 |
- } else if hours < 48 { |
|
125 |
- return fmt.Sprintf("%d hours", hours) |
|
126 |
- } else if hours < 24*7*2 { |
|
127 |
- return fmt.Sprintf("%d days", hours/24) |
|
128 |
- } else if hours < 24*30*3 { |
|
129 |
- return fmt.Sprintf("%d weeks", hours/24/7) |
|
130 |
- } else if hours < 24*365*2 { |
|
131 |
- return fmt.Sprintf("%d months", hours/24/30) |
|
132 |
- } |
|
133 |
- return fmt.Sprintf("%d years", d.Hours()/24/365) |
|
134 |
-} |
|
135 |
- |
|
136 |
-func Trunc(s string, maxlen int) string { |
|
137 |
- if len(s) <= maxlen { |
|
138 |
- return s |
|
139 |
- } |
|
140 |
- return s[:maxlen] |
|
141 |
-} |
|
142 |
- |
|
143 |
-// Figure out the absolute path of our own binary |
|
144 |
-func SelfPath() string { |
|
145 |
- path, err := exec.LookPath(os.Args[0]) |
|
146 |
- if err != nil { |
|
147 |
- panic(err) |
|
148 |
- } |
|
149 |
- path, err = filepath.Abs(path) |
|
150 |
- if err != nil { |
|
151 |
- panic(err) |
|
152 |
- } |
|
153 |
- return path |
|
154 |
-} |
|
155 |
- |
|
156 |
-type nopWriter struct { |
|
157 |
-} |
|
158 |
- |
|
159 |
-func (w *nopWriter) Write(buf []byte) (int, error) { |
|
160 |
- return len(buf), nil |
|
161 |
-} |
|
162 |
- |
|
163 |
-type nopWriteCloser struct { |
|
164 |
- io.Writer |
|
165 |
-} |
|
166 |
- |
|
167 |
-func (w *nopWriteCloser) Close() error { return nil } |
|
168 |
- |
|
169 |
-func NopWriteCloser(w io.Writer) io.WriteCloser { |
|
170 |
- return &nopWriteCloser{w} |
|
171 |
-} |
|
172 |
- |
|
173 |
-type bufReader struct { |
|
174 |
- buf *bytes.Buffer |
|
175 |
- reader io.Reader |
|
176 |
- err error |
|
177 |
- l sync.Mutex |
|
178 |
- wait sync.Cond |
|
179 |
-} |
|
180 |
- |
|
181 |
-func newBufReader(r io.Reader) *bufReader { |
|
182 |
- reader := &bufReader{ |
|
183 |
- buf: &bytes.Buffer{}, |
|
184 |
- reader: r, |
|
185 |
- } |
|
186 |
- reader.wait.L = &reader.l |
|
187 |
- go reader.drain() |
|
188 |
- return reader |
|
189 |
-} |
|
190 |
- |
|
191 |
-func (r *bufReader) drain() { |
|
192 |
- buf := make([]byte, 1024) |
|
193 |
- for { |
|
194 |
- n, err := r.reader.Read(buf) |
|
195 |
- r.l.Lock() |
|
196 |
- if err != nil { |
|
197 |
- r.err = err |
|
198 |
- } else { |
|
199 |
- r.buf.Write(buf[0:n]) |
|
200 |
- } |
|
201 |
- r.wait.Signal() |
|
202 |
- r.l.Unlock() |
|
203 |
- if err != nil { |
|
204 |
- break |
|
205 |
- } |
|
206 |
- } |
|
207 |
-} |
|
208 |
- |
|
209 |
-func (r *bufReader) Read(p []byte) (n int, err error) { |
|
210 |
- r.l.Lock() |
|
211 |
- defer r.l.Unlock() |
|
212 |
- for { |
|
213 |
- n, err = r.buf.Read(p) |
|
214 |
- if n > 0 { |
|
215 |
- return n, err |
|
216 |
- } |
|
217 |
- if r.err != nil { |
|
218 |
- return 0, r.err |
|
219 |
- } |
|
220 |
- r.wait.Wait() |
|
221 |
- } |
|
222 |
- panic("unreachable") |
|
223 |
-} |
|
224 |
- |
|
225 |
-func (r *bufReader) Close() error { |
|
226 |
- closer, ok := r.reader.(io.ReadCloser) |
|
227 |
- if !ok { |
|
228 |
- return nil |
|
229 |
- } |
|
230 |
- return closer.Close() |
|
231 |
-} |
|
232 |
- |
|
233 |
-type writeBroadcaster struct { |
|
234 |
- mu sync.Mutex |
|
235 |
- writers map[io.WriteCloser]struct{} |
|
236 |
-} |
|
237 |
- |
|
238 |
-func (w *writeBroadcaster) AddWriter(writer io.WriteCloser) { |
|
239 |
- w.mu.Lock() |
|
240 |
- w.writers[writer] = struct{}{} |
|
241 |
- w.mu.Unlock() |
|
242 |
-} |
|
243 |
- |
|
244 |
-// FIXME: Is that function used? |
|
245 |
-// FIXME: This relies on the concrete writer type used having equality operator |
|
246 |
-func (w *writeBroadcaster) RemoveWriter(writer io.WriteCloser) { |
|
247 |
- w.mu.Lock() |
|
248 |
- delete(w.writers, writer) |
|
249 |
- w.mu.Unlock() |
|
250 |
-} |
|
251 |
- |
|
252 |
-func (w *writeBroadcaster) Write(p []byte) (n int, err error) { |
|
253 |
- w.mu.Lock() |
|
254 |
- defer w.mu.Unlock() |
|
255 |
- for writer := range w.writers { |
|
256 |
- if n, err := writer.Write(p); err != nil || n != len(p) { |
|
257 |
- // On error, evict the writer |
|
258 |
- delete(w.writers, writer) |
|
259 |
- } |
|
260 |
- } |
|
261 |
- return len(p), nil |
|
262 |
-} |
|
263 |
- |
|
264 |
-func (w *writeBroadcaster) CloseWriters() error { |
|
265 |
- w.mu.Lock() |
|
266 |
- defer w.mu.Unlock() |
|
267 |
- for writer := range w.writers { |
|
268 |
- writer.Close() |
|
269 |
- } |
|
270 |
- w.writers = make(map[io.WriteCloser]struct{}) |
|
271 |
- return nil |
|
272 |
-} |
|
273 |
- |
|
274 |
-func newWriteBroadcaster() *writeBroadcaster { |
|
275 |
- return &writeBroadcaster{writers: make(map[io.WriteCloser]struct{})} |
|
276 |
-} |
|
277 |
- |
|
278 |
-func getTotalUsedFds() int { |
|
279 |
- if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { |
|
280 |
- Debugf("Error opening /proc/%d/fd: %s", os.Getpid(), err) |
|
281 |
- } else { |
|
282 |
- return len(fds) |
|
283 |
- } |
|
284 |
- return -1 |
|
285 |
-} |
|
286 |
- |
|
287 |
-// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. |
|
288 |
-// This is used to retrieve image and container IDs by more convenient shorthand prefixes. |
|
289 |
-type TruncIndex struct { |
|
290 |
- index *suffixarray.Index |
|
291 |
- ids map[string]bool |
|
292 |
- bytes []byte |
|
293 |
-} |
|
294 |
- |
|
295 |
-func NewTruncIndex() *TruncIndex { |
|
296 |
- return &TruncIndex{ |
|
297 |
- index: suffixarray.New([]byte{' '}), |
|
298 |
- ids: make(map[string]bool), |
|
299 |
- bytes: []byte{' '}, |
|
300 |
- } |
|
301 |
-} |
|
302 |
- |
|
303 |
-func (idx *TruncIndex) Add(id string) error { |
|
304 |
- if strings.Contains(id, " ") { |
|
305 |
- return fmt.Errorf("Illegal character: ' '") |
|
306 |
- } |
|
307 |
- if _, exists := idx.ids[id]; exists { |
|
308 |
- return fmt.Errorf("Id already exists: %s", id) |
|
309 |
- } |
|
310 |
- idx.ids[id] = true |
|
311 |
- idx.bytes = append(idx.bytes, []byte(id+" ")...) |
|
312 |
- idx.index = suffixarray.New(idx.bytes) |
|
313 |
- return nil |
|
314 |
-} |
|
315 |
- |
|
316 |
-func (idx *TruncIndex) Delete(id string) error { |
|
317 |
- if _, exists := idx.ids[id]; !exists { |
|
318 |
- return fmt.Errorf("No such id: %s", id) |
|
319 |
- } |
|
320 |
- before, after, err := idx.lookup(id) |
|
321 |
- if err != nil { |
|
322 |
- return err |
|
323 |
- } |
|
324 |
- delete(idx.ids, id) |
|
325 |
- idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...) |
|
326 |
- idx.index = suffixarray.New(idx.bytes) |
|
327 |
- return nil |
|
328 |
-} |
|
329 |
- |
|
330 |
-func (idx *TruncIndex) lookup(s string) (int, int, error) { |
|
331 |
- offsets := idx.index.Lookup([]byte(" "+s), -1) |
|
332 |
- //log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes()) |
|
333 |
- if offsets == nil || len(offsets) == 0 || len(offsets) > 1 { |
|
334 |
- return -1, -1, fmt.Errorf("No such id: %s", s) |
|
335 |
- } |
|
336 |
- offsetBefore := offsets[0] + 1 |
|
337 |
- offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ") |
|
338 |
- return offsetBefore, offsetAfter, nil |
|
339 |
-} |
|
340 |
- |
|
341 |
-func (idx *TruncIndex) Get(s string) (string, error) { |
|
342 |
- before, after, err := idx.lookup(s) |
|
343 |
- //log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after) |
|
344 |
- if err != nil { |
|
345 |
- return "", err |
|
346 |
- } |
|
347 |
- return string(idx.bytes[before:after]), err |
|
348 |
-} |
|
349 |
- |
|
350 |
-// TruncateId returns a shorthand version of a string identifier for convenience. |
|
351 |
-// A collision with other shorthands is very unlikely, but possible. |
|
352 |
-// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller |
|
353 |
-// will need to use a langer prefix, or the full-length Id. |
|
354 |
-func TruncateId(id string) string { |
|
355 |
- shortLen := 12 |
|
356 |
- if len(id) < shortLen { |
|
357 |
- shortLen = len(id) |
|
358 |
- } |
|
359 |
- return id[:shortLen] |
|
360 |
-} |
|
361 |
- |
|
362 |
-// Code c/c from io.Copy() modified to handle escape sequence |
|
363 |
-func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) { |
|
364 |
- buf := make([]byte, 32*1024) |
|
365 |
- for { |
|
366 |
- nr, er := src.Read(buf) |
|
367 |
- if nr > 0 { |
|
368 |
- // ---- Docker addition |
|
369 |
- // char 16 is C-p |
|
370 |
- if nr == 1 && buf[0] == 16 { |
|
371 |
- nr, er = src.Read(buf) |
|
372 |
- // char 17 is C-q |
|
373 |
- if nr == 1 && buf[0] == 17 { |
|
374 |
- if err := src.Close(); err != nil { |
|
375 |
- return 0, err |
|
376 |
- } |
|
377 |
- return 0, io.EOF |
|
378 |
- } |
|
379 |
- } |
|
380 |
- // ---- End of docker |
|
381 |
- nw, ew := dst.Write(buf[0:nr]) |
|
382 |
- if nw > 0 { |
|
383 |
- written += int64(nw) |
|
384 |
- } |
|
385 |
- if ew != nil { |
|
386 |
- err = ew |
|
387 |
- break |
|
388 |
- } |
|
389 |
- if nr != nw { |
|
390 |
- err = io.ErrShortWrite |
|
391 |
- break |
|
392 |
- } |
|
393 |
- } |
|
394 |
- if er == io.EOF { |
|
395 |
- break |
|
396 |
- } |
|
397 |
- if er != nil { |
|
398 |
- err = er |
|
399 |
- break |
|
400 |
- } |
|
401 |
- } |
|
402 |
- return written, err |
|
403 |
-} |
|
404 |
- |
|
405 |
-func SetRawTerminal() (*term.State, error) { |
|
406 |
- oldState, err := term.MakeRaw(int(os.Stdin.Fd())) |
|
407 |
- if err != nil { |
|
408 |
- return nil, err |
|
409 |
- } |
|
410 |
- c := make(chan os.Signal, 1) |
|
411 |
- signal.Notify(c, os.Interrupt) |
|
412 |
- go func() { |
|
413 |
- _ = <-c |
|
414 |
- term.Restore(int(os.Stdin.Fd()), oldState) |
|
415 |
- os.Exit(0) |
|
416 |
- }() |
|
417 |
- return oldState, err |
|
418 |
-} |
|
419 |
- |
|
420 |
-func RestoreTerminal(state *term.State) { |
|
421 |
- term.Restore(int(os.Stdin.Fd()), state) |
|
422 |
-} |
|
423 |
- |
|
424 |
-func HashData(src io.Reader) (string, error) { |
|
425 |
- h := sha256.New() |
|
426 |
- if _, err := io.Copy(h, src); err != nil { |
|
427 |
- return "", err |
|
428 |
- } |
|
429 |
- return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil |
|
430 |
-} |
|
431 |
- |
|
432 |
-type KernelVersionInfo struct { |
|
433 |
- Kernel int |
|
434 |
- Major int |
|
435 |
- Minor int |
|
436 |
- Flavor string |
|
437 |
-} |
|
438 |
- |
|
439 |
-// FIXME: this doens't build on Darwin |
|
440 |
-func GetKernelVersion() (*KernelVersionInfo, error) { |
|
441 |
- return getKernelVersion() |
|
442 |
-} |
|
443 |
- |
|
444 |
-func (k *KernelVersionInfo) String() string { |
|
445 |
- flavor := "" |
|
446 |
- if len(k.Flavor) > 0 { |
|
447 |
- flavor = fmt.Sprintf("-%s", k.Flavor) |
|
448 |
- } |
|
449 |
- return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, flavor) |
|
450 |
-} |
|
451 |
- |
|
452 |
-// Compare two KernelVersionInfo struct. |
|
453 |
-// Returns -1 if a < b, = if a == b, 1 it a > b |
|
454 |
-func CompareKernelVersion(a, b *KernelVersionInfo) int { |
|
455 |
- if a.Kernel < b.Kernel { |
|
456 |
- return -1 |
|
457 |
- } else if a.Kernel > b.Kernel { |
|
458 |
- return 1 |
|
459 |
- } |
|
460 |
- |
|
461 |
- if a.Major < b.Major { |
|
462 |
- return -1 |
|
463 |
- } else if a.Major > b.Major { |
|
464 |
- return 1 |
|
465 |
- } |
|
466 |
- |
|
467 |
- if a.Minor < b.Minor { |
|
468 |
- return -1 |
|
469 |
- } else if a.Minor > b.Minor { |
|
470 |
- return 1 |
|
471 |
- } |
|
472 |
- |
|
473 |
- return 0 |
|
474 |
-} |
|
475 |
- |
|
476 |
-func FindCgroupMountpoint(cgroupType string) (string, error) { |
|
477 |
- output, err := ioutil.ReadFile("/proc/mounts") |
|
478 |
- if err != nil { |
|
479 |
- return "", err |
|
480 |
- } |
|
481 |
- |
|
482 |
- // /proc/mounts has 6 fields per line, one mount per line, e.g. |
|
483 |
- // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 |
|
484 |
- for _, line := range strings.Split(string(output), "\n") { |
|
485 |
- parts := strings.Split(line, " ") |
|
486 |
- if len(parts) == 6 && parts[2] == "cgroup" { |
|
487 |
- for _, opt := range strings.Split(parts[3], ",") { |
|
488 |
- if opt == cgroupType { |
|
489 |
- return parts[1], nil |
|
490 |
- } |
|
491 |
- } |
|
492 |
- } |
|
493 |
- } |
|
494 |
- |
|
495 |
- return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) |
|
496 |
-} |
|
497 |
- |
|
498 | 7 |
// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields |
499 | 8 |
// If OpenStdin is set, then it differs |
500 | 9 |
func CompareConfig(a, b *Config) bool { |
... | ... |
@@ -542,3 +51,7 @@ func CompareConfig(a, b *Config) bool { |
542 | 542 |
|
543 | 543 |
return true |
544 | 544 |
} |
545 |
+ |
|
546 |
+func GetKernelVersion() (*utils.KernelVersionInfo, error) { |
|
547 |
+ return getKernelVersion() |
|
548 |
+} |
545 | 549 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,470 @@ |
0 |
+package utils |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "bytes" |
|
4 |
+ "crypto/sha256" |
|
5 |
+ "encoding/hex" |
|
6 |
+ "errors" |
|
7 |
+ "fmt" |
|
8 |
+ "index/suffixarray" |
|
9 |
+ "io" |
|
10 |
+ "io/ioutil" |
|
11 |
+ "net/http" |
|
12 |
+ "os" |
|
13 |
+ "os/exec" |
|
14 |
+ "path/filepath" |
|
15 |
+ "runtime" |
|
16 |
+ "strings" |
|
17 |
+ "sync" |
|
18 |
+ "time" |
|
19 |
+) |
|
20 |
+ |
|
21 |
+// Go is a basic promise implementation: it wraps calls a function in a goroutine, |
|
22 |
+// and returns a channel which will later return the function's return value. |
|
23 |
+func Go(f func() error) chan error { |
|
24 |
+ ch := make(chan error) |
|
25 |
+ go func() { |
|
26 |
+ ch <- f() |
|
27 |
+ }() |
|
28 |
+ return ch |
|
29 |
+} |
|
30 |
+ |
|
31 |
+// Request a given URL and return an io.Reader |
|
32 |
+func Download(url string, stderr io.Writer) (*http.Response, error) { |
|
33 |
+ var resp *http.Response |
|
34 |
+ var err error = nil |
|
35 |
+ if resp, err = http.Get(url); err != nil { |
|
36 |
+ return nil, err |
|
37 |
+ } |
|
38 |
+ if resp.StatusCode >= 400 { |
|
39 |
+ return nil, errors.New("Got HTTP status code >= 400: " + resp.Status) |
|
40 |
+ } |
|
41 |
+ return resp, nil |
|
42 |
+} |
|
43 |
+ |
|
44 |
+// Debug function, if the debug flag is set, then display. Do nothing otherwise |
|
45 |
+// If Docker is in damon mode, also send the debug info on the socket |
|
46 |
+func Debugf(format string, a ...interface{}) { |
|
47 |
+ if os.Getenv("DEBUG") != "" { |
|
48 |
+ |
|
49 |
+ // Retrieve the stack infos |
|
50 |
+ _, file, line, ok := runtime.Caller(1) |
|
51 |
+ if !ok { |
|
52 |
+ file = "<unknown>" |
|
53 |
+ line = -1 |
|
54 |
+ } else { |
|
55 |
+ file = file[strings.LastIndex(file, "/")+1:] |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ fmt.Fprintf(os.Stderr, fmt.Sprintf("[debug] %s:%d %s\n", file, line, format), a...) |
|
59 |
+ } |
|
60 |
+} |
|
61 |
+ |
|
62 |
+// Reader with progress bar |
|
63 |
+type progressReader struct { |
|
64 |
+ reader io.ReadCloser // Stream to read from |
|
65 |
+ output io.Writer // Where to send progress bar to |
|
66 |
+ readTotal int // Expected stream length (bytes) |
|
67 |
+ readProgress int // How much has been read so far (bytes) |
|
68 |
+ lastUpdate int // How many bytes read at least update |
|
69 |
+ template string // Template to print. Default "%v/%v (%v)" |
|
70 |
+} |
|
71 |
+ |
|
72 |
+func (r *progressReader) Read(p []byte) (n int, err error) { |
|
73 |
+ read, err := io.ReadCloser(r.reader).Read(p) |
|
74 |
+ r.readProgress += read |
|
75 |
+ |
|
76 |
+ updateEvery := 4096 |
|
77 |
+ if r.readTotal > 0 { |
|
78 |
+ // Only update progress for every 1% read |
|
79 |
+ if increment := int(0.01 * float64(r.readTotal)); increment > updateEvery { |
|
80 |
+ updateEvery = increment |
|
81 |
+ } |
|
82 |
+ } |
|
83 |
+ if r.readProgress-r.lastUpdate > updateEvery || err != nil { |
|
84 |
+ if r.readTotal > 0 { |
|
85 |
+ fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) |
|
86 |
+ } else { |
|
87 |
+ fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a") |
|
88 |
+ } |
|
89 |
+ r.lastUpdate = r.readProgress |
|
90 |
+ } |
|
91 |
+ // Send newline when complete |
|
92 |
+ if err != nil { |
|
93 |
+ fmt.Fprintf(r.output, "\n") |
|
94 |
+ } |
|
95 |
+ |
|
96 |
+ return read, err |
|
97 |
+} |
|
98 |
+func (r *progressReader) Close() error { |
|
99 |
+ return io.ReadCloser(r.reader).Close() |
|
100 |
+} |
|
101 |
+func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader { |
|
102 |
+ if template == "" { |
|
103 |
+ template = "%v/%v (%v)" |
|
104 |
+ } |
|
105 |
+ return &progressReader{r, output, size, 0, 0, template} |
|
106 |
+} |
|
107 |
+ |
|
108 |
+// HumanDuration returns a human-readable approximation of a duration |
|
109 |
+// (eg. "About a minute", "4 hours ago", etc.) |
|
110 |
+func HumanDuration(d time.Duration) string { |
|
111 |
+ if seconds := int(d.Seconds()); seconds < 1 { |
|
112 |
+ return "Less than a second" |
|
113 |
+ } else if seconds < 60 { |
|
114 |
+ return fmt.Sprintf("%d seconds", seconds) |
|
115 |
+ } else if minutes := int(d.Minutes()); minutes == 1 { |
|
116 |
+ return "About a minute" |
|
117 |
+ } else if minutes < 60 { |
|
118 |
+ return fmt.Sprintf("%d minutes", minutes) |
|
119 |
+ } else if hours := int(d.Hours()); hours == 1 { |
|
120 |
+ return "About an hour" |
|
121 |
+ } else if hours < 48 { |
|
122 |
+ return fmt.Sprintf("%d hours", hours) |
|
123 |
+ } else if hours < 24*7*2 { |
|
124 |
+ return fmt.Sprintf("%d days", hours/24) |
|
125 |
+ } else if hours < 24*30*3 { |
|
126 |
+ return fmt.Sprintf("%d weeks", hours/24/7) |
|
127 |
+ } else if hours < 24*365*2 { |
|
128 |
+ return fmt.Sprintf("%d months", hours/24/30) |
|
129 |
+ } |
|
130 |
+ return fmt.Sprintf("%d years", d.Hours()/24/365) |
|
131 |
+} |
|
132 |
+ |
|
133 |
+func Trunc(s string, maxlen int) string { |
|
134 |
+ if len(s) <= maxlen { |
|
135 |
+ return s |
|
136 |
+ } |
|
137 |
+ return s[:maxlen] |
|
138 |
+} |
|
139 |
+ |
|
140 |
+// Figure out the absolute path of our own binary |
|
141 |
+func SelfPath() string { |
|
142 |
+ path, err := exec.LookPath(os.Args[0]) |
|
143 |
+ if err != nil { |
|
144 |
+ panic(err) |
|
145 |
+ } |
|
146 |
+ path, err = filepath.Abs(path) |
|
147 |
+ if err != nil { |
|
148 |
+ panic(err) |
|
149 |
+ } |
|
150 |
+ return path |
|
151 |
+} |
|
152 |
+ |
|
153 |
+type nopWriter struct { |
|
154 |
+} |
|
155 |
+ |
|
156 |
+func (w *nopWriter) Write(buf []byte) (int, error) { |
|
157 |
+ return len(buf), nil |
|
158 |
+} |
|
159 |
+ |
|
160 |
+type nopWriteCloser struct { |
|
161 |
+ io.Writer |
|
162 |
+} |
|
163 |
+ |
|
164 |
+func (w *nopWriteCloser) Close() error { return nil } |
|
165 |
+ |
|
166 |
+func NopWriteCloser(w io.Writer) io.WriteCloser { |
|
167 |
+ return &nopWriteCloser{w} |
|
168 |
+} |
|
169 |
+ |
|
170 |
+type bufReader struct { |
|
171 |
+ buf *bytes.Buffer |
|
172 |
+ reader io.Reader |
|
173 |
+ err error |
|
174 |
+ l sync.Mutex |
|
175 |
+ wait sync.Cond |
|
176 |
+} |
|
177 |
+ |
|
178 |
+func NewBufReader(r io.Reader) *bufReader { |
|
179 |
+ reader := &bufReader{ |
|
180 |
+ buf: &bytes.Buffer{}, |
|
181 |
+ reader: r, |
|
182 |
+ } |
|
183 |
+ reader.wait.L = &reader.l |
|
184 |
+ go reader.drain() |
|
185 |
+ return reader |
|
186 |
+} |
|
187 |
+ |
|
188 |
+func (r *bufReader) drain() { |
|
189 |
+ buf := make([]byte, 1024) |
|
190 |
+ for { |
|
191 |
+ n, err := r.reader.Read(buf) |
|
192 |
+ r.l.Lock() |
|
193 |
+ if err != nil { |
|
194 |
+ r.err = err |
|
195 |
+ } else { |
|
196 |
+ r.buf.Write(buf[0:n]) |
|
197 |
+ } |
|
198 |
+ r.wait.Signal() |
|
199 |
+ r.l.Unlock() |
|
200 |
+ if err != nil { |
|
201 |
+ break |
|
202 |
+ } |
|
203 |
+ } |
|
204 |
+} |
|
205 |
+ |
|
206 |
+func (r *bufReader) Read(p []byte) (n int, err error) { |
|
207 |
+ r.l.Lock() |
|
208 |
+ defer r.l.Unlock() |
|
209 |
+ for { |
|
210 |
+ n, err = r.buf.Read(p) |
|
211 |
+ if n > 0 { |
|
212 |
+ return n, err |
|
213 |
+ } |
|
214 |
+ if r.err != nil { |
|
215 |
+ return 0, r.err |
|
216 |
+ } |
|
217 |
+ r.wait.Wait() |
|
218 |
+ } |
|
219 |
+ panic("unreachable") |
|
220 |
+} |
|
221 |
+ |
|
222 |
+func (r *bufReader) Close() error { |
|
223 |
+ closer, ok := r.reader.(io.ReadCloser) |
|
224 |
+ if !ok { |
|
225 |
+ return nil |
|
226 |
+ } |
|
227 |
+ return closer.Close() |
|
228 |
+} |
|
229 |
+ |
|
230 |
+type WriteBroadcaster struct { |
|
231 |
+ mu sync.Mutex |
|
232 |
+ writers map[io.WriteCloser]struct{} |
|
233 |
+} |
|
234 |
+ |
|
235 |
+func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser) { |
|
236 |
+ w.mu.Lock() |
|
237 |
+ w.writers[writer] = struct{}{} |
|
238 |
+ w.mu.Unlock() |
|
239 |
+} |
|
240 |
+ |
|
241 |
+// FIXME: Is that function used? |
|
242 |
+// FIXME: This relies on the concrete writer type used having equality operator |
|
243 |
+func (w *WriteBroadcaster) RemoveWriter(writer io.WriteCloser) { |
|
244 |
+ w.mu.Lock() |
|
245 |
+ delete(w.writers, writer) |
|
246 |
+ w.mu.Unlock() |
|
247 |
+} |
|
248 |
+ |
|
249 |
+func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { |
|
250 |
+ w.mu.Lock() |
|
251 |
+ defer w.mu.Unlock() |
|
252 |
+ for writer := range w.writers { |
|
253 |
+ if n, err := writer.Write(p); err != nil || n != len(p) { |
|
254 |
+ // On error, evict the writer |
|
255 |
+ delete(w.writers, writer) |
|
256 |
+ } |
|
257 |
+ } |
|
258 |
+ return len(p), nil |
|
259 |
+} |
|
260 |
+ |
|
261 |
+func (w *WriteBroadcaster) CloseWriters() error { |
|
262 |
+ w.mu.Lock() |
|
263 |
+ defer w.mu.Unlock() |
|
264 |
+ for writer := range w.writers { |
|
265 |
+ writer.Close() |
|
266 |
+ } |
|
267 |
+ w.writers = make(map[io.WriteCloser]struct{}) |
|
268 |
+ return nil |
|
269 |
+} |
|
270 |
+ |
|
271 |
+func NewWriteBroadcaster() *WriteBroadcaster { |
|
272 |
+ return &WriteBroadcaster{writers: make(map[io.WriteCloser]struct{})} |
|
273 |
+} |
|
274 |
+ |
|
275 |
+func GetTotalUsedFds() int { |
|
276 |
+ if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { |
|
277 |
+ Debugf("Error opening /proc/%d/fd: %s", os.Getpid(), err) |
|
278 |
+ } else { |
|
279 |
+ return len(fds) |
|
280 |
+ } |
|
281 |
+ return -1 |
|
282 |
+} |
|
283 |
+ |
|
284 |
+// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. |
|
285 |
+// This is used to retrieve image and container IDs by more convenient shorthand prefixes. |
|
286 |
+type TruncIndex struct { |
|
287 |
+ index *suffixarray.Index |
|
288 |
+ ids map[string]bool |
|
289 |
+ bytes []byte |
|
290 |
+} |
|
291 |
+ |
|
292 |
+func NewTruncIndex() *TruncIndex { |
|
293 |
+ return &TruncIndex{ |
|
294 |
+ index: suffixarray.New([]byte{' '}), |
|
295 |
+ ids: make(map[string]bool), |
|
296 |
+ bytes: []byte{' '}, |
|
297 |
+ } |
|
298 |
+} |
|
299 |
+ |
|
300 |
+func (idx *TruncIndex) Add(id string) error { |
|
301 |
+ if strings.Contains(id, " ") { |
|
302 |
+ return fmt.Errorf("Illegal character: ' '") |
|
303 |
+ } |
|
304 |
+ if _, exists := idx.ids[id]; exists { |
|
305 |
+ return fmt.Errorf("Id already exists: %s", id) |
|
306 |
+ } |
|
307 |
+ idx.ids[id] = true |
|
308 |
+ idx.bytes = append(idx.bytes, []byte(id+" ")...) |
|
309 |
+ idx.index = suffixarray.New(idx.bytes) |
|
310 |
+ return nil |
|
311 |
+} |
|
312 |
+ |
|
313 |
+func (idx *TruncIndex) Delete(id string) error { |
|
314 |
+ if _, exists := idx.ids[id]; !exists { |
|
315 |
+ return fmt.Errorf("No such id: %s", id) |
|
316 |
+ } |
|
317 |
+ before, after, err := idx.lookup(id) |
|
318 |
+ if err != nil { |
|
319 |
+ return err |
|
320 |
+ } |
|
321 |
+ delete(idx.ids, id) |
|
322 |
+ idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...) |
|
323 |
+ idx.index = suffixarray.New(idx.bytes) |
|
324 |
+ return nil |
|
325 |
+} |
|
326 |
+ |
|
327 |
+func (idx *TruncIndex) lookup(s string) (int, int, error) { |
|
328 |
+ offsets := idx.index.Lookup([]byte(" "+s), -1) |
|
329 |
+ //log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes()) |
|
330 |
+ if offsets == nil || len(offsets) == 0 || len(offsets) > 1 { |
|
331 |
+ return -1, -1, fmt.Errorf("No such id: %s", s) |
|
332 |
+ } |
|
333 |
+ offsetBefore := offsets[0] + 1 |
|
334 |
+ offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ") |
|
335 |
+ return offsetBefore, offsetAfter, nil |
|
336 |
+} |
|
337 |
+ |
|
338 |
+func (idx *TruncIndex) Get(s string) (string, error) { |
|
339 |
+ before, after, err := idx.lookup(s) |
|
340 |
+ //log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after) |
|
341 |
+ if err != nil { |
|
342 |
+ return "", err |
|
343 |
+ } |
|
344 |
+ return string(idx.bytes[before:after]), err |
|
345 |
+} |
|
346 |
+ |
|
347 |
+// TruncateId returns a shorthand version of a string identifier for convenience. |
|
348 |
+// A collision with other shorthands is very unlikely, but possible. |
|
349 |
+// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller |
|
350 |
+// will need to use a langer prefix, or the full-length Id. |
|
351 |
+func TruncateId(id string) string { |
|
352 |
+ shortLen := 12 |
|
353 |
+ if len(id) < shortLen { |
|
354 |
+ shortLen = len(id) |
|
355 |
+ } |
|
356 |
+ return id[:shortLen] |
|
357 |
+} |
|
358 |
+ |
|
359 |
+// Code c/c from io.Copy() modified to handle escape sequence |
|
360 |
+func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) { |
|
361 |
+ buf := make([]byte, 32*1024) |
|
362 |
+ for { |
|
363 |
+ nr, er := src.Read(buf) |
|
364 |
+ if nr > 0 { |
|
365 |
+ // ---- Docker addition |
|
366 |
+ // char 16 is C-p |
|
367 |
+ if nr == 1 && buf[0] == 16 { |
|
368 |
+ nr, er = src.Read(buf) |
|
369 |
+ // char 17 is C-q |
|
370 |
+ if nr == 1 && buf[0] == 17 { |
|
371 |
+ if err := src.Close(); err != nil { |
|
372 |
+ return 0, err |
|
373 |
+ } |
|
374 |
+ return 0, io.EOF |
|
375 |
+ } |
|
376 |
+ } |
|
377 |
+ // ---- End of docker |
|
378 |
+ nw, ew := dst.Write(buf[0:nr]) |
|
379 |
+ if nw > 0 { |
|
380 |
+ written += int64(nw) |
|
381 |
+ } |
|
382 |
+ if ew != nil { |
|
383 |
+ err = ew |
|
384 |
+ break |
|
385 |
+ } |
|
386 |
+ if nr != nw { |
|
387 |
+ err = io.ErrShortWrite |
|
388 |
+ break |
|
389 |
+ } |
|
390 |
+ } |
|
391 |
+ if er == io.EOF { |
|
392 |
+ break |
|
393 |
+ } |
|
394 |
+ if er != nil { |
|
395 |
+ err = er |
|
396 |
+ break |
|
397 |
+ } |
|
398 |
+ } |
|
399 |
+ return written, err |
|
400 |
+} |
|
401 |
+ |
|
402 |
+func HashData(src io.Reader) (string, error) { |
|
403 |
+ h := sha256.New() |
|
404 |
+ if _, err := io.Copy(h, src); err != nil { |
|
405 |
+ return "", err |
|
406 |
+ } |
|
407 |
+ return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil |
|
408 |
+} |
|
409 |
+ |
|
410 |
+type KernelVersionInfo struct { |
|
411 |
+ Kernel int |
|
412 |
+ Major int |
|
413 |
+ Minor int |
|
414 |
+ Flavor string |
|
415 |
+} |
|
416 |
+ |
|
417 |
+func (k *KernelVersionInfo) String() string { |
|
418 |
+ flavor := "" |
|
419 |
+ if len(k.Flavor) > 0 { |
|
420 |
+ flavor = fmt.Sprintf("-%s", k.Flavor) |
|
421 |
+ } |
|
422 |
+ return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, flavor) |
|
423 |
+} |
|
424 |
+ |
|
425 |
+// Compare two KernelVersionInfo struct. |
|
426 |
+// Returns -1 if a < b, = if a == b, 1 it a > b |
|
427 |
+func CompareKernelVersion(a, b *KernelVersionInfo) int { |
|
428 |
+ if a.Kernel < b.Kernel { |
|
429 |
+ return -1 |
|
430 |
+ } else if a.Kernel > b.Kernel { |
|
431 |
+ return 1 |
|
432 |
+ } |
|
433 |
+ |
|
434 |
+ if a.Major < b.Major { |
|
435 |
+ return -1 |
|
436 |
+ } else if a.Major > b.Major { |
|
437 |
+ return 1 |
|
438 |
+ } |
|
439 |
+ |
|
440 |
+ if a.Minor < b.Minor { |
|
441 |
+ return -1 |
|
442 |
+ } else if a.Minor > b.Minor { |
|
443 |
+ return 1 |
|
444 |
+ } |
|
445 |
+ |
|
446 |
+ return 0 |
|
447 |
+} |
|
448 |
+ |
|
449 |
+func FindCgroupMountpoint(cgroupType string) (string, error) { |
|
450 |
+ output, err := ioutil.ReadFile("/proc/mounts") |
|
451 |
+ if err != nil { |
|
452 |
+ return "", err |
|
453 |
+ } |
|
454 |
+ |
|
455 |
+ // /proc/mounts has 6 fields per line, one mount per line, e.g. |
|
456 |
+ // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 |
|
457 |
+ for _, line := range strings.Split(string(output), "\n") { |
|
458 |
+ parts := strings.Split(line, " ") |
|
459 |
+ if len(parts) == 6 && parts[2] == "cgroup" { |
|
460 |
+ for _, opt := range strings.Split(parts[3], ",") { |
|
461 |
+ if opt == cgroupType { |
|
462 |
+ return parts[1], nil |
|
463 |
+ } |
|
464 |
+ } |
|
465 |
+ } |
|
466 |
+ } |
|
467 |
+ |
|
468 |
+ return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) |
|
469 |
+} |
2 | 472 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,263 @@ |
0 |
+package utils |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "bytes" |
|
4 |
+ "errors" |
|
5 |
+ "io" |
|
6 |
+ "io/ioutil" |
|
7 |
+ "testing" |
|
8 |
+) |
|
9 |
+ |
|
10 |
+func TestBufReader(t *testing.T) { |
|
11 |
+ reader, writer := io.Pipe() |
|
12 |
+ bufreader := newBufReader(reader) |
|
13 |
+ |
|
14 |
+ // Write everything down to a Pipe |
|
15 |
+ // Usually, a pipe should block but because of the buffered reader, |
|
16 |
+ // the writes will go through |
|
17 |
+ done := make(chan bool) |
|
18 |
+ go func() { |
|
19 |
+ writer.Write([]byte("hello world")) |
|
20 |
+ writer.Close() |
|
21 |
+ done <- true |
|
22 |
+ }() |
|
23 |
+ |
|
24 |
+ // Drain the reader *after* everything has been written, just to verify |
|
25 |
+ // it is indeed buffering |
|
26 |
+ <-done |
|
27 |
+ output, err := ioutil.ReadAll(bufreader) |
|
28 |
+ if err != nil { |
|
29 |
+ t.Fatal(err) |
|
30 |
+ } |
|
31 |
+ if !bytes.Equal(output, []byte("hello world")) { |
|
32 |
+ t.Error(string(output)) |
|
33 |
+ } |
|
34 |
+} |
|
35 |
+ |
|
36 |
+type dummyWriter struct { |
|
37 |
+ buffer bytes.Buffer |
|
38 |
+ failOnWrite bool |
|
39 |
+} |
|
40 |
+ |
|
41 |
+func (dw *dummyWriter) Write(p []byte) (n int, err error) { |
|
42 |
+ if dw.failOnWrite { |
|
43 |
+ return 0, errors.New("Fake fail") |
|
44 |
+ } |
|
45 |
+ return dw.buffer.Write(p) |
|
46 |
+} |
|
47 |
+ |
|
48 |
+func (dw *dummyWriter) String() string { |
|
49 |
+ return dw.buffer.String() |
|
50 |
+} |
|
51 |
+ |
|
52 |
+func (dw *dummyWriter) Close() error { |
|
53 |
+ return nil |
|
54 |
+} |
|
55 |
+ |
|
56 |
+func TestWriteBroadcaster(t *testing.T) { |
|
57 |
+ writer := newWriteBroadcaster() |
|
58 |
+ |
|
59 |
+ // Test 1: Both bufferA and bufferB should contain "foo" |
|
60 |
+ bufferA := &dummyWriter{} |
|
61 |
+ writer.AddWriter(bufferA) |
|
62 |
+ bufferB := &dummyWriter{} |
|
63 |
+ writer.AddWriter(bufferB) |
|
64 |
+ writer.Write([]byte("foo")) |
|
65 |
+ |
|
66 |
+ if bufferA.String() != "foo" { |
|
67 |
+ t.Errorf("Buffer contains %v", bufferA.String()) |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ if bufferB.String() != "foo" { |
|
71 |
+ t.Errorf("Buffer contains %v", bufferB.String()) |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ // Test2: bufferA and bufferB should contain "foobar", |
|
75 |
+ // while bufferC should only contain "bar" |
|
76 |
+ bufferC := &dummyWriter{} |
|
77 |
+ writer.AddWriter(bufferC) |
|
78 |
+ writer.Write([]byte("bar")) |
|
79 |
+ |
|
80 |
+ if bufferA.String() != "foobar" { |
|
81 |
+ t.Errorf("Buffer contains %v", bufferA.String()) |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ if bufferB.String() != "foobar" { |
|
85 |
+ t.Errorf("Buffer contains %v", bufferB.String()) |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ if bufferC.String() != "bar" { |
|
89 |
+ t.Errorf("Buffer contains %v", bufferC.String()) |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ // Test3: Test removal |
|
93 |
+ writer.RemoveWriter(bufferB) |
|
94 |
+ writer.Write([]byte("42")) |
|
95 |
+ if bufferA.String() != "foobar42" { |
|
96 |
+ t.Errorf("Buffer contains %v", bufferA.String()) |
|
97 |
+ } |
|
98 |
+ if bufferB.String() != "foobar" { |
|
99 |
+ t.Errorf("Buffer contains %v", bufferB.String()) |
|
100 |
+ } |
|
101 |
+ if bufferC.String() != "bar42" { |
|
102 |
+ t.Errorf("Buffer contains %v", bufferC.String()) |
|
103 |
+ } |
|
104 |
+ |
|
105 |
+ // Test4: Test eviction on failure |
|
106 |
+ bufferA.failOnWrite = true |
|
107 |
+ writer.Write([]byte("fail")) |
|
108 |
+ if bufferA.String() != "foobar42" { |
|
109 |
+ t.Errorf("Buffer contains %v", bufferA.String()) |
|
110 |
+ } |
|
111 |
+ if bufferC.String() != "bar42fail" { |
|
112 |
+ t.Errorf("Buffer contains %v", bufferC.String()) |
|
113 |
+ } |
|
114 |
+ // Even though we reset the flag, no more writes should go in there |
|
115 |
+ bufferA.failOnWrite = false |
|
116 |
+ writer.Write([]byte("test")) |
|
117 |
+ if bufferA.String() != "foobar42" { |
|
118 |
+ t.Errorf("Buffer contains %v", bufferA.String()) |
|
119 |
+ } |
|
120 |
+ if bufferC.String() != "bar42failtest" { |
|
121 |
+ t.Errorf("Buffer contains %v", bufferC.String()) |
|
122 |
+ } |
|
123 |
+ |
|
124 |
+ writer.CloseWriters() |
|
125 |
+} |
|
126 |
+ |
|
127 |
+type devNullCloser int |
|
128 |
+ |
|
129 |
+func (d devNullCloser) Close() error { |
|
130 |
+ return nil |
|
131 |
+} |
|
132 |
+ |
|
133 |
+func (d devNullCloser) Write(buf []byte) (int, error) { |
|
134 |
+ return len(buf), nil |
|
135 |
+} |
|
136 |
+ |
|
137 |
+// This test checks for races. It is only useful when run with the race detector. |
|
138 |
+func TestRaceWriteBroadcaster(t *testing.T) { |
|
139 |
+ writer := newWriteBroadcaster() |
|
140 |
+ c := make(chan bool) |
|
141 |
+ go func() { |
|
142 |
+ writer.AddWriter(devNullCloser(0)) |
|
143 |
+ c <- true |
|
144 |
+ }() |
|
145 |
+ writer.Write([]byte("hello")) |
|
146 |
+ <-c |
|
147 |
+} |
|
148 |
+ |
|
149 |
+// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix. |
|
150 |
+func TestTruncIndex(t *testing.T) { |
|
151 |
+ index := NewTruncIndex() |
|
152 |
+ // Get on an empty index |
|
153 |
+ if _, err := index.Get("foobar"); err == nil { |
|
154 |
+ t.Fatal("Get on an empty index should return an error") |
|
155 |
+ } |
|
156 |
+ |
|
157 |
+ // Spaces should be illegal in an id |
|
158 |
+ if err := index.Add("I have a space"); err == nil { |
|
159 |
+ t.Fatalf("Adding an id with ' ' should return an error") |
|
160 |
+ } |
|
161 |
+ |
|
162 |
+ id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96" |
|
163 |
+ // Add an id |
|
164 |
+ if err := index.Add(id); err != nil { |
|
165 |
+ t.Fatal(err) |
|
166 |
+ } |
|
167 |
+ // Get a non-existing id |
|
168 |
+ assertIndexGet(t, index, "abracadabra", "", true) |
|
169 |
+ // Get the exact id |
|
170 |
+ assertIndexGet(t, index, id, id, false) |
|
171 |
+ // The first letter should match |
|
172 |
+ assertIndexGet(t, index, id[:1], id, false) |
|
173 |
+ // The first half should match |
|
174 |
+ assertIndexGet(t, index, id[:len(id)/2], id, false) |
|
175 |
+ // The second half should NOT match |
|
176 |
+ assertIndexGet(t, index, id[len(id)/2:], "", true) |
|
177 |
+ |
|
178 |
+ id2 := id[:6] + "blabla" |
|
179 |
+ // Add an id |
|
180 |
+ if err := index.Add(id2); err != nil { |
|
181 |
+ t.Fatal(err) |
|
182 |
+ } |
|
183 |
+ // Both exact IDs should work |
|
184 |
+ assertIndexGet(t, index, id, id, false) |
|
185 |
+ assertIndexGet(t, index, id2, id2, false) |
|
186 |
+ |
|
187 |
+ // 6 characters or less should conflict |
|
188 |
+ assertIndexGet(t, index, id[:6], "", true) |
|
189 |
+ assertIndexGet(t, index, id[:4], "", true) |
|
190 |
+ assertIndexGet(t, index, id[:1], "", true) |
|
191 |
+ |
|
192 |
+ // 7 characters should NOT conflict |
|
193 |
+ assertIndexGet(t, index, id[:7], id, false) |
|
194 |
+ assertIndexGet(t, index, id2[:7], id2, false) |
|
195 |
+ |
|
196 |
+ // Deleting a non-existing id should return an error |
|
197 |
+ if err := index.Delete("non-existing"); err == nil { |
|
198 |
+ t.Fatalf("Deleting a non-existing id should return an error") |
|
199 |
+ } |
|
200 |
+ |
|
201 |
+ // Deleting id2 should remove conflicts |
|
202 |
+ if err := index.Delete(id2); err != nil { |
|
203 |
+ t.Fatal(err) |
|
204 |
+ } |
|
205 |
+ // id2 should no longer work |
|
206 |
+ assertIndexGet(t, index, id2, "", true) |
|
207 |
+ assertIndexGet(t, index, id2[:7], "", true) |
|
208 |
+ assertIndexGet(t, index, id2[:11], "", true) |
|
209 |
+ |
|
210 |
+ // conflicts between id and id2 should be gone |
|
211 |
+ assertIndexGet(t, index, id[:6], id, false) |
|
212 |
+ assertIndexGet(t, index, id[:4], id, false) |
|
213 |
+ assertIndexGet(t, index, id[:1], id, false) |
|
214 |
+ |
|
215 |
+ // non-conflicting substrings should still not conflict |
|
216 |
+ assertIndexGet(t, index, id[:7], id, false) |
|
217 |
+ assertIndexGet(t, index, id[:15], id, false) |
|
218 |
+ assertIndexGet(t, index, id, id, false) |
|
219 |
+} |
|
220 |
+ |
|
221 |
+func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) { |
|
222 |
+ if result, err := index.Get(input); err != nil && !expectError { |
|
223 |
+ t.Fatalf("Unexpected error getting '%s': %s", input, err) |
|
224 |
+ } else if err == nil && expectError { |
|
225 |
+ t.Fatalf("Getting '%s' should return an error", input) |
|
226 |
+ } else if result != expectedResult { |
|
227 |
+ t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult) |
|
228 |
+ } |
|
229 |
+} |
|
230 |
+ |
|
231 |
+func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) { |
|
232 |
+ if r := CompareKernelVersion(a, b); r != result { |
|
233 |
+ t.Fatalf("Unepected kernel version comparaison result. Found %d, expected %d", r, result) |
|
234 |
+ } |
|
235 |
+} |
|
236 |
+ |
|
237 |
+func TestCompareKernelVersion(t *testing.T) { |
|
238 |
+ assertKernelVersion(t, |
|
239 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
240 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
241 |
+ 0) |
|
242 |
+ assertKernelVersion(t, |
|
243 |
+ &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, |
|
244 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
245 |
+ -1) |
|
246 |
+ assertKernelVersion(t, |
|
247 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
248 |
+ &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, |
|
249 |
+ 1) |
|
250 |
+ assertKernelVersion(t, |
|
251 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"}, |
|
252 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "16"}, |
|
253 |
+ 0) |
|
254 |
+ assertKernelVersion(t, |
|
255 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5}, |
|
256 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
257 |
+ 1) |
|
258 |
+ assertKernelVersion(t, |
|
259 |
+ &KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20, Flavor: "25"}, |
|
260 |
+ &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"}, |
|
261 |
+ -1) |
|
262 |
+} |
0 | 263 |
deleted file mode 100644 |
... | ... |
@@ -1,263 +0,0 @@ |
1 |
-package docker |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "bytes" |
|
5 |
- "errors" |
|
6 |
- "io" |
|
7 |
- "io/ioutil" |
|
8 |
- "testing" |
|
9 |
-) |
|
10 |
- |
|
11 |
-func TestBufReader(t *testing.T) { |
|
12 |
- reader, writer := io.Pipe() |
|
13 |
- bufreader := newBufReader(reader) |
|
14 |
- |
|
15 |
- // Write everything down to a Pipe |
|
16 |
- // Usually, a pipe should block but because of the buffered reader, |
|
17 |
- // the writes will go through |
|
18 |
- done := make(chan bool) |
|
19 |
- go func() { |
|
20 |
- writer.Write([]byte("hello world")) |
|
21 |
- writer.Close() |
|
22 |
- done <- true |
|
23 |
- }() |
|
24 |
- |
|
25 |
- // Drain the reader *after* everything has been written, just to verify |
|
26 |
- // it is indeed buffering |
|
27 |
- <-done |
|
28 |
- output, err := ioutil.ReadAll(bufreader) |
|
29 |
- if err != nil { |
|
30 |
- t.Fatal(err) |
|
31 |
- } |
|
32 |
- if !bytes.Equal(output, []byte("hello world")) { |
|
33 |
- t.Error(string(output)) |
|
34 |
- } |
|
35 |
-} |
|
36 |
- |
|
37 |
-type dummyWriter struct { |
|
38 |
- buffer bytes.Buffer |
|
39 |
- failOnWrite bool |
|
40 |
-} |
|
41 |
- |
|
42 |
-func (dw *dummyWriter) Write(p []byte) (n int, err error) { |
|
43 |
- if dw.failOnWrite { |
|
44 |
- return 0, errors.New("Fake fail") |
|
45 |
- } |
|
46 |
- return dw.buffer.Write(p) |
|
47 |
-} |
|
48 |
- |
|
49 |
-func (dw *dummyWriter) String() string { |
|
50 |
- return dw.buffer.String() |
|
51 |
-} |
|
52 |
- |
|
53 |
-func (dw *dummyWriter) Close() error { |
|
54 |
- return nil |
|
55 |
-} |
|
56 |
- |
|
57 |
-func TestWriteBroadcaster(t *testing.T) { |
|
58 |
- writer := newWriteBroadcaster() |
|
59 |
- |
|
60 |
- // Test 1: Both bufferA and bufferB should contain "foo" |
|
61 |
- bufferA := &dummyWriter{} |
|
62 |
- writer.AddWriter(bufferA) |
|
63 |
- bufferB := &dummyWriter{} |
|
64 |
- writer.AddWriter(bufferB) |
|
65 |
- writer.Write([]byte("foo")) |
|
66 |
- |
|
67 |
- if bufferA.String() != "foo" { |
|
68 |
- t.Errorf("Buffer contains %v", bufferA.String()) |
|
69 |
- } |
|
70 |
- |
|
71 |
- if bufferB.String() != "foo" { |
|
72 |
- t.Errorf("Buffer contains %v", bufferB.String()) |
|
73 |
- } |
|
74 |
- |
|
75 |
- // Test2: bufferA and bufferB should contain "foobar", |
|
76 |
- // while bufferC should only contain "bar" |
|
77 |
- bufferC := &dummyWriter{} |
|
78 |
- writer.AddWriter(bufferC) |
|
79 |
- writer.Write([]byte("bar")) |
|
80 |
- |
|
81 |
- if bufferA.String() != "foobar" { |
|
82 |
- t.Errorf("Buffer contains %v", bufferA.String()) |
|
83 |
- } |
|
84 |
- |
|
85 |
- if bufferB.String() != "foobar" { |
|
86 |
- t.Errorf("Buffer contains %v", bufferB.String()) |
|
87 |
- } |
|
88 |
- |
|
89 |
- if bufferC.String() != "bar" { |
|
90 |
- t.Errorf("Buffer contains %v", bufferC.String()) |
|
91 |
- } |
|
92 |
- |
|
93 |
- // Test3: Test removal |
|
94 |
- writer.RemoveWriter(bufferB) |
|
95 |
- writer.Write([]byte("42")) |
|
96 |
- if bufferA.String() != "foobar42" { |
|
97 |
- t.Errorf("Buffer contains %v", bufferA.String()) |
|
98 |
- } |
|
99 |
- if bufferB.String() != "foobar" { |
|
100 |
- t.Errorf("Buffer contains %v", bufferB.String()) |
|
101 |
- } |
|
102 |
- if bufferC.String() != "bar42" { |
|
103 |
- t.Errorf("Buffer contains %v", bufferC.String()) |
|
104 |
- } |
|
105 |
- |
|
106 |
- // Test4: Test eviction on failure |
|
107 |
- bufferA.failOnWrite = true |
|
108 |
- writer.Write([]byte("fail")) |
|
109 |
- if bufferA.String() != "foobar42" { |
|
110 |
- t.Errorf("Buffer contains %v", bufferA.String()) |
|
111 |
- } |
|
112 |
- if bufferC.String() != "bar42fail" { |
|
113 |
- t.Errorf("Buffer contains %v", bufferC.String()) |
|
114 |
- } |
|
115 |
- // Even though we reset the flag, no more writes should go in there |
|
116 |
- bufferA.failOnWrite = false |
|
117 |
- writer.Write([]byte("test")) |
|
118 |
- if bufferA.String() != "foobar42" { |
|
119 |
- t.Errorf("Buffer contains %v", bufferA.String()) |
|
120 |
- } |
|
121 |
- if bufferC.String() != "bar42failtest" { |
|
122 |
- t.Errorf("Buffer contains %v", bufferC.String()) |
|
123 |
- } |
|
124 |
- |
|
125 |
- writer.CloseWriters() |
|
126 |
-} |
|
127 |
- |
|
128 |
-type devNullCloser int |
|
129 |
- |
|
130 |
-func (d devNullCloser) Close() error { |
|
131 |
- return nil |
|
132 |
-} |
|
133 |
- |
|
134 |
-func (d devNullCloser) Write(buf []byte) (int, error) { |
|
135 |
- return len(buf), nil |
|
136 |
-} |
|
137 |
- |
|
138 |
-// This test checks for races. It is only useful when run with the race detector. |
|
139 |
-func TestRaceWriteBroadcaster(t *testing.T) { |
|
140 |
- writer := newWriteBroadcaster() |
|
141 |
- c := make(chan bool) |
|
142 |
- go func() { |
|
143 |
- writer.AddWriter(devNullCloser(0)) |
|
144 |
- c <- true |
|
145 |
- }() |
|
146 |
- writer.Write([]byte("hello")) |
|
147 |
- <-c |
|
148 |
-} |
|
149 |
- |
|
150 |
-// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix. |
|
151 |
-func TestTruncIndex(t *testing.T) { |
|
152 |
- index := NewTruncIndex() |
|
153 |
- // Get on an empty index |
|
154 |
- if _, err := index.Get("foobar"); err == nil { |
|
155 |
- t.Fatal("Get on an empty index should return an error") |
|
156 |
- } |
|
157 |
- |
|
158 |
- // Spaces should be illegal in an id |
|
159 |
- if err := index.Add("I have a space"); err == nil { |
|
160 |
- t.Fatalf("Adding an id with ' ' should return an error") |
|
161 |
- } |
|
162 |
- |
|
163 |
- id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96" |
|
164 |
- // Add an id |
|
165 |
- if err := index.Add(id); err != nil { |
|
166 |
- t.Fatal(err) |
|
167 |
- } |
|
168 |
- // Get a non-existing id |
|
169 |
- assertIndexGet(t, index, "abracadabra", "", true) |
|
170 |
- // Get the exact id |
|
171 |
- assertIndexGet(t, index, id, id, false) |
|
172 |
- // The first letter should match |
|
173 |
- assertIndexGet(t, index, id[:1], id, false) |
|
174 |
- // The first half should match |
|
175 |
- assertIndexGet(t, index, id[:len(id)/2], id, false) |
|
176 |
- // The second half should NOT match |
|
177 |
- assertIndexGet(t, index, id[len(id)/2:], "", true) |
|
178 |
- |
|
179 |
- id2 := id[:6] + "blabla" |
|
180 |
- // Add an id |
|
181 |
- if err := index.Add(id2); err != nil { |
|
182 |
- t.Fatal(err) |
|
183 |
- } |
|
184 |
- // Both exact IDs should work |
|
185 |
- assertIndexGet(t, index, id, id, false) |
|
186 |
- assertIndexGet(t, index, id2, id2, false) |
|
187 |
- |
|
188 |
- // 6 characters or less should conflict |
|
189 |
- assertIndexGet(t, index, id[:6], "", true) |
|
190 |
- assertIndexGet(t, index, id[:4], "", true) |
|
191 |
- assertIndexGet(t, index, id[:1], "", true) |
|
192 |
- |
|
193 |
- // 7 characters should NOT conflict |
|
194 |
- assertIndexGet(t, index, id[:7], id, false) |
|
195 |
- assertIndexGet(t, index, id2[:7], id2, false) |
|
196 |
- |
|
197 |
- // Deleting a non-existing id should return an error |
|
198 |
- if err := index.Delete("non-existing"); err == nil { |
|
199 |
- t.Fatalf("Deleting a non-existing id should return an error") |
|
200 |
- } |
|
201 |
- |
|
202 |
- // Deleting id2 should remove conflicts |
|
203 |
- if err := index.Delete(id2); err != nil { |
|
204 |
- t.Fatal(err) |
|
205 |
- } |
|
206 |
- // id2 should no longer work |
|
207 |
- assertIndexGet(t, index, id2, "", true) |
|
208 |
- assertIndexGet(t, index, id2[:7], "", true) |
|
209 |
- assertIndexGet(t, index, id2[:11], "", true) |
|
210 |
- |
|
211 |
- // conflicts between id and id2 should be gone |
|
212 |
- assertIndexGet(t, index, id[:6], id, false) |
|
213 |
- assertIndexGet(t, index, id[:4], id, false) |
|
214 |
- assertIndexGet(t, index, id[:1], id, false) |
|
215 |
- |
|
216 |
- // non-conflicting substrings should still not conflict |
|
217 |
- assertIndexGet(t, index, id[:7], id, false) |
|
218 |
- assertIndexGet(t, index, id[:15], id, false) |
|
219 |
- assertIndexGet(t, index, id, id, false) |
|
220 |
-} |
|
221 |
- |
|
222 |
-func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) { |
|
223 |
- if result, err := index.Get(input); err != nil && !expectError { |
|
224 |
- t.Fatalf("Unexpected error getting '%s': %s", input, err) |
|
225 |
- } else if err == nil && expectError { |
|
226 |
- t.Fatalf("Getting '%s' should return an error", input) |
|
227 |
- } else if result != expectedResult { |
|
228 |
- t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult) |
|
229 |
- } |
|
230 |
-} |
|
231 |
- |
|
232 |
-func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) { |
|
233 |
- if r := CompareKernelVersion(a, b); r != result { |
|
234 |
- t.Fatalf("Unepected kernel version comparaison result. Found %d, expected %d", r, result) |
|
235 |
- } |
|
236 |
-} |
|
237 |
- |
|
238 |
-func TestCompareKernelVersion(t *testing.T) { |
|
239 |
- assertKernelVersion(t, |
|
240 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
241 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
242 |
- 0) |
|
243 |
- assertKernelVersion(t, |
|
244 |
- &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, |
|
245 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
246 |
- -1) |
|
247 |
- assertKernelVersion(t, |
|
248 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
249 |
- &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, |
|
250 |
- 1) |
|
251 |
- assertKernelVersion(t, |
|
252 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"}, |
|
253 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "16"}, |
|
254 |
- 0) |
|
255 |
- assertKernelVersion(t, |
|
256 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5}, |
|
257 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, |
|
258 |
- 1) |
|
259 |
- assertKernelVersion(t, |
|
260 |
- &KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20, Flavor: "25"}, |
|
261 |
- &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "0"}, |
|
262 |
- -1) |
|
263 |
-} |