From 84076bf95f485bccfd9e03108fb69a1f72b42918 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <rothberg@redhat.com>
Date: Thu, 9 Jul 2020 13:32:20 +0200
Subject: log API: add context to allow for cancelling

Add a `context.Context` to the log APIs to allow for cancelling
streaming (e.g., via `podman logs -f`).  This fixes issues for
the remote API where some go routines of the server will continue
writing and produce nothing but heat and waste CPU cycles.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
---
 pkg/api/handlers/compat/containers_logs.go | 76 +++++++++++++++---------------
 pkg/domain/infra/abi/containers.go         |  2 +-
 pkg/varlinkapi/containers.go               |  2 +-
 3 files changed, 39 insertions(+), 41 deletions(-)

(limited to 'pkg')

diff --git a/pkg/api/handlers/compat/containers_logs.go b/pkg/api/handlers/compat/containers_logs.go
index 8147f4d38..30ee030e8 100644
--- a/pkg/api/handlers/compat/containers_logs.go
+++ b/pkg/api/handlers/compat/containers_logs.go
@@ -92,7 +92,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
 	options.WaitGroup = &wg
 
 	logChannel := make(chan *logs.LogLine, tail+1)
-	if err := runtime.Log([]*libpod.Container{ctnr}, options, logChannel); err != nil {
+	if err := runtime.Log(r.Context(), []*libpod.Container{ctnr}, options, logChannel); err != nil {
 		utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain logs for Container '%s'", name))
 		return
 	}
@@ -105,50 +105,48 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
 
 	var frame strings.Builder
 	header := make([]byte, 8)
-	for ok := true; ok; ok = query.Follow {
-		for line := range logChannel {
-			if _, found := r.URL.Query()["until"]; found {
-				if line.Time.After(until) {
-					break
-				}
+	for line := range logChannel {
+		if _, found := r.URL.Query()["until"]; found {
+			if line.Time.After(until) {
+				break
 			}
+		}
 
-			// Reset buffer we're ready to loop again
-			frame.Reset()
-			switch line.Device {
-			case "stdout":
-				if !query.Stdout {
-					continue
-				}
-				header[0] = 1
-			case "stderr":
-				if !query.Stderr {
-					continue
-				}
-				header[0] = 2
-			default:
-				// Logging and moving on is the best we can do here. We may have already sent
-				// a Status and Content-Type to client therefore we can no longer report an error.
-				log.Infof("unknown Device type '%s' in log file from Container %s", line.Device, ctnr.ID())
+		// Reset buffer we're ready to loop again
+		frame.Reset()
+		switch line.Device {
+		case "stdout":
+			if !query.Stdout {
 				continue
 			}
-
-			if query.Timestamps {
-				frame.WriteString(line.Time.Format(time.RFC3339))
-				frame.WriteString(" ")
+			header[0] = 1
+		case "stderr":
+			if !query.Stderr {
+				continue
 			}
-			frame.WriteString(line.Msg)
+			header[0] = 2
+		default:
+			// Logging and moving on is the best we can do here. We may have already sent
+			// a Status and Content-Type to client therefore we can no longer report an error.
+			log.Infof("unknown Device type '%s' in log file from Container %s", line.Device, ctnr.ID())
+			continue
+		}
 
-			binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
-			if _, err := w.Write(header[0:8]); err != nil {
-				log.Errorf("unable to write log output header: %q", err)
-			}
-			if _, err := io.WriteString(w, frame.String()); err != nil {
-				log.Errorf("unable to write frame string: %q", err)
-			}
-			if flusher, ok := w.(http.Flusher); ok {
-				flusher.Flush()
-			}
+		if query.Timestamps {
+			frame.WriteString(line.Time.Format(time.RFC3339))
+			frame.WriteString(" ")
+		}
+		frame.WriteString(line.Msg)
+
+		binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
+		if _, err := w.Write(header[0:8]); err != nil {
+			log.Errorf("unable to write log output header: %q", err)
+		}
+		if _, err := io.WriteString(w, frame.String()); err != nil {
+			log.Errorf("unable to write frame string: %q", err)
+		}
+		if flusher, ok := w.(http.Flusher); ok {
+			flusher.Flush()
 		}
 	}
 }
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 596fc2cc1..8909f831d 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -924,7 +924,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
 	}
 	logChannel := make(chan *logs.LogLine, chSize)
 
-	if err := ic.Libpod.Log(ctrs, logOpts, logChannel); err != nil {
+	if err := ic.Libpod.Log(ctx, ctrs, logOpts, logChannel); err != nil {
 		return err
 	}
 
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 8650ba000..07b492331 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -754,7 +754,7 @@ func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string
 	if err != nil {
 		return call.ReplyErrorOccurred(err.Error())
 	}
-	if err := i.Runtime.Log(containers, &options, logChannel); err != nil {
+	if err := i.Runtime.Log(getContext(), containers, &options, logChannel); err != nil {
 		return err
 	}
 	go func() {
-- 
cgit v1.2.3-54-g00ecf