From 53e63bc0291fcc1a1f58a0adb8e963b2710a0777 Mon Sep 17 00:00:00 2001 From: "Fabio M. Graetz, Ph.D" Date: Tue, 13 Jun 2023 22:34:49 +0200 Subject: [PATCH] Feat: Add pod start and finish time in RFC3339 time format to logging link templating variables #minor (#360) * Add RFC3339 time format log link vars for start and finish time Signed-off-by: Fabio Graetz * Add RFC3339 timestamp to log link generation tests Signed-off-by: Fabio Graetz --------- Signed-off-by: Fabio Graetz --- go/tasks/logs/logging_utils.go | 21 ++- go/tasks/pluginmachinery/tasklog/plugin.go | 20 +-- go/tasks/pluginmachinery/tasklog/template.go | 72 +++++---- .../pluginmachinery/tasklog/template_test.go | 143 ++++++++++++------ 4 files changed, 163 insertions(+), 93 deletions(-) diff --git a/go/tasks/logs/logging_utils.go b/go/tasks/logs/logging_utils.go index 38e895d78..3ba296d07 100755 --- a/go/tasks/logs/logging_utils.go +++ b/go/tasks/logs/logging_utils.go @@ -38,16 +38,21 @@ func GetLogsForContainerInPod(ctx context.Context, logPlugin tasklog.Plugin, pod return nil, nil } + startTime := pod.CreationTimestamp.Unix() + finishTime := time.Now().Unix() + logs, err := logPlugin.GetTaskLogs( tasklog.Input{ - PodName: pod.Name, - PodUID: string(pod.GetUID()), - Namespace: pod.Namespace, - ContainerName: pod.Spec.Containers[index].Name, - ContainerID: pod.Status.ContainerStatuses[index].ContainerID, - LogName: nameSuffix, - PodUnixStartTime: pod.CreationTimestamp.Unix(), - PodUnixFinishTime: time.Now().Unix(), + PodName: pod.Name, + PodUID: string(pod.GetUID()), + Namespace: pod.Namespace, + ContainerName: pod.Spec.Containers[index].Name, + ContainerID: pod.Status.ContainerStatuses[index].ContainerID, + LogName: nameSuffix, + PodRFC3339StartTime: time.Unix(startTime, 0).Format(time.RFC3339), + PodRFC3339FinishTime: time.Unix(finishTime, 0).Format(time.RFC3339), + PodUnixStartTime: startTime, + PodUnixFinishTime: finishTime, }, ) diff --git a/go/tasks/pluginmachinery/tasklog/plugin.go b/go/tasks/pluginmachinery/tasklog/plugin.go index f96c9cf8a..6a7bd8942 100644 --- a/go/tasks/pluginmachinery/tasklog/plugin.go +++ b/go/tasks/pluginmachinery/tasklog/plugin.go @@ -5,15 +5,17 @@ import "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/core" // Input contains all available information about task's execution that a log plugin can use to construct task's // log links. type Input struct { - HostName string `json:"hostname"` - PodName string `json:"podName"` - Namespace string `json:"namespace"` - ContainerName string `json:"containerName"` - ContainerID string `json:"containerId"` - LogName string `json:"logName"` - PodUnixStartTime int64 `json:"podUnixStartTime"` - PodUnixFinishTime int64 `json:"podUnixFinishTime"` - PodUID string `json:"podUID"` + HostName string `json:"hostname"` + PodName string `json:"podName"` + Namespace string `json:"namespace"` + ContainerName string `json:"containerName"` + ContainerID string `json:"containerId"` + LogName string `json:"logName"` + PodRFC3339StartTime string `json:"podRFC3339StartTime"` + PodRFC3339FinishTime string `json:"podRFC3339FinishTime"` + PodUnixStartTime int64 `json:"podUnixStartTime"` + PodUnixFinishTime int64 `json:"podUnixFinishTime"` + PodUID string `json:"podUID"` } // Output contains all task logs a plugin generates for a given Input. diff --git a/go/tasks/pluginmachinery/tasklog/template.go b/go/tasks/pluginmachinery/tasklog/template.go index 4bb52aef4..9bc6121b6 100644 --- a/go/tasks/pluginmachinery/tasklog/template.go +++ b/go/tasks/pluginmachinery/tasklog/template.go @@ -17,6 +17,8 @@ import ( // {{ .containerId }}: The container id docker/crio generated at run time, // {{ .logName }}: A deployment specific name where to expect the logs to be. // {{ .hostname }}: The hostname where the pod is running and where logs reside. +// {{ .PodRFC3339StartTime }}: The pod creation time in RFC3339 format +// {{ .PodRFC3339FinishTime }}: Don't have a good mechanism for this yet, but approximating with time.Now for now // {{ .podUnixStartTime }}: The pod creation time (in unix seconds, not millis) // {{ .podUnixFinishTime }}: Don't have a good mechanism for this yet, but approximating with time.Now for now type TemplateLogPlugin struct { @@ -30,28 +32,32 @@ type regexValPair struct { } type templateRegexes struct { - PodName *regexp.Regexp - PodUID *regexp.Regexp - Namespace *regexp.Regexp - ContainerName *regexp.Regexp - ContainerID *regexp.Regexp - LogName *regexp.Regexp - Hostname *regexp.Regexp - PodUnixStartTime *regexp.Regexp - PodUnixFinishTime *regexp.Regexp + PodName *regexp.Regexp + PodUID *regexp.Regexp + Namespace *regexp.Regexp + ContainerName *regexp.Regexp + ContainerID *regexp.Regexp + LogName *regexp.Regexp + Hostname *regexp.Regexp + PodRFC3339StartTime *regexp.Regexp + PodRFC3339FinishTime *regexp.Regexp + PodUnixStartTime *regexp.Regexp + PodUnixFinishTime *regexp.Regexp } func mustInitTemplateRegexes() templateRegexes { return templateRegexes{ - PodName: mustCreateRegex("podName"), - PodUID: mustCreateRegex("podUID"), - Namespace: mustCreateRegex("namespace"), - ContainerName: mustCreateRegex("containerName"), - ContainerID: mustCreateRegex("containerID"), - LogName: mustCreateRegex("logName"), - Hostname: mustCreateRegex("hostname"), - PodUnixStartTime: mustCreateRegex("podUnixStartTime"), - PodUnixFinishTime: mustCreateRegex("podUnixFinishTime"), + PodName: mustCreateRegex("podName"), + PodUID: mustCreateRegex("podUID"), + Namespace: mustCreateRegex("namespace"), + ContainerName: mustCreateRegex("containerName"), + ContainerID: mustCreateRegex("containerID"), + LogName: mustCreateRegex("logName"), + Hostname: mustCreateRegex("hostname"), + PodRFC3339StartTime: mustCreateRegex("podRFC3339StartTime"), + PodRFC3339FinishTime: mustCreateRegex("podRFC3339FinishTime"), + PodUnixStartTime: mustCreateRegex("podUnixStartTime"), + PodUnixFinishTime: mustCreateRegex("podUnixFinishTime"), } } @@ -69,16 +75,18 @@ func replaceAll(template string, values []regexValPair) string { return template } -func (s TemplateLogPlugin) GetTaskLog(podName, podUID, namespace, containerName, containerID, logName string, podUnixStartTime, podUnixFinishTime int64) (core.TaskLog, error) { +func (s TemplateLogPlugin) GetTaskLog(podName, podUID, namespace, containerName, containerID, logName string, podRFC3339StartTime string, podRFC3339FinishTime string, podUnixStartTime, podUnixFinishTime int64) (core.TaskLog, error) { o, err := s.GetTaskLogs(Input{ - LogName: logName, - Namespace: namespace, - PodName: podName, - PodUID: podUID, - ContainerName: containerName, - ContainerID: containerID, - PodUnixStartTime: podUnixStartTime, - PodUnixFinishTime: podUnixFinishTime, + LogName: logName, + Namespace: namespace, + PodName: podName, + PodUID: podUID, + ContainerName: containerName, + ContainerID: containerID, + PodRFC3339StartTime: podRFC3339StartTime, + PodRFC3339FinishTime: podRFC3339FinishTime, + PodUnixStartTime: podUnixStartTime, + PodUnixFinishTime: podUnixFinishTime, }) if err != nil || len(o.TaskLogs) == 0 { @@ -131,6 +139,14 @@ func (s TemplateLogPlugin) GetTaskLogs(input Input) (Output, error) { regex: regexes.Hostname, val: input.HostName, }, + { + regex: regexes.PodRFC3339StartTime, + val: input.PodRFC3339StartTime, + }, + { + regex: regexes.PodRFC3339FinishTime, + val: input.PodRFC3339FinishTime, + }, { regex: regexes.PodUnixStartTime, val: strconv.FormatInt(input.PodUnixStartTime, 10), @@ -160,6 +176,8 @@ func (s TemplateLogPlugin) GetTaskLogs(input Input) (Output, error) { // {{ .containerId }}: The container id docker/crio generated at run time, // {{ .logName }}: A deployment specific name where to expect the logs to be. // {{ .hostname }}: The hostname where the pod is running and where logs reside. +// {{ .PodRFC3339StartTime }}: The pod creation time in RFC3339 format +// {{ .PodRFC3339FinishTime }}: Don't have a good mechanism for this yet, but approximating with time.Now for now // {{ .podUnixStartTime }}: The pod creation time (in unix seconds, not millis) // {{ .podUnixFinishTime }}: Don't have a good mechanism for this yet, but approximating with time.Now for now func NewTemplateLogPlugin(templateUris []string, messageFormat core.TaskLog_MessageFormat) TemplateLogPlugin { diff --git a/go/tasks/pluginmachinery/tasklog/template_test.go b/go/tasks/pluginmachinery/tasklog/template_test.go index b74d4efa7..d646b2843 100644 --- a/go/tasks/pluginmachinery/tasklog/template_test.go +++ b/go/tasks/pluginmachinery/tasklog/template_test.go @@ -19,6 +19,8 @@ func TestTemplateLog(t *testing.T) { "spark-kubernetes-driver", "cri-o://abc", "main_logs", + "2015-03-14T17:08:14+01:00", + "2021-06-15T20:47:57+02:00", 1426349294, 1623782877, ) @@ -41,14 +43,16 @@ func Test_templateLogPlugin_Regression(t *testing.T) { messageFormat core.TaskLog_MessageFormat } type args struct { - podName string - podUID string - namespace string - containerName string - containerID string - logName string - podUnixStartTime int64 - podUnixFinishTime int64 + podName string + podUID string + namespace string + containerName string + containerID string + logName string + podRFC3339StartTime string + podRFC3339FinishTime string + podUnixStartTime int64 + podUnixFinishTime int64 } tests := []struct { name string @@ -64,14 +68,16 @@ func Test_templateLogPlugin_Regression(t *testing.T) { messageFormat: core.TaskLog_JSON, }, args{ - podName: "f-uuid-driver", - podUID: "pod-uid", - namespace: "flyteexamples-production", - containerName: "spark-kubernetes-driver", - containerID: "cri-o://abc", - logName: "main_logs", - podUnixStartTime: 123, - podUnixFinishTime: 12345, + podName: "f-uuid-driver", + podUID: "pod-uid", + namespace: "flyteexamples-production", + containerName: "spark-kubernetes-driver", + containerID: "cri-o://abc", + logName: "main_logs", + podRFC3339StartTime: "1970-01-01T01:02:03+01:00", + podRFC3339FinishTime: "1970-01-01T04:25:45+01:00", + podUnixStartTime: 123, + podUnixFinishTime: 12345, }, core.TaskLog{ Uri: "https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logEventViewer:group=/flyte-production/kubernetes;stream=var.log.containers.f-uuid-driver_flyteexamples-production_spark-kubernetes-driver-abc.log", @@ -87,14 +93,16 @@ func Test_templateLogPlugin_Regression(t *testing.T) { messageFormat: core.TaskLog_JSON, }, args{ - podName: "podName", - podUID: "pod-uid", - namespace: "flyteexamples-production", - containerName: "spark-kubernetes-driver", - containerID: "cri-o://abc", - logName: "main_logs", - podUnixStartTime: 123, - podUnixFinishTime: 12345, + podName: "podName", + podUID: "pod-uid", + namespace: "flyteexamples-production", + containerName: "spark-kubernetes-driver", + containerID: "cri-o://abc", + logName: "main_logs", + podRFC3339StartTime: "1970-01-01T01:02:03+01:00", + podRFC3339FinishTime: "1970-01-01T04:25:45+01:00", + podUnixStartTime: 123, + podUnixFinishTime: 12345, }, core.TaskLog{ Uri: "https://console.cloud.google.com/logs/viewer?project=test-gcp-project&angularJsUrl=%2Flogs%2Fviewer%3Fproject%3Dtest-gcp-project&resource=aws_ec2_instance&advancedFilter=resource.labels.pod_name%3DpodName", @@ -110,14 +118,16 @@ func Test_templateLogPlugin_Regression(t *testing.T) { messageFormat: core.TaskLog_JSON, }, args{ - podName: "flyteexamples-development-task-name", - podUID: "pod-uid", - namespace: "flyteexamples-development", - containerName: "ignore", - containerID: "ignore", - logName: "main_logs", - podUnixStartTime: 123, - podUnixFinishTime: 12345, + podName: "flyteexamples-development-task-name", + podUID: "pod-uid", + namespace: "flyteexamples-development", + containerName: "ignore", + containerID: "ignore", + logName: "main_logs", + podRFC3339StartTime: "1970-01-01T01:02:03+01:00", + podRFC3339FinishTime: "1970-01-01T04:25:45+01:00", + podUnixStartTime: 123, + podUnixFinishTime: 12345, }, core.TaskLog{ Uri: "https://dashboard.k8s.net/#!/log/flyteexamples-development/flyteexamples-development-task-name/pod?namespace=flyteexamples-development", @@ -134,7 +144,7 @@ func Test_templateLogPlugin_Regression(t *testing.T) { messageFormat: tt.fields.messageFormat, } - got, err := s.GetTaskLog(tt.args.podName, tt.args.podUID, tt.args.namespace, tt.args.containerName, tt.args.containerID, tt.args.logName, tt.args.podUnixStartTime, tt.args.podUnixFinishTime) + got, err := s.GetTaskLog(tt.args.podName, tt.args.podUID, tt.args.namespace, tt.args.containerName, tt.args.containerID, tt.args.logName, tt.args.podRFC3339FinishTime, tt.args.podRFC3339FinishTime, tt.args.podUnixStartTime, tt.args.podUnixFinishTime) if (err != nil) != tt.wantErr { t.Errorf("GetTaskLog() error = %v, wantErr %v", err, tt.wantErr) return @@ -170,14 +180,16 @@ func TestTemplateLogPlugin_NewTaskLog(t *testing.T) { }, args{ input: Input{ - HostName: "my-host", - PodName: "my-pod", - Namespace: "my-namespace", - ContainerName: "my-container", - ContainerID: "ignore", - LogName: "main_logs", - PodUnixStartTime: 123, - PodUnixFinishTime: 12345, + HostName: "my-host", + PodName: "my-pod", + Namespace: "my-namespace", + ContainerName: "my-container", + ContainerID: "ignore", + LogName: "main_logs", + PodRFC3339StartTime: "1970-01-01T01:02:03+01:00", + PodRFC3339FinishTime: "1970-01-01T04:25:45+01:00", + PodUnixStartTime: 123, + PodUnixFinishTime: 12345, }, }, Output{ @@ -199,14 +211,16 @@ func TestTemplateLogPlugin_NewTaskLog(t *testing.T) { }, args{ input: Input{ - HostName: "my-host", - PodName: "my-pod", - Namespace: "my-namespace", - ContainerName: "my-container", - ContainerID: "ignore", - LogName: "main_logs", - PodUnixStartTime: 123, - PodUnixFinishTime: 12345, + HostName: "my-host", + PodName: "my-pod", + Namespace: "my-namespace", + ContainerName: "my-container", + ContainerID: "ignore", + LogName: "main_logs", + PodRFC3339StartTime: "1970-01-01T01:02:03+01:00", + PodRFC3339FinishTime: "1970-01-01T04:25:45+01:00", + PodUnixStartTime: 123, + PodUnixFinishTime: 12345, }, }, Output{ @@ -220,6 +234,37 @@ func TestTemplateLogPlugin_NewTaskLog(t *testing.T) { }, false, }, + { + "stackdriver-with-rfc3339-timestamp", + fields{ + templateURI: "https://console.cloud.google.com/logs/viewer?project=test-gcp-project&angularJsUrl=%2Flogs%2Fviewer%3Fproject%3Dtest-gcp-project&resource=aws_ec2_instance&advancedFilter=resource.labels.pod_name%3D{{.podName}}%20%22{{.podRFC3339StartTime}}%22", + messageFormat: core.TaskLog_JSON, + }, + args{ + input: Input{ + HostName: "my-host", + PodName: "my-pod", + Namespace: "my-namespace", + ContainerName: "my-container", + ContainerID: "ignore", + LogName: "main_logs", + PodRFC3339StartTime: "1970-01-01T01:02:03+01:00", + PodRFC3339FinishTime: "1970-01-01T04:25:45+01:00", + PodUnixStartTime: 123, + PodUnixFinishTime: 12345, + }, + }, + Output{ + TaskLogs: []*core.TaskLog{ + { + Uri: "https://console.cloud.google.com/logs/viewer?project=test-gcp-project&angularJsUrl=%2Flogs%2Fviewer%3Fproject%3Dtest-gcp-project&resource=aws_ec2_instance&advancedFilter=resource.labels.pod_name%3Dmy-pod%20%221970-01-01T01:02:03+01:00%22", + MessageFormat: core.TaskLog_JSON, + Name: "main_logs", + }, + }, + }, + false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {