From 91a3cb04e9e6e51c9117b7ef57dbd49fb522d16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20M=C3=ADchal?= Date: Tue, 14 Jul 2020 20:17:40 +0200 Subject: [PATCH] cmd/list: Decode image/container JSON to struct Every time Podman changes their JSON API Toolbox breaks horribly. That is caused by the combination of decoding the JSON purely by hand and by the complete lack of type assertions to make the process stable. I previously didn't know that unmarshalling with Go works on the 'do the best job it can' logic (thank you Owen!). This makes the need to check for subtle changes in the names of fields go away and type checking a bit more bareable (marking the questioned field as interface{} and then immediatelly type asserting). If even now an existing field does not hold an expected value the field will remain blank. --- src/cmd/list.go | 112 +++++++++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/src/cmd/list.go b/src/cmd/list.go index 7188701b7..41017d71c 100644 --- a/src/cmd/list.go +++ b/src/cmd/list.go @@ -17,13 +17,16 @@ package cmd import ( + "encoding/json" "errors" "fmt" "os" "text/tabwriter" + "time" "github.com/containers/toolbox/pkg/podman" "github.com/containers/toolbox/pkg/utils" + "github.com/docker/go-units" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -187,25 +190,36 @@ func listOutput(images, containers []map[string]interface{}) { writer := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintf(writer, "%s\t%s\t%s\n", "IMAGE ID", "IMAGE NAME", "CREATED") - var idKey, nameKey, createdKey string - if podman.CheckVersion("2.0.0") { - idKey = "Id" - nameKey = "Names" - createdKey = "Created" - } else if podman.CheckVersion("1.8.3") { - idKey = "ID" - nameKey = "Names" - createdKey = "Created" - } else { - idKey = "id" - nameKey = "names" - createdKey = "created" - } - for _, image := range images { - id := utils.ShortID(image[idKey].(string)) - name := image[nameKey].([]interface{})[0].(string) - created := image[createdKey].(string) + imageJSON, err := json.Marshal(image) + if err != nil { + logrus.Errorf("failed to marshal image: %v", err) + continue + } + + var toolboxImage struct { + ID string + Names []string + Created interface{} + } + err = json.Unmarshal(imageJSON, &toolboxImage) + if err != nil { + logrus.Errorf("failed to unmarshal toolbox image: %v", err) + continue + } + + var id, name, created string + id = utils.ShortID(toolboxImage.ID) + + name = toolboxImage.Names[0] + + switch value := toolboxImage.Created.(type) { + case string: + created = value + case float64: + created = units.HumanDuration(time.Since(time.Unix(int64(value), 0))) + " ago" + } + fmt.Fprintf(writer, "%s\t%s\t%s\n", id, name, created) } @@ -226,33 +240,53 @@ func listOutput(images, containers []map[string]interface{}) { "STATUS", "IMAGE NAME") - var idKey, createdKey, statusKey string - if podman.CheckVersion("2.0.0") { - idKey = "Id" - createdKey = "CreatedAt" - statusKey = "State" - } else { - idKey = "ID" - createdKey = "Created" - statusKey = "Status" - } - for _, container := range containers { - id := utils.ShortID(container[idKey].(string)) + containerJSON, err := json.Marshal(container) + if err != nil { + logrus.Errorf("failed to marshal container: %v", err) + continue + } + + var toolboxContainer struct { + ID string + Name string + Names []string + Status string + State string + Created interface{} + Image string + } + err = json.Unmarshal(containerJSON, &toolboxContainer) + if err != nil { + logrus.Errorf("failed to unmarshal toolbox container: %v", err) + continue + } + + var id, name, created, status, imageName string + id = utils.ShortID(toolboxContainer.ID) + + if toolboxContainer.Name != "" { + name = toolboxContainer.Name + } else { + name = toolboxContainer.Names[0] + } + + if toolboxContainer.Status != "" { + status = toolboxContainer.Status + } else { + status = toolboxContainer.State + } - var nameString string - switch name := container["Names"].(type) { + switch value := toolboxContainer.Created.(type) { case string: - nameString = name - case []interface{}: - nameString = name[0].(string) + created = value + case float64: + created = units.HumanDuration(time.Since(time.Unix(int64(value), 0))) + " ago" } - created := container[createdKey].(string) - status := container[statusKey].(string) - imageName := container["Image"].(string) + imageName = toolboxContainer.Image - fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\n", id, nameString, created, status, imageName) + fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\n", id, name, created, status, imageName) } writer.Flush()