Browse code

Prevent ContainerLogs from hanging if container doesn't run for long

Signed-off-by: Jim Minter <jminter@redhat.com>

Jim Minter authored on 2017/03/16 00:41:12
Showing 5 changed files
... ...
@@ -18,12 +18,13 @@ import (
18 18
 const name = "journald"
19 19
 
20 20
 type journald struct {
21
+	mu      sync.Mutex
21 22
 	vars    map[string]string // additional variables and values to send to the journal along with the log message
22 23
 	readers readerList
24
+	closed  bool
23 25
 }
24 26
 
25 27
 type readerList struct {
26
-	mu      sync.Mutex
27 28
 	readers map[*logger.LogWatcher]*logger.LogWatcher
28 29
 }
29 30
 
... ...
@@ -161,11 +161,12 @@ import (
161 161
 )
162 162
 
163 163
 func (s *journald) Close() error {
164
-	s.readers.mu.Lock()
164
+	s.mu.Lock()
165
+	s.closed = true
165 166
 	for reader := range s.readers.readers {
166 167
 		reader.Close()
167 168
 	}
168
-	s.readers.mu.Unlock()
169
+	s.mu.Unlock()
169 170
 	return nil
170 171
 }
171 172
 
... ...
@@ -245,9 +246,16 @@ drain:
245 245
 }
246 246
 
247 247
 func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.ReadConfig, j *C.sd_journal, pfd [2]C.int, cursor *C.char) *C.char {
248
-	s.readers.mu.Lock()
248
+	s.mu.Lock()
249 249
 	s.readers.readers[logWatcher] = logWatcher
250
-	s.readers.mu.Unlock()
250
+	if s.closed {
251
+		// the journald Logger is closed, presumably because the container has been
252
+		// reset.  So we shouldn't follow, because we'll never be woken up.  But we
253
+		// should make one more drainJournal call to be sure we've got all the logs.
254
+		// Close pfd[1] so that one drainJournal happens, then cleanup, then return.
255
+		C.close(pfd[1])
256
+	}
257
+	s.mu.Unlock()
251 258
 
252 259
 	newCursor := make(chan *C.char)
253 260
 
... ...
@@ -274,22 +282,22 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re
274 274
 
275 275
 		// Clean up.
276 276
 		C.close(pfd[0])
277
-		s.readers.mu.Lock()
277
+		s.mu.Lock()
278 278
 		delete(s.readers.readers, logWatcher)
279
-		s.readers.mu.Unlock()
279
+		s.mu.Unlock()
280 280
 		close(logWatcher.Msg)
281 281
 		newCursor <- cursor
282 282
 	}()
283 283
 
284 284
 	// Wait until we're told to stop.
285 285
 	select {
286
+	case cursor = <-newCursor:
286 287
 	case <-logWatcher.WatchClose():
287 288
 		// Notify the other goroutine that its work is done.
288 289
 		C.close(pfd[1])
290
+		cursor = <-newCursor
289 291
 	}
290 292
 
291
-	cursor = <-newCursor
292
-
293 293
 	return cursor
294 294
 }
295 295
 
... ...
@@ -27,6 +27,7 @@ type JSONFileLogger struct {
27 27
 	mu      sync.Mutex
28 28
 	readers map[*logger.LogWatcher]struct{} // stores the active log followers
29 29
 	extra   []byte                          // json-encoded extra attributes
30
+	closed  bool
30 31
 }
31 32
 
32 33
 func init() {
... ...
@@ -142,6 +143,7 @@ func (l *JSONFileLogger) LogPath() string {
142 142
 // Close closes underlying file and signals all readers to stop.
143 143
 func (l *JSONFileLogger) Close() error {
144 144
 	l.mu.Lock()
145
+	l.closed = true
145 146
 	err := l.writer.Close()
146 147
 	for r := range l.readers {
147 148
 		r.Close()
... ...
@@ -88,10 +88,7 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R
88 88
 		}
89 89
 	}
90 90
 
91
-	if !config.Follow {
92
-		if err := latestFile.Close(); err != nil {
93
-			logrus.Errorf("Error closing file: %v", err)
94
-		}
91
+	if !config.Follow || l.closed {
95 92
 		l.mu.Unlock()
96 93
 		return
97 94
 	}
... ...
@@ -100,17 +97,18 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R
100 100
 		latestFile.Seek(0, os.SEEK_END)
101 101
 	}
102 102
 
103
+	notifyRotate := l.writer.NotifyRotate()
104
+	defer l.writer.NotifyRotateEvict(notifyRotate)
105
+
103 106
 	l.readers[logWatcher] = struct{}{}
107
+
104 108
 	l.mu.Unlock()
105 109
 
106
-	notifyRotate := l.writer.NotifyRotate()
107 110
 	followLogs(latestFile, logWatcher, notifyRotate, config.Since)
108 111
 
109 112
 	l.mu.Lock()
110 113
 	delete(l.readers, logWatcher)
111 114
 	l.mu.Unlock()
112
-
113
-	l.writer.NotifyRotateEvict(notifyRotate)
114 115
 }
115 116
 
116 117
 func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) {
... ...
@@ -62,7 +62,7 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c
62 62
 		return nil, logger.ErrReadLogsNotSupported
63 63
 	}
64 64
 
65
-	follow := config.Follow && container.IsRunning()
65
+	follow := config.Follow && !cLogCreated
66 66
 	tailLines, err := strconv.Atoi(config.Tail)
67 67
 	if err != nil {
68 68
 		tailLines = -1