Skip to content

Commit

Permalink
Fix invalid JSON for multiple images/containers/pods
Browse files Browse the repository at this point in the history
Signed-off-by: Sascha Grunert <[email protected]>
  • Loading branch information
saschagrunert committed Jul 10, 2024
1 parent 767879c commit a55af6c
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 144 deletions.
74 changes: 41 additions & 33 deletions cmd/crictl/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,12 +517,16 @@ var containerStatusCommand = &cli.Command{
return err
}

for i := 0; i < c.NArg(); i++ {
containerID := c.Args().Get(i)
if err := ContainerStatus(runtimeClient, containerID, c.String("output"), c.String("template"), c.Bool("quiet")); err != nil {
return fmt.Errorf("getting the status of the container %q: %w", containerID, err)
}
if err := containerStatus(
runtimeClient,
c.Args().Slice(),
c.String("output"),
c.String("template"),
c.Bool("quiet"),
); err != nil {
return fmt.Errorf("get the status of containers: %w", err)
}

return nil
},
}
Expand Down Expand Up @@ -989,43 +993,49 @@ func marshalContainerStatus(cs *pb.ContainerStatus) (string, error) {
return marshalMapInOrder(jsonMap, *cs)
}

// ContainerStatus sends a ContainerStatusRequest to the server, and parses
// containerStatus sends a ContainerStatusRequest to the server, and parses
// the returned ContainerStatusResponse.
func ContainerStatus(client internalapi.RuntimeService, id, output string, tmplStr string, quiet bool) error {
// nolint:dupl // pods and containers are similar, but still different
func containerStatus(client internalapi.RuntimeService, ids []string, output string, tmplStr string, quiet bool) error {
verbose := !(quiet)
if output == "" { // default to json output
output = "json"
}
if id == "" {
if len(ids) == 0 {
return errors.New("ID cannot be empty")
}
request := &pb.ContainerStatusRequest{
ContainerId: id,
Verbose: verbose,
}
logrus.Debugf("ContainerStatusRequest: %v", request)
r, err := InterruptableRPC(nil, func(ctx context.Context) (*pb.ContainerStatusResponse, error) {
return client.ContainerStatus(ctx, id, verbose)
})
logrus.Debugf("ContainerStatusResponse: %v", r)
if err != nil {
return err
}

status, err := marshalContainerStatus(r.Status)
if err != nil {
return err
}
statuses := []statusData{}
for _, id := range ids {
request := &pb.ContainerStatusRequest{
ContainerId: id,
Verbose: verbose,
}
logrus.Debugf("ContainerStatusRequest: %v", request)
r, err := InterruptableRPC(nil, func(ctx context.Context) (*pb.ContainerStatusResponse, error) {
return client.ContainerStatus(ctx, id, verbose)
})
logrus.Debugf("ContainerStatusResponse: %v", r)
if err != nil {
return fmt.Errorf("get container status: %w", err)
}

switch output {
case "json", "yaml", "go-template":
return outputStatusInfo(status, "", r.Info, output, tmplStr)
case "table": // table output is after this switch block
default:
return fmt.Errorf("output option cannot be %s", output)
statusJSON, err := marshalContainerStatus(r.Status)
if err != nil {
return fmt.Errorf("marshal container status: %w", err)
}

if output == "table" {
outputContainerStatusTable(r, verbose)
} else {
statuses = append(statuses, statusData{json: statusJSON, info: r.Info})
}
}

// output in table format
return outputStatusData(statuses, output, tmplStr)
}

func outputContainerStatusTable(r *pb.ContainerStatusResponse, verbose bool) {
fmt.Printf("ID: %s\n", r.Status.Id)
if r.Status.Metadata != nil {
if r.Status.Metadata.Name != "" {
Expand Down Expand Up @@ -1064,8 +1074,6 @@ func ContainerStatus(client internalapi.RuntimeService, id, output string, tmplS
if verbose {
fmt.Printf("Info: %v\n", r.GetInfo())
}

return nil
}

// ListContainers sends a ListContainerRequest to the server, and parses
Expand Down
95 changes: 46 additions & 49 deletions cmd/crictl/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,52 +323,52 @@ var imageStatusCommand = &cli.Command{
output = "json"
}
tmplStr := c.String("template")

statuses := []statusData{}
for i := 0; i < c.NArg(); i++ {
id := c.Args().Get(i)

r, err := ImageStatus(imageClient, id, verbose)
if err != nil {
return fmt.Errorf("image status for %q request: %w", id, err)
}
image := r.Image
if image == nil {

if r.Image == nil {
return fmt.Errorf("no such image %q present", id)
}

status, err := protobufObjectToJSON(r.Image)
statusJSON, err := protobufObjectToJSON(r.Image)
if err != nil {
return fmt.Errorf("marshal status to json for %q: %w", id, err)
}
switch output {
case "json", "yaml", "go-template":
if err := outputStatusInfo(status, "", r.Info, output, tmplStr); err != nil {
return fmt.Errorf("output status for %q: %w", id, err)
}
continue
case "table": // table output is after this switch block
default:
return fmt.Errorf("output option cannot be %s", output)
return fmt.Errorf("marshal status to JSON for %q: %w", id, err)
}

// otherwise output in table format
fmt.Printf("ID: %s\n", image.Id)
for _, tag := range image.RepoTags {
fmt.Printf("Tag: %s\n", tag)
}
for _, digest := range image.RepoDigests {
fmt.Printf("Digest: %s\n", digest)
}
size := units.HumanSizeWithPrecision(float64(image.GetSize_()), 3)
fmt.Printf("Size: %s\n", size)
if verbose {
fmt.Printf("Info: %v\n", r.GetInfo())
if output == "table" {
outputImageStatusTable(r, verbose)
} else {
statuses = append(statuses, statusData{json: statusJSON, info: r.Info})
}
}

return nil
return outputStatusData(statuses, output, tmplStr)
},
}

func outputImageStatusTable(r *pb.ImageStatusResponse, verbose bool) {
// otherwise output in table format
fmt.Printf("ID: %s\n", r.Image.Id)
for _, tag := range r.Image.RepoTags {
fmt.Printf("Tag: %s\n", tag)
}
for _, digest := range r.Image.RepoDigests {
fmt.Printf("Digest: %s\n", digest)
}
size := units.HumanSizeWithPrecision(float64(r.Image.GetSize_()), 3)
fmt.Printf("Size: %s\n", size)
if verbose {
fmt.Printf("Info: %v\n", r.GetInfo())
}
}

var removeImageCommand = &cli.Command{
Name: "rmi",
Usage: "Remove one or more images",
Expand Down Expand Up @@ -541,34 +541,31 @@ var imageFsInfoCommand = &cli.Command{
return fmt.Errorf("marshal filesystem info to json: %w", err)
}

switch output {
case "json", "yaml", "go-template":
if err := outputStatusInfo(status, "", nil, output, tmplStr); err != nil {
return fmt.Errorf("output filesystem info: %w", err)
}
return nil
case "table": // table output is after this switch block
default:
return fmt.Errorf("output option cannot be %s", output)
}

tablePrintFileSystem := func(fileLabel string, filesystem []*pb.FilesystemUsage) {
fmt.Printf("%s Filesystem \n", fileLabel)
for i, val := range filesystem {
fmt.Printf("TimeStamp[%d]: %d\n", i, val.Timestamp)
fmt.Printf("Disk[%d]: %s\n", i, units.HumanSize(float64(val.UsedBytes.GetValue())))
fmt.Printf("Inodes[%d]: %d\n", i, val.InodesUsed.GetValue())
fmt.Printf("Mountpoint[%d]: %s\n", i, val.FsId.Mountpoint)
}
if output == "table" {
ouputImageFsInfoTable(r)
} else {
return outputStatusData([]statusData{{json: status}}, output, tmplStr)
}
// otherwise output in table format
tablePrintFileSystem("Container", r.ContainerFilesystems)
tablePrintFileSystem("Image", r.ImageFilesystems)

return nil
},
}

func ouputImageFsInfoTable(r *pb.ImageFsInfoResponse) {
tablePrintFileSystem := func(fileLabel string, filesystem []*pb.FilesystemUsage) {
fmt.Printf("%s Filesystem \n", fileLabel)
for i, val := range filesystem {
fmt.Printf("TimeStamp[%d]: %d\n", i, val.Timestamp)
fmt.Printf("Disk[%d]: %s\n", i, units.HumanSize(float64(val.UsedBytes.GetValue())))
fmt.Printf("Inodes[%d]: %d\n", i, val.InodesUsed.GetValue())
fmt.Printf("Mountpoint[%d]: %s\n", i, val.FsId.Mountpoint)
}
}
// otherwise output in table format
tablePrintFileSystem("Container", r.ContainerFilesystems)
tablePrintFileSystem("Image", r.ImageFilesystems)
}

func parseCreds(creds string) (string, string, error) {
if creds == "" {
return "", "", errors.New("credentials can't be empty")
Expand Down
7 changes: 4 additions & 3 deletions cmd/crictl/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ func Info(cliContext *cli.Context, client internalapi.RuntimeService) error {
return err
}

status, err := protobufObjectToJSON(r.Status)
statusJSON, err := protobufObjectToJSON(r.Status)
if err != nil {
return err
return fmt.Errorf("create status JSON: %w", err)
}
handlers, err := json.Marshal(r.RuntimeHandlers) // protobufObjectToJSON cannot be used
if err != nil {
return err
}
return outputStatusInfo(status, string(handlers), r.Info, cliContext.String("output"), cliContext.String("template"))
data := []statusData{{json: statusJSON, runtimeHandlers: string(handlers), info: r.Info}}
return outputStatusData(data, cliContext.String("output"), cliContext.String("template"))
}
76 changes: 43 additions & 33 deletions cmd/crictl/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,14 +229,17 @@ var podStatusCommand = &cli.Command{
if err != nil {
return err
}
for i := 0; i < c.NArg(); i++ {
id := c.Args().Get(i)

err := PodSandboxStatus(runtimeClient, id, c.String("output"), c.Bool("quiet"), c.String("template"))
if err != nil {
return fmt.Errorf("getting the pod sandbox status for %q: %w", id, err)
}
if err := podSandboxStatus(
runtimeClient,
c.Args().Slice(),
c.String("output"),
c.Bool("quiet"),
c.String("template"),
); err != nil {
return fmt.Errorf("get the status of pod sandboxes: %w", err)
}

return nil
},
}
Expand Down Expand Up @@ -397,42 +400,51 @@ func marshalPodSandboxStatus(ps *pb.PodSandboxStatus) (string, error) {
return marshalMapInOrder(jsonMap, *ps)
}

// PodSandboxStatus sends a PodSandboxStatusRequest to the server, and parses
// podSandboxStatus sends a PodSandboxStatusRequest to the server, and parses
// the returned PodSandboxStatusResponse.
func PodSandboxStatus(client internalapi.RuntimeService, id, output string, quiet bool, tmplStr string) error {
// nolint:dupl // pods and containers are similar, but still different
func podSandboxStatus(client internalapi.RuntimeService, ids []string, output string, quiet bool, tmplStr string) error {
verbose := !(quiet)
if output == "" { // default to json output
output = "json"
}
if id == "" {
if len(ids) == 0 {
return errors.New("ID cannot be empty")
}

request := &pb.PodSandboxStatusRequest{
PodSandboxId: id,
Verbose: verbose,
}
logrus.Debugf("PodSandboxStatusRequest: %v", request)
r, err := InterruptableRPC(nil, func(ctx context.Context) (*pb.PodSandboxStatusResponse, error) {
return client.PodSandboxStatus(ctx, id, verbose)
})
logrus.Debugf("PodSandboxStatusResponse: %v", r)
if err != nil {
return err
}
statuses := []statusData{}
for _, id := range ids {
request := &pb.PodSandboxStatusRequest{
PodSandboxId: id,
Verbose: verbose,
}
logrus.Debugf("PodSandboxStatusRequest: %v", request)
r, err := InterruptableRPC(nil, func(ctx context.Context) (*pb.PodSandboxStatusResponse, error) {
return client.PodSandboxStatus(ctx, id, verbose)
})

logrus.Debugf("PodSandboxStatusResponse: %v", r)
if err != nil {
return fmt.Errorf("get pod sandbox status: %w", err)
}

statusJSON, err := marshalPodSandboxStatus(r.Status)
if err != nil {
return fmt.Errorf("marshal pod sandbox status: %w", err)
}

if output == "table" {
outputPodSandboxStatusTable(r, verbose)
} else {
statuses = append(statuses, statusData{json: statusJSON, info: r.Info})
}

status, err := marshalPodSandboxStatus(r.Status)
if err != nil {
return err
}
switch output {
case "json", "yaml", "go-template":
return outputStatusInfo(status, "", r.Info, output, tmplStr)
case "table": // table output is after this switch block
default:
return fmt.Errorf("output option cannot be %s", output)
}

return outputStatusData(statuses, output, tmplStr)
}

func outputPodSandboxStatusTable(r *pb.PodSandboxStatusResponse, verbose bool) {
// output in table format by default.
fmt.Printf("ID: %s\n", r.Status.Id)
if r.Status.Metadata != nil {
Expand Down Expand Up @@ -472,8 +484,6 @@ func PodSandboxStatus(client internalapi.RuntimeService, id, output string, quie
if verbose {
fmt.Printf("Info: %v\n", r.GetInfo())
}

return nil
}

// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
Expand Down
Loading

0 comments on commit a55af6c

Please sign in to comment.