diff --git a/pkg/cmd/get.go b/pkg/cmd/get.go index 65db89d4..af276481 100644 --- a/pkg/cmd/get.go +++ b/pkg/cmd/get.go @@ -4,12 +4,16 @@ import ( "fmt" "io" "text/tabwriter" + "time" "github.com/iovisor/kubectl-trace/pkg/factory" "github.com/iovisor/kubectl-trace/pkg/meta" "github.com/iovisor/kubectl-trace/pkg/tracejob" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/duration" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/cli-runtime/pkg/genericclioptions" batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -187,7 +191,21 @@ func jobsTablePrint(o io.Writer, jobs []tracejob.TraceJob) { // them as missing. fmt.Fprintf(w, format, "NAMESPACE", "NODE", "NAME", "STATUS", "AGE") for _, j := range jobs { - fmt.Fprintf(w, "\n"+format, j.Namespace, j.Hostname, j.Name, "", "") + status := j.Status + if status == "" { + status = tracejob.TraceJobUnknown + } + fmt.Fprintf(w, "\n"+format, j.Namespace, j.Hostname, j.Name, status, translateTimestampSince(j.StartTime)) } fmt.Fprintf(w, "\n") } + +// translateTimestampSince returns the elapsed time since timestamp in +// human-readable approximation. +func translateTimestampSince(timestamp metav1.Time) string { + if timestamp.IsZero() { + return "" + } + + return duration.HumanDuration(time.Since(timestamp.Time)) +} diff --git a/pkg/tracejob/job.go b/pkg/tracejob/job.go index 82990369..fb50ae94 100644 --- a/pkg/tracejob/job.go +++ b/pkg/tracejob/job.go @@ -35,6 +35,8 @@ type TraceJob struct { ImageNameTag string InitImageNameTag string FetchHeaders bool + StartTime metav1.Time + Status TraceJobStatus } // WithOutStream setup a file stream to output trace job operation information @@ -129,6 +131,8 @@ func (t *TraceJobClient) GetJob(nf TraceJobFilter) ([]TraceJob, error) { ID: types.UID(id), Namespace: j.Namespace, Hostname: hostname, + StartTime: *j.Status.StartTime, + Status: jobStatus(j), } tjobs = append(tjobs, tj) } @@ -362,3 +366,31 @@ func jobHostname(j batchv1.Job) (string, error) { return "", fmt.Errorf("hostname not found for job") } + +// TraceJobStatus is a label for the running status of a trace job at the current time. +type TraceJobStatus string + +// These are the valid status of traces. +const ( + // TraceJobRunning means the trace job has active pods. + TraceJobRunning TraceJobStatus = "Running" + // TraceJobCompleted means the trace job does not have any active pod and has success pods. + TraceJobCompleted TraceJobStatus = "Completed" + // TraceJobFailed means the trace job does not have any active or success pod and has fpods that failed. + TraceJobFailed TraceJobStatus = "Failed" + // TraceJobUnknown means that for some reason we do not have the information to determine the status. + TraceJobUnknown TraceJobStatus = "Unknown" +) + +func jobStatus(j batchv1.Job) TraceJobStatus { + if j.Status.Active > 0 { + return TraceJobRunning + } + if j.Status.Succeeded > 0 { + return TraceJobCompleted + } + if j.Status.Failed > 0 { + return TraceJobFailed + } + return TraceJobUnknown +} \ No newline at end of file diff --git a/vendor/k8s.io/apimachinery/pkg/util/duration/duration.go b/vendor/k8s.io/apimachinery/pkg/util/duration/duration.go new file mode 100644 index 00000000..961ec5ed --- /dev/null +++ b/vendor/k8s.io/apimachinery/pkg/util/duration/duration.go @@ -0,0 +1,89 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package duration + +import ( + "fmt" + "time" +) + +// ShortHumanDuration returns a succint representation of the provided duration +// with limited precision for consumption by humans. +func ShortHumanDuration(d time.Duration) string { + // Allow deviation no more than 2 seconds(excluded) to tolerate machine time + // inconsistence, it can be considered as almost now. + if seconds := int(d.Seconds()); seconds < -1 { + return fmt.Sprintf("") + } else if seconds < 0 { + return fmt.Sprintf("0s") + } else if seconds < 60 { + return fmt.Sprintf("%ds", seconds) + } else if minutes := int(d.Minutes()); minutes < 60 { + return fmt.Sprintf("%dm", minutes) + } else if hours := int(d.Hours()); hours < 24 { + return fmt.Sprintf("%dh", hours) + } else if hours < 24*365 { + return fmt.Sprintf("%dd", hours/24) + } + return fmt.Sprintf("%dy", int(d.Hours()/24/365)) +} + +// HumanDuration returns a succint representation of the provided duration +// with limited precision for consumption by humans. It provides ~2-3 significant +// figures of duration. +func HumanDuration(d time.Duration) string { + // Allow deviation no more than 2 seconds(excluded) to tolerate machine time + // inconsistence, it can be considered as almost now. + if seconds := int(d.Seconds()); seconds < -1 { + return fmt.Sprintf("") + } else if seconds < 0 { + return fmt.Sprintf("0s") + } else if seconds < 60*2 { + return fmt.Sprintf("%ds", seconds) + } + minutes := int(d / time.Minute) + if minutes < 10 { + s := int(d/time.Second) % 60 + if s == 0 { + return fmt.Sprintf("%dm", minutes) + } + return fmt.Sprintf("%dm%ds", minutes, s) + } else if minutes < 60*3 { + return fmt.Sprintf("%dm", minutes) + } + hours := int(d / time.Hour) + if hours < 8 { + m := int(d/time.Minute) % 60 + if m == 0 { + return fmt.Sprintf("%dh", hours) + } + return fmt.Sprintf("%dh%dm", hours, m) + } else if hours < 48 { + return fmt.Sprintf("%dh", hours) + } else if hours < 24*8 { + h := hours % 24 + if h == 0 { + return fmt.Sprintf("%dd", hours/24) + } + return fmt.Sprintf("%dd%dh", hours/24, h) + } else if hours < 24*365*2 { + return fmt.Sprintf("%dd", hours/24) + } else if hours < 24*365*8 { + return fmt.Sprintf("%dy%dd", hours/24/365, (hours/24)%365) + } + return fmt.Sprintf("%dy", int(hours/24/365)) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5b8ad060..374d5c01 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -246,6 +246,7 @@ k8s.io/api/storage/v1beta1 k8s.io/apimachinery/pkg/apis/meta/v1 k8s.io/apimachinery/pkg/types k8s.io/apimachinery/pkg/util/wait +k8s.io/apimachinery/pkg/util/duration k8s.io/apimachinery/pkg/util/uuid k8s.io/apimachinery/pkg/api/meta k8s.io/apimachinery/pkg/runtime/schema