Skip to content

Commit fa6c0a3

Browse files
authored
Merge pull request #1446 from laozc/windows-info
Fixes invalid JSON in crictl info
2 parents 7654680 + 88df400 commit fa6c0a3

File tree

2 files changed

+162
-14
lines changed

2 files changed

+162
-14
lines changed

cmd/crictl/util.go

+29-14
Original file line numberDiff line numberDiff line change
@@ -245,37 +245,52 @@ func outputStatusInfo(status, handlers string, info map[string]string, format st
245245
}
246246
sort.Strings(keys)
247247

248-
jsonInfo := "{" + "\"status\":" + status + ","
248+
infoMap := map[string]interface{}{}
249+
250+
if status != "" {
251+
var statusVal map[string]interface{}
252+
err := json.Unmarshal([]byte(status), &statusVal)
253+
if err != nil {
254+
return err
255+
}
256+
infoMap["status"] = statusVal
257+
}
258+
249259
if handlers != "" {
250-
jsonInfo += "\"runtimeHandlers\":" + handlers + ","
260+
var handlersVal []*interface{}
261+
err := json.Unmarshal([]byte(handlers), &handlersVal)
262+
if err != nil {
263+
return err
264+
}
265+
if handlersVal != nil {
266+
infoMap["runtimeHandlers"] = handlersVal
267+
}
251268
}
269+
252270
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-
}
271+
infoMap[k] = strings.Trim(info[k], "\"")
272+
}
273+
274+
jsonInfo, err := json.Marshal(infoMap)
275+
if err != nil {
276+
return err
260277
}
261-
jsonInfo = jsonInfo[:len(jsonInfo)-1]
262-
jsonInfo += "}"
263278

264279
switch format {
265280
case "yaml":
266-
yamlInfo, err := yaml.JSONToYAML([]byte(jsonInfo))
281+
yamlInfo, err := yaml.JSONToYAML(jsonInfo)
267282
if err != nil {
268283
return err
269284
}
270285
fmt.Println(string(yamlInfo))
271286
case "json":
272287
var output bytes.Buffer
273-
if err := json.Indent(&output, []byte(jsonInfo), "", " "); err != nil {
288+
if err := json.Indent(&output, jsonInfo, "", " "); err != nil {
274289
return err
275290
}
276291
fmt.Println(output.String())
277292
case "go-template":
278-
output, err := tmplExecuteRawJSON(tmplStr, jsonInfo)
293+
output, err := tmplExecuteRawJSON(tmplStr, string(jsonInfo))
279294
if err != nil {
280295
return err
281296
}

cmd/crictl/util_test.go

+133
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,135 @@ 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+
}
75+
76+
func TestOutputStatusInfo(t *testing.T) {
77+
const (
78+
statusResponse = `{"conditions":[
79+
{
80+
"message": "no network config found in C:\\Program Files",
81+
"reason": "NetworkPluginNotReady",
82+
"status": false,
83+
"type": "NetworkReady"
84+
}
85+
]}`
86+
handlerResponse = `[
87+
{
88+
"features": {
89+
"recursive_read_only_mounts": true
90+
},
91+
"name": "runc"
92+
},
93+
{
94+
"features": {
95+
"recursive_read_only_mounts": true,
96+
"user_namespaces": true
97+
},
98+
"name": "crun"
99+
}
100+
]`
101+
emptyResponse = ""
102+
)
103+
testCases := []struct {
104+
name string
105+
status string
106+
handlers string
107+
info map[string]string
108+
format string
109+
tmplStr string
110+
expectedOut string
111+
}{
112+
{
113+
name: "YAML format",
114+
status: statusResponse,
115+
handlers: handlerResponse,
116+
info: map[string]string{"key1": "value1", "key2": "/var/lib"},
117+
format: "yaml",
118+
tmplStr: "",
119+
expectedOut: "key1: value1\nkey2: /var/lib\nruntimeHandlers:\n- features:\n recursive_read_only_mounts: true\n name: runc\n- features:\n recursive_read_only_mounts: true\n user_namespaces: true\n name: crun\nstatus:\n conditions:\n - message: no network config found in C:\\Program Files\n reason: NetworkPluginNotReady\n status: false\n type: NetworkReady",
120+
},
121+
{
122+
name: "YAML format with empty status response",
123+
status: emptyResponse,
124+
handlers: handlerResponse,
125+
info: map[string]string{"key1": "value1", "key2": "/var/lib"},
126+
format: "yaml",
127+
tmplStr: "",
128+
expectedOut: "key1: value1\nkey2: /var/lib\nruntimeHandlers:\n- features:\n recursive_read_only_mounts: true\n name: runc\n- features:\n recursive_read_only_mounts: true\n user_namespaces: true\n name: crun",
129+
},
130+
{
131+
name: "YAML format with empty handlers response",
132+
status: statusResponse,
133+
handlers: emptyResponse,
134+
info: map[string]string{"key1": "value1", "key2": "/var/lib"},
135+
format: "yaml",
136+
tmplStr: "",
137+
expectedOut: "key1: value1\nkey2: /var/lib\nstatus:\n conditions:\n - message: no network config found in C:\\Program Files\n reason: NetworkPluginNotReady\n status: false\n type: NetworkReady",
138+
},
139+
{
140+
name: "JSON format",
141+
status: statusResponse,
142+
handlers: handlerResponse,
143+
info: map[string]string{"key1": "\"value1\"", "key2": "\"C:\\ProgramFiles\""},
144+
format: "json",
145+
tmplStr: "",
146+
expectedOut: "{\n \"key1\": \"value1\",\n \"key2\": \"C:\\\\ProgramFiles\",\n \"runtimeHandlers\": [\n {\n \"features\": {\n \"recursive_read_only_mounts\": true\n },\n \"name\": \"runc\"\n },\n {\n \"features\": {\n \"recursive_read_only_mounts\": true,\n \"user_namespaces\": true\n },\n \"name\": \"crun\"\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}",
147+
},
148+
{
149+
name: "Go template format",
150+
status: statusResponse,
151+
handlers: handlerResponse,
152+
info: map[string]string{"key1": "value1", "key2": "value2"},
153+
format: "go-template",
154+
tmplStr: `NetworkReady: {{ (index .status.conditions 0).status }}`,
155+
expectedOut: "NetworkReady: false",
156+
},
157+
}
158+
159+
// Run tests
160+
for _, tc := range testCases {
161+
t.Run(tc.name, func(t *testing.T) {
162+
captureOutput := func(f func() error) (string, error) {
163+
var err error
164+
old := os.Stdout
165+
166+
r, w, _ := os.Pipe()
167+
os.Stdout = w
168+
defer func() {
169+
os.Stdout = old
170+
}()
171+
172+
err = f()
173+
if err != nil {
174+
return "", err
175+
}
176+
177+
err = w.Close()
178+
if err != nil {
179+
return "", err
180+
}
181+
182+
out, err := io.ReadAll(r)
183+
return strings.TrimRight(string(out), "\n"), err
184+
}
185+
186+
outStr, err := captureOutput(func() error {
187+
err := outputStatusInfo(tc.status, tc.handlers, tc.info, tc.format, tc.tmplStr)
188+
if err != nil {
189+
t.Errorf("Unexpected error: %v", err)
190+
}
191+
return nil
192+
})
67193

194+
if err != nil {
195+
Expect(err).To(BeNil())
196+
}
197+
198+
if outStr != tc.expectedOut {
199+
t.Errorf("Expected output:\n%s\nGot:\n%s", tc.expectedOut, outStr)
200+
}
68201
})
69202
}
70203
}

0 commit comments

Comments
 (0)