Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix invalid JSON for multiple images/containers/pods #1493

Merged
merged 1 commit into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 := range c.NArg() {
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 := range c.NArg() {
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 := range c.NArg() {
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
Loading