Skip to content

Commit e4b27e1

Browse files
author
Roy Lin
committed
fix(cli): container inspect returns a JSON array with a Docker-shaped State object
`a3s-box inspect <container>` printed a bare flattened object with a flat `status` string, so `inspect X | jq '.[0].State.Running'` yielded null. It now emits a top-level JSON array (like docker inspect) and adds a nested State {Status, Running, Paused, ExitCode} (a paused container is Running=true, Paused=true, matching Docker), while keeping the existing a3s-native fields.
1 parent 3d7f228 commit e4b27e1

1 file changed

Lines changed: 48 additions & 2 deletions

File tree

src/cli/src/commands/inspect.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,42 @@ pub async fn execute(args: InspectArgs) -> Result<(), Box<dyn std::error::Error>
3838
}
3939
}
4040

41+
/// Docker-shaped `State` sub-object so tooling can read `.[0].State.Running` etc.
42+
#[derive(Serialize)]
43+
struct DockerState {
44+
#[serde(rename = "Status")]
45+
status: String,
46+
#[serde(rename = "Running")]
47+
running: bool,
48+
#[serde(rename = "Paused")]
49+
paused: bool,
50+
#[serde(rename = "ExitCode")]
51+
exit_code: i32,
52+
}
53+
4154
#[derive(Serialize)]
4255
struct InspectView<'a> {
4356
#[serde(flatten)]
4457
record: &'a BoxRecord,
4558
status_detail: status::StatusDetails,
59+
#[serde(rename = "State")]
60+
state: DockerState,
4661
}
4762

4863
fn inspect_json(record: &BoxRecord) -> Result<String, serde_json::Error> {
49-
serde_json::to_string_pretty(&InspectView {
64+
let view = InspectView {
5065
record,
5166
status_detail: status::status_details(record),
52-
})
67+
state: DockerState {
68+
status: record.status.clone(),
69+
// Docker: a paused container is still Running (Running=true, Paused=true).
70+
running: matches!(record.status.as_str(), "running" | "paused"),
71+
paused: record.status == "paused",
72+
exit_code: record.exit_code.unwrap_or(0),
73+
},
74+
};
75+
// `docker inspect` returns a top-level JSON array, even for one container.
76+
serde_json::to_string_pretty(&vec![view])
5377
}
5478

5579
#[cfg(test)]
@@ -64,9 +88,31 @@ mod tests {
6488

6589
let json = inspect_json(&record).unwrap();
6690

91+
// Top-level array (docker inspect) with a Docker-shaped State object.
92+
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
93+
assert!(parsed.is_array());
94+
assert_eq!(parsed[0]["State"]["Status"], "dead");
95+
assert_eq!(parsed[0]["State"]["Running"], false);
96+
assert_eq!(parsed[0]["State"]["ExitCode"], 137);
97+
6798
assert!(json.contains("\"status\": \"dead\""));
6899
assert!(json.contains("\"status_detail\""));
69100
assert!(json.contains("\"summary\": \"dead (Exit 137)\""));
70101
assert!(json.contains("a3s-box restart box"));
71102
}
103+
104+
#[test]
105+
fn test_inspect_state_running_and_paused() {
106+
let running = make_record("id", "box", "running", Some(1));
107+
let parsed: serde_json::Value =
108+
serde_json::from_str(&inspect_json(&running).unwrap()).unwrap();
109+
assert_eq!(parsed[0]["State"]["Running"], true);
110+
assert_eq!(parsed[0]["State"]["Paused"], false);
111+
112+
let paused = make_record("id", "box", "paused", Some(1));
113+
let parsed: serde_json::Value =
114+
serde_json::from_str(&inspect_json(&paused).unwrap()).unwrap();
115+
assert_eq!(parsed[0]["State"]["Running"], true);
116+
assert_eq!(parsed[0]["State"]["Paused"], true);
117+
}
72118
}

0 commit comments

Comments
 (0)