From 580af78488799733b40e2cd16295e228962500a6 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Thu, 29 Aug 2024 13:27:11 -0400 Subject: [PATCH] stack status cmd - add columns for image build date and VCS reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When dealing with snapshot builds that are constantly updated, it's difficult to know what build of a service is really running. When viewing historic build logs from CI this information is important to know. This adds information about the service to the output of `elastic-package stack status`. The data is read from http://label-schema.org/rc1/ labels. It uses the org.label-schema.build-date and org.label-schema.vcs-ref labels. ``` BEFORE: ╭──────────────────┬─────────────────┬─────────────────────╮ │ SERVICE │ VERSION │ STATUS │ ├──────────────────┼─────────────────┼─────────────────────┤ │ elastic-agent │ 8.16.0-SNAPSHOT │ running (unhealthy) │ │ elasticsearch │ 8.16.0-SNAPSHOT │ running (healthy) │ │ fleet-server │ 8.16.0-SNAPSHOT │ running (healthy) │ │ kibana │ 8.16.0-SNAPSHOT │ running (healthy) │ │ package-registry │ latest │ running (healthy) │ ╰──────────────────┴─────────────────┴─────────────────────╯ ``` ``` AFTER: ╭──────────────────┬─────────────────┬─────────────────────┬───────────────────┬────────────╮ │ SERVICE │ VERSION │ STATUS │ IMAGE BUILD DATE │ VCS REF │ ├──────────────────┼─────────────────┼─────────────────────┼───────────────────┼────────────┤ │ elastic-agent │ 8.16.0-SNAPSHOT │ running (unhealthy) │ 2024-08-22T02:44Z │ b96a4ca8fa │ │ elasticsearch │ 8.16.0-SNAPSHOT │ running (healthy) │ 2024-08-22T13:26Z │ 1362d56865 │ │ fleet-server │ 8.16.0-SNAPSHOT │ running (healthy) │ 2024-08-22T02:44Z │ b96a4ca8fa │ │ kibana │ 8.16.0-SNAPSHOT │ running (healthy) │ 2024-08-22T11:09Z │ cdcdfddd3f │ │ package-registry │ latest │ running (healthy) │ │ │ ╰──────────────────┴─────────────────┴─────────────────────┴───────────────────┴────────────╯ ``` --- cmd/stack.go | 22 ++++++++++++++++++++-- internal/docker/docker.go | 4 ++++ internal/stack/compose.go | 2 ++ internal/stack/compose_test.go | 3 +++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cmd/stack.go b/cmd/stack.go index 63899ba2eb..379ca16348 100644 --- a/cmd/stack.go +++ b/cmd/stack.go @@ -7,6 +7,7 @@ package cmd import ( "fmt" "strings" + "time" "github.com/jedib0t/go-pretty/table" @@ -350,11 +351,28 @@ func printStatus(cmd *cobra.Command, servicesStatus []stack.ServiceStatus) { return } t := table.NewWriter() - t.AppendHeader(table.Row{"Service", "Version", "Status"}) + t.AppendHeader(table.Row{"Service", "Version", "Status", "Image Build Date", "VCS Ref"}) for _, service := range servicesStatus { - t.AppendRow(table.Row{service.Name, service.Version, service.Status}) + t.AppendRow(table.Row{service.Name, service.Version, service.Status, formatTime(service.Labels.BuildDate), truncate(service.Labels.VCSRef, 10)}) } t.SetStyle(table.StyleRounded) cmd.Println(t.Render()) } + +// formatTime returns the given RFC3339 time formated as 2006-01-02T15:04Z. +// If the value is not in RFC3339 format, then it is returned as-is. +func formatTime(maybeRFC3339Time string) string { + if t, err := time.Parse(time.RFC3339, maybeRFC3339Time); err == nil { + return t.UTC().Format("2006-01-02T15:04Z") + } + return maybeRFC3339Time +} + +// truncate truncates text if it longer than maxLength. +func truncate(text string, maxLength int) string { + if len(text) > maxLength { + return text[:maxLength] + } + return text +} diff --git a/internal/docker/docker.go b/internal/docker/docker.go index fedfe3a151..4f4f9d5537 100644 --- a/internal/docker/docker.go +++ b/internal/docker/docker.go @@ -49,6 +49,10 @@ type ConfigLabels struct { ComposeProject string `json:"com.docker.compose.project"` ComposeService string `json:"com.docker.compose.service"` ComposeVersion string `json:"com.docker.compose.version"` + + // http://label-schema.org/rc1/ Labels + BuildDate string `json:"org.label-schema.build-date,omitempty"` // This label contains the Date/Time the image was built. The value SHOULD be formatted according to RFC 3339. + VCSRef string `json:"org.label-schema.vcs-ref,omitempty"` // Identifier for the version of the source code from which this image was built. For example if the version control system is git this is the SHA. } // String function dumps string representation of the container description. diff --git a/internal/stack/compose.go b/internal/stack/compose.go index 483e638e80..a7270183b1 100644 --- a/internal/stack/compose.go +++ b/internal/stack/compose.go @@ -18,6 +18,7 @@ type ServiceStatus struct { Name string Status string Version string + Labels *docker.ConfigLabels // Container labels. } const readyServicesSuffix = "is_ready" @@ -214,6 +215,7 @@ func newServiceStatus(description *docker.ContainerDescription) (*ServiceStatus, Name: description.Config.Labels.ComposeService, Status: description.State.Status, Version: getVersionFromDockerImage(description.Config.Image), + Labels: &description.Config.Labels, } if description.State.Status == "running" { healthStatus := "unknown health" diff --git a/internal/stack/compose_test.go b/internal/stack/compose_test.go index 96be46942c..496ce48ecd 100644 --- a/internal/stack/compose_test.go +++ b/internal/stack/compose_test.go @@ -78,6 +78,7 @@ func TestNewServiceStatus(t *testing.T) { Name: "myservice", Status: "running (healthy)", Version: "1.42.0", + Labels: &docker.ConfigLabels{ComposeService: "myservice"}, }, }, { @@ -112,6 +113,7 @@ func TestNewServiceStatus(t *testing.T) { Name: "myservice", Status: "exited (128)", Version: "1.42.0", + Labels: &docker.ConfigLabels{ComposeService: "myservice"}, }, }, { @@ -155,6 +157,7 @@ func TestNewServiceStatus(t *testing.T) { Name: "myservice", Status: "running (starting)", Version: "1.42.0", + Labels: &docker.ConfigLabels{ComposeService: "myservice"}, }, }, }