19 // Test that CopyPipeToChildLog works even on lines longer than
20 // bufio.MaxScanTokenSize.
21 func TestCopyPipeToChildLogLongLines(t *testing.T) {
22 logger, logBuf := bufLogger()
24 pipeIn, pipeOut := io.Pipe()
25 copied := make(chan bool)
27 copyPipeToChildLog(pipeIn, logger)
31 sentBytes := make([]byte, bufio.MaxScanTokenSize+MaxLogLine+(1<<22))
33 pipeOut.Write([]byte("before\n"))
35 for i := range sentBytes {
36 // Some bytes that aren't newlines:
37 sentBytes[i] = byte((rand.Int() & 0xff) | 0x80)
39 sentBytes[len(sentBytes)-1] = '\n'
40 pipeOut.Write(sentBytes)
42 pipeOut.Write([]byte("after"))
46 if before, err := logBuf.ReadBytes('\n'); err != nil || string(before) != "before\n" {
47 t.Fatalf("\"before\n\" not received (got \"%s\", %s)", before, err)
50 var receivedBytes []byte
53 line, err := logBuf.ReadBytes('\n')
57 if len(line) >= 5 && string(line[0:5]) == "[...]" {
58 if receivedBytes == nil {
59 t.Fatal("Beginning of line reported as continuation")
63 if len(line) >= 6 && string(line[len(line)-6:]) == "[...]\n" {
64 line = line[:len(line)-6]
68 receivedBytes = append(receivedBytes, line...)
70 if bytes.Compare(receivedBytes, sentBytes) != 0 {
71 t.Fatalf("sent %d bytes, got %d different bytes", len(sentBytes), len(receivedBytes))
74 if after, err := logBuf.ReadBytes('\n'); err != nil || string(after) != "after\n" {
75 t.Fatalf("\"after\n\" not received (got \"%s\", %s)", after, err)
79 case <-time.After(time.Second):
86 func bufLogger() (*log.Logger, *bufio.Reader) {
88 logger := log.New(w, "", 0)
89 return logger, bufio.NewReader(r)
92 func TestSignalOnDeadPPID(t *testing.T) {
93 if !testDeadParent(t, 0) {
94 t.Fatal("child should still be alive after parent dies")
96 if testDeadParent(t, 15) {
97 t.Fatal("child should have been killed when parent died")
101 // testDeadParent returns true if crunchstat's child proc is still
102 // alive after its parent dies.
103 func testDeadParent(t *testing.T, signum int) bool {
105 var bin, childlockfile, parentlockfile *os.File
106 for _, f := range []**os.File{&bin, &childlockfile, &parentlockfile} {
107 *f, err = ioutil.TempFile("", "crunchstat_")
112 defer os.Remove((*f).Name())
116 err = exec.Command("go", "build", "-o", bin.Name()).Run()
121 err = syscall.Flock(int(parentlockfile.Fd()), syscall.LOCK_EX)
126 cmd := exec.Command("bash", "-c", `
128 "$BINFILE" -cgroup-root=/none -ppid-check-interval=10ms -signal-on-dead-ppid="$SIGNUM" bash -c '
131 flock --unlock "$CHILDLOCKFD"
135 flock --exclusive "$CHILDLOCKFD"
136 echo -n "$$" > "$CHILDLOCKFILE"
137 flock --unlock "$PARENTLOCKFD"
138 sleep 20 </dev/null >/dev/null 2>/dev/null &
143 # wait for inner bash to start, to ensure $BINFILE has seen this bash proc as its initial PPID
144 flock --exclusive "$PARENTLOCKFILE" true
146 cmd.Env = append(os.Environ(),
147 "SIGNUM="+fmt.Sprintf("%d", signum),
149 "PARENTLOCKFILE="+parentlockfile.Name(),
151 "CHILDLOCKFILE="+childlockfile.Name(),
152 "BINFILE="+bin.Name())
153 cmd.ExtraFiles = []*os.File{parentlockfile, childlockfile}
154 stderr, err := cmd.StderrPipe()
158 stdout, err := cmd.StdoutPipe()
165 var wg sync.WaitGroup
168 for _, rdr := range []io.ReadCloser{stderr, stdout} {
169 go func(rdr io.ReadCloser) {
171 buf := make([]byte, 1024)
173 n, err := rdr.Read(buf)
175 t.Logf("%s", buf[:n])
184 // Wait until inner bash process releases parentlockfile
185 // (which means it has locked childlockfile and written its
187 err = exec.Command("flock", "--exclusive", parentlockfile.Name(), "true").Run()
192 childDone := make(chan bool)
194 // Notify the main thread when the inner bash process
195 // releases its lock on childlockfile (which means
196 // either its sleep process ended or it received a
199 err = exec.Command("flock", "--exclusive", childlockfile.Name(), "true").Run()
203 t.Logf("child done after %s", time.Since(t0))
208 case <-time.After(500 * time.Millisecond):
209 // Inner bash process is still alive after the timeout
210 // period. Kill it now, so our stdout and stderr pipes
211 // can finish and we don't leave a mess of child procs
213 buf, err := ioutil.ReadFile(childlockfile.Name())
218 _, err = fmt.Sscanf(string(buf), "%d", &childPID)
222 child, err := os.FindProcess(childPID)
226 child.Signal(syscall.Signal(15))
230 // Inner bash process ended soon after its grandparent