Skip to content

Commit

Permalink
cmd/list: Decode image/container JSON to struct
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
HarryMichal committed Jul 14, 2020
1 parent 140c2d7 commit 91a3cb0
Showing 1 changed file with 73 additions and 39 deletions.
112 changes: 73 additions & 39 deletions src/cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
}

Expand All @@ -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()
Expand Down

0 comments on commit 91a3cb0

Please sign in to comment.