Skip to content

Commit 39a77d4

Browse files
committed
Fixes invalid JSON in crictl info
containerd on Windows may not escape the return message which may result in invalid JSON in crictl info. Message from containerd: cni config load failed: no network config found in C:\Program Files \containerd\cni\conf: cni plugin not initialized: failed to load cni config
1 parent d7be47c commit 39a77d4

File tree

2 files changed

+123
-15
lines changed

2 files changed

+123
-15
lines changed

cmd/crictl/util.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -245,37 +245,48 @@ func outputStatusInfo(status, handlers string, info map[string]string, format st
245245
}
246246
sort.Strings(keys)
247247

248-
jsonInfo := "{" + "\"status\":" + status + ","
249-
if handlers != "" {
250-
jsonInfo += "\"runtimeHandlers\":" + handlers + ","
248+
infoMap := map[string]interface{}{}
249+
250+
var statusVal map[string]interface{}
251+
err := json.Unmarshal([]byte(status), &statusVal)
252+
if err != nil {
253+
return err
254+
}
255+
infoMap["status"] = statusVal
256+
257+
var handlersVal map[string]interface{}
258+
err = json.Unmarshal([]byte(handlers), &handlersVal)
259+
if err != nil {
260+
return err
251261
}
262+
if handlersVal != nil {
263+
infoMap["runtimeHandlers"] = handlersVal
264+
}
265+
252266
for _, k := range keys {
253-
var res interface{}
254-
// We attempt to convert key into JSON if possible else use it directly
255-
if err := json.Unmarshal([]byte(info[k]), &res); err != nil {
256-
jsonInfo += "\"" + k + "\"" + ":" + "\"" + info[k] + "\","
257-
} else {
258-
jsonInfo += "\"" + k + "\"" + ":" + info[k] + ","
259-
}
267+
infoMap[k] = strings.Trim(info[k], "\"")
268+
}
269+
270+
jsonInfo, err := json.Marshal(infoMap)
271+
if err != nil {
272+
return err
260273
}
261-
jsonInfo = jsonInfo[:len(jsonInfo)-1]
262-
jsonInfo += "}"
263274

264275
switch format {
265276
case "yaml":
266-
yamlInfo, err := yaml.JSONToYAML([]byte(jsonInfo))
277+
yamlInfo, err := yaml.JSONToYAML(jsonInfo)
267278
if err != nil {
268279
return err
269280
}
270281
fmt.Println(string(yamlInfo))
271282
case "json":
272283
var output bytes.Buffer
273-
if err := json.Indent(&output, []byte(jsonInfo), "", " "); err != nil {
284+
if err := json.Indent(&output, jsonInfo, "", " "); err != nil {
274285
return err
275286
}
276287
fmt.Println(output.String())
277288
case "go-template":
278-
output, err := tmplExecuteRawJSON(tmplStr, jsonInfo)
289+
output, err := tmplExecuteRawJSON(tmplStr, string(jsonInfo))
279290
if err != nil {
280291
return err
281292
}

cmd/crictl/util_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"io"
21+
"os"
22+
"strings"
2023
"testing"
24+
25+
. "github.com/onsi/gomega"
2126
)
2227

2328
func TestNameFilterByRegex(t *testing.T) {
@@ -64,7 +69,99 @@ func TestNameFilterByRegex(t *testing.T) {
6469
if r != tc.isMatch {
6570
t.Errorf("expected matched to be %v; actual result is %v", tc.isMatch, r)
6671
}
72+
})
73+
}
74+
}
6775

76+
func TestOutputStatusInfo(t *testing.T) {
77+
const (
78+
statusResponse = `{"conditions":[{
79+
"message": "no network config found in C:\\Program Files",
80+
"reason": "NetworkPluginNotReady",
81+
"status": false,
82+
"type": "NetworkReady"
83+
}]}`
84+
)
85+
testCases := []struct {
86+
name string
87+
status string
88+
handlers string
89+
info map[string]string
90+
format string
91+
tmplStr string
92+
expectedOut string
93+
}{
94+
{
95+
name: "YAML format",
96+
status: statusResponse,
97+
handlers: `{"handlers":["handler1","handler2"]}`,
98+
info: map[string]string{"key1": "value1", "key2": "/var/lib"},
99+
format: "yaml",
100+
tmplStr: "",
101+
expectedOut: "key1: value1\nkey2: /var/lib\nruntimeHandlers:\n handlers:\n - handler1\n - handler2\nstatus:\n conditions:\n - message: no network config found in C:\\Program Files\n reason: NetworkPluginNotReady\n status: false\n type: NetworkReady",
102+
},
103+
{
104+
name: "JSON format",
105+
status: statusResponse,
106+
handlers: `{"handlers":["handler1","handler2"]}`,
107+
info: map[string]string{"key1": "\"value1\"", "key2": "\"C:\\ProgramFiles\""},
108+
format: "json",
109+
tmplStr: "",
110+
expectedOut: "{\n \"key1\": \"value1\",\n \"key2\": \"C:\\\\ProgramFiles\",\n \"runtimeHandlers\": {\n \"handlers\": [\n \"handler1\",\n \"handler2\"\n ]\n },\n \"status\": {\n \"conditions\": [\n {\n \"message\": \"no network config found in C:\\\\Program Files\",\n \"reason\": \"NetworkPluginNotReady\",\n \"status\": false,\n \"type\": \"NetworkReady\"\n }\n ]\n }\n}",
111+
},
112+
{
113+
name: "Go template format",
114+
status: statusResponse,
115+
handlers: `{"handlers":["handler1","handler2"]}`,
116+
info: map[string]string{"key1": "value1", "key2": "value2"},
117+
format: "go-template",
118+
tmplStr: `NetworkReady: {{ (index .status.conditions 0).status }}`,
119+
expectedOut: "NetworkReady: false",
120+
},
121+
}
122+
123+
// Run tests
124+
for _, tc := range testCases {
125+
t.Run(tc.name, func(t *testing.T) {
126+
captureOutput := func(f func() error) (string, error) {
127+
var err error
128+
old := os.Stdout
129+
130+
r, w, _ := os.Pipe()
131+
os.Stdout = w
132+
defer func() {
133+
os.Stdout = old
134+
}()
135+
136+
err = f()
137+
if err != nil {
138+
return "", err
139+
}
140+
141+
err = w.Close()
142+
if err != nil {
143+
return "", err
144+
}
145+
146+
out, err := io.ReadAll(r)
147+
return strings.TrimRight(string(out), "\n"), err
148+
}
149+
150+
outStr, err := captureOutput(func() error {
151+
err := outputStatusInfo(tc.status, tc.handlers, tc.info, tc.format, tc.tmplStr)
152+
if err != nil {
153+
t.Errorf("Unexpected error: %v", err)
154+
}
155+
return nil
156+
})
157+
158+
if err != nil {
159+
Expect(err).To(BeNil())
160+
}
161+
162+
if outStr != tc.expectedOut {
163+
t.Errorf("Expected output:\n%s\nGot:\n%s", tc.expectedOut, outStr)
164+
}
68165
})
69166
}
70167
}

0 commit comments

Comments
 (0)