Merge pull request #10868 from cdoern/untilLog
Implemented Until Query Parameter for Containers/logs
This commit is contained in:
		
						commit
						bef1f03d3c
					
				|  | @ -56,7 +56,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption | ||||||
| 		for _, nll := range tailLog { | 		for _, nll := range tailLog { | ||||||
| 			nll.CID = c.ID() | 			nll.CID = c.ID() | ||||||
| 			nll.CName = c.Name() | 			nll.CName = c.Name() | ||||||
| 			if nll.Since(options.Since) { | 			if nll.Since(options.Since) && nll.Until(options.Until) { | ||||||
| 				logChannel <- nll | 				logChannel <- nll | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -88,7 +88,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption | ||||||
| 			} | 			} | ||||||
| 			nll.CID = c.ID() | 			nll.CID = c.ID() | ||||||
| 			nll.CName = c.Name() | 			nll.CName = c.Name() | ||||||
| 			if nll.Since(options.Since) { | 			if nll.Since(options.Since) && nll.Until(options.Until) { | ||||||
| 				logChannel <- nll | 				logChannel <- nll | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -97,6 +97,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption | ||||||
| 			} | 			} | ||||||
| 		}() | 		}() | ||||||
| 
 | 
 | ||||||
|  | 		beforeTimeStamp := true | ||||||
| 		afterTimeStamp := false        // needed for options.Since
 | 		afterTimeStamp := false        // needed for options.Since
 | ||||||
| 		tailQueue := []*logs.LogLine{} // needed for options.Tail
 | 		tailQueue := []*logs.LogLine{} // needed for options.Tail
 | ||||||
| 		doTail := options.Tail > 0 | 		doTail := options.Tail > 0 | ||||||
|  | @ -156,6 +157,13 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption | ||||||
| 				} | 				} | ||||||
| 				afterTimeStamp = true | 				afterTimeStamp = true | ||||||
| 			} | 			} | ||||||
|  | 			if beforeTimeStamp { | ||||||
|  | 				entryTime := time.Unix(0, int64(entry.RealtimeTimestamp)*int64(time.Microsecond)) | ||||||
|  | 				if entryTime.Before(options.Until) || !options.Until.IsZero() { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				beforeTimeStamp = false | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			// If we're reading an event and the container exited/died,
 | 			// If we're reading an event and the container exited/died,
 | ||||||
| 			// then we're done and can return.
 | 			// then we're done and can return.
 | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ type LogOptions struct { | ||||||
| 	Details    bool | 	Details    bool | ||||||
| 	Follow     bool | 	Follow     bool | ||||||
| 	Since      time.Time | 	Since      time.Time | ||||||
|  | 	Until      time.Time | ||||||
| 	Tail       int64 | 	Tail       int64 | ||||||
| 	Timestamps bool | 	Timestamps bool | ||||||
| 	Multi      bool | 	Multi      bool | ||||||
|  | @ -184,7 +185,12 @@ func (l *LogLine) String(options *LogOptions) string { | ||||||
| 
 | 
 | ||||||
| // Since returns a bool as to whether a log line occurred after a given time
 | // Since returns a bool as to whether a log line occurred after a given time
 | ||||||
| func (l *LogLine) Since(since time.Time) bool { | func (l *LogLine) Since(since time.Time) bool { | ||||||
| 	return l.Time.After(since) | 	return l.Time.After(since) || since.IsZero() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Until returns a bool as to whether a log line occurred before a given time
 | ||||||
|  | func (l *LogLine) Until(until time.Time) bool { | ||||||
|  | 	return l.Time.Before(until) || until.IsZero() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewLogLine creates a logLine struct from a container log string
 | // NewLogLine creates a logLine struct from a container log string
 | ||||||
|  |  | ||||||
|  | @ -72,11 +72,12 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { | ||||||
| 
 | 
 | ||||||
| 	var until time.Time | 	var until time.Time | ||||||
| 	if _, found := r.URL.Query()["until"]; found { | 	if _, found := r.URL.Query()["until"]; found { | ||||||
| 		// FIXME: until != since but the logs backend does not yet support until.
 | 		if query.Until != "0" { | ||||||
| 		since, err = util.ParseInputTime(query.Until) | 			until, err = util.ParseInputTime(query.Until) | ||||||
| 		if err != nil { | 			if err != nil { | ||||||
| 			utils.BadRequest(w, "until", query.Until, err) | 				utils.BadRequest(w, "until", query.Until, err) | ||||||
| 			return | 				return | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -84,6 +85,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { | ||||||
| 		Details:    true, | 		Details:    true, | ||||||
| 		Follow:     query.Follow, | 		Follow:     query.Follow, | ||||||
| 		Since:      since, | 		Since:      since, | ||||||
|  | 		Until:      until, | ||||||
| 		Tail:       tail, | 		Tail:       tail, | ||||||
| 		Timestamps: query.Timestamps, | 		Timestamps: query.Timestamps, | ||||||
| 	} | 	} | ||||||
|  | @ -119,7 +121,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { | ||||||
| 
 | 
 | ||||||
| 	for line := range logChannel { | 	for line := range logChannel { | ||||||
| 		if _, found := r.URL.Query()["until"]; found { | 		if _, found := r.URL.Query()["until"]; found { | ||||||
| 			if line.Time.After(until) { | 			if line.Time.After(until) && !until.IsZero() { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| import random | import random | ||||||
| import unittest | import unittest | ||||||
|  | import json | ||||||
| 
 | 
 | ||||||
| import requests | import requests | ||||||
| from dateutil.parser import parse | from dateutil.parser import parse | ||||||
|  | @ -97,6 +98,18 @@ class ContainerTestCase(APITestCase): | ||||||
|     def test_logs(self): |     def test_logs(self): | ||||||
|         r = requests.get(self.uri(self.resolve_container("/containers/{}/logs?stdout=true"))) |         r = requests.get(self.uri(self.resolve_container("/containers/{}/logs?stdout=true"))) | ||||||
|         self.assertEqual(r.status_code, 200, r.text) |         self.assertEqual(r.status_code, 200, r.text) | ||||||
|  |         r = requests.post( | ||||||
|  |             self.podman_url + "/v1.40/containers/create?name=topcontainer", | ||||||
|  |             json={"Cmd": ["top", "ls"], "Image": "alpine:latest"}, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(r.status_code, 201, r.text) | ||||||
|  |         payload = r.json() | ||||||
|  |         container_id = payload["Id"] | ||||||
|  |         self.assertIsNotNone(container_id) | ||||||
|  |         r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=0") | ||||||
|  |         self.assertEqual(r.status_code, 200, r.text) | ||||||
|  |         r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=1") | ||||||
|  |         self.assertEqual(r.status_code, 200, r.text) | ||||||
| 
 | 
 | ||||||
|     def test_commit(self): |     def test_commit(self): | ||||||
|         r = requests.post(self.uri(self.resolve_container("/commit?container={}"))) |         r = requests.post(self.uri(self.resolve_container("/commit?container={}"))) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue