Skip to content

Commit 782df44

Browse files
authored
Reimplement containers list using Dash API endpoints (#12893)
1 parent 175772f commit 782df44

11 files changed

Lines changed: 885 additions & 289 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"wrangler": minor
3+
"@cloudflare/containers-shared": patch
4+
---
5+
6+
Rewrite `wrangler containers list` to use the paginated Dash API endpoint
7+
8+
`wrangler containers list` now fetches from the `/dash/applications` endpoint instead of `/applications`, displaying results in a paginated table with columns for ID, Name, State, Live Instances, and Last Modified. Container state is derived from health instance counters (active, degraded, provisioning, ready).
9+
10+
The command supports `--per-page` (default 25) for interactive pagination with Enter to load more and q/Esc to quit, and `--json` for machine-readable output. Non-interactive environments load all results in a single request.

packages/containers-shared/src/client/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export type { CreateImageRegistryRequestBody } from "./models/CreateImageRegistr
5757
export type { CreateSSHPublicKeyError } from "./models/CreateSSHPublicKeyError";
5858
export type { CreateSSHPublicKeyRequestBody } from "./models/CreateSSHPublicKeyRequestBody";
5959
export type { CustomerImageRegistry } from "./models/CustomerImageRegistry";
60+
export type { DashApplication } from "./models/DashApplication";
6061
export type { DashApplicationDurableObjectInstance } from "./models/DashApplicationDurableObjectInstance";
6162
export type { DashApplicationInstance } from "./models/DashApplicationInstance";
6263
export type { DashApplicationInstances } from "./models/DashApplicationInstances";
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
import type { ApplicationHealth } from "./ApplicationHealth";
6+
import type { ApplicationID } from "./ApplicationID";
7+
import type { ApplicationName } from "./ApplicationName";
8+
import type { Image } from "./Image";
9+
import type { ISO8601Timestamp } from "./ISO8601Timestamp";
10+
11+
/**
12+
* Summary representation of a container application, returned by the Dash endpoint.
13+
* Contains only the fields needed for list display, not the full configuration.
14+
*/
15+
export type DashApplication = {
16+
id: ApplicationID;
17+
created_at: ISO8601Timestamp;
18+
updated_at: ISO8601Timestamp;
19+
name: ApplicationName;
20+
version: number;
21+
instances: number;
22+
image: Image;
23+
health: ApplicationHealth;
24+
};

packages/containers-shared/src/client/services/ApplicationsService.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type { ApplicationStatus } from "../models/ApplicationStatus";
1414
import type { CreateApplicationJobRequest } from "../models/CreateApplicationJobRequest";
1515
import type { CreateApplicationRequest } from "../models/CreateApplicationRequest";
1616
import type { CreateApplicationRolloutRequest } from "../models/CreateApplicationRolloutRequest";
17+
import type { DashApplication } from "../models/DashApplication";
1718
import type { DashApplicationInstances } from "../models/DashApplicationInstances";
1819
import type { DeploymentID } from "../models/DeploymentID";
1920
import type { DeploymentV2 } from "../models/DeploymentV2";
@@ -531,6 +532,33 @@ export class ApplicationsService {
531532
});
532533
}
533534

535+
/**
536+
* List container applications with pagination (Dash endpoint)
537+
* Returns summary application data suitable for list display
538+
* @param perPage Number of results per page
539+
* @param pageToken Token for fetching the next page
540+
* @returns PaginatedResult<DashApplication[]> Paginated list of applications
541+
* @throws ApiError
542+
*/
543+
public static listDashApplications(
544+
perPage?: number,
545+
pageToken?: string
546+
): CancelablePromise<PaginatedResult<DashApplication[]>> {
547+
return requestPaginated(OpenAPI, {
548+
method: "GET",
549+
url: "/dash/applications",
550+
query: {
551+
per_page: perPage,
552+
page_token: pageToken,
553+
},
554+
errors: {
555+
401: `Unauthorized`,
556+
404: `Not found`,
557+
500: `There has been an internal error`,
558+
},
559+
});
560+
}
561+
534562
/**
535563
* List container instances for a given application
536564
* Returns instances and optional durable object instances with pagination support

packages/wrangler/src/__tests__/containers/instances.test.ts

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ describe("containers instances", () => {
135135
);
136136
});
137137

138-
it("should list instances as JSON (non-TTY)", async () => {
138+
it("should render a table (non-TTY)", async () => {
139139
setIsTTY(false);
140140
setWranglerConfig({});
141141
msw.use(
@@ -156,25 +156,18 @@ describe("containers instances", () => {
156156
);
157157
await runWrangler(`containers instances ${APP_ID}`);
158158
expect(std.err).toMatchInlineSnapshot(`""`);
159-
const output = JSON.parse(std.out);
160-
expect(output).toHaveLength(2);
161-
expect(output[0]).toEqual({
162-
id: "11111111-1111-1111-1111-111111111111",
163-
state: "running",
164-
location: "sfo06",
165-
version: 3,
166-
created: "2025-06-01T10:00:00Z",
167-
});
168-
expect(output[1]).toEqual({
169-
id: "22222222-2222-2222-2222-222222222222",
170-
state: "provisioning",
171-
location: "iad01",
172-
version: 2,
173-
created: "2025-06-01T11:00:00Z",
174-
});
159+
expect(std.out).toMatchInlineSnapshot(`
160+
"┌─┬─┬─┬─┬─┐
161+
│ INSTANCE │ STATE │ LOCATION │ VERSION │ CREATED │
162+
├─┼─┼─┼─┼─┤
163+
│ 11111111-1111-1111-1111-111111111111 │ running │ sfo06 │ 3 │ 2025-06-01T10:00:00Z │
164+
├─┼─┼─┼─┼─┤
165+
│ 22222222-2222-2222-2222-222222222222 │ provisioning │ iad01 │ 2 │ 2025-06-01T11:00:00Z │
166+
└─┴─┴─┴─┴─┘"
167+
`);
175168
});
176169

177-
it("should show DO instance IDs and names for DO-backed apps (non-TTY)", async () => {
170+
it("should render DO instance table (non-TTY)", async () => {
178171
setIsTTY(false);
179172
setWranglerConfig({});
180173
msw.use(
@@ -193,26 +186,31 @@ describe("containers instances", () => {
193186
)
194187
);
195188
await runWrangler(`containers instances ${APP_ID}`);
196-
const output = JSON.parse(std.out);
197-
expect(output).toHaveLength(2);
198-
// First row: DO with a running deployment
199-
expect(output[0]).toEqual({
200-
id: "do-instance-1111",
201-
name: "random-76",
202-
state: "running",
203-
location: "dfw01",
204-
version: 57,
205-
created: "2025-06-01T10:00:00Z",
206-
});
207-
// Second row: DO without a running deployment (inactive)
208-
expect(output[1]).toEqual({
209-
id: "do-instance-2222",
210-
name: "random-88",
211-
state: "inactive",
212-
location: null,
213-
version: null,
214-
created: "2025-05-26T10:00:00Z",
215-
});
189+
expect(std.out).toMatchInlineSnapshot(`
190+
"┌─┬─┬─┬─┬─┬─┐
191+
│ INSTANCE │ NAME │ STATE │ LOCATION │ VERSION │ CREATED │
192+
├─┼─┼─┼─┼─┼─┤
193+
│ do-instance-1111 │ random-76 │ running │ dfw01 │ 57 │ 2025-06-01T10:00:00Z │
194+
├─┼─┼─┼─┼─┼─┤
195+
│ do-instance-2222 │ random-88 │ inactive │ - │ - │ 2025-05-26T10:00:00Z │
196+
└─┴─┴─┴─┴─┴─┘"
197+
`);
198+
});
199+
200+
it("should reject --per-page 0", async () => {
201+
setIsTTY(false);
202+
setWranglerConfig({});
203+
await expect(
204+
runWrangler(`containers instances ${APP_ID} --per-page 0`)
205+
).rejects.toThrowError(/--per-page must be at least 1/);
206+
});
207+
208+
it("should reject --per-page with negative value", async () => {
209+
setIsTTY(false);
210+
setWranglerConfig({});
211+
await expect(
212+
runWrangler(`containers instances ${APP_ID} --per-page -1`)
213+
).rejects.toThrowError(/--per-page must be at least 1/);
216214
});
217215

218216
it("should error on invalid ID format", async () => {
@@ -286,10 +284,9 @@ describe("containers instances", () => {
286284
);
287285
await runWrangler(`containers instances ${APP_ID}`);
288286
expect(requestCount).toBe(1);
289-
const output = JSON.parse(std.out);
290-
expect(output).toHaveLength(2);
291-
expect(output[0].id).toBe("11111111-1111-1111-1111-111111111111");
292-
expect(output[1].id).toBe("22222222-2222-2222-2222-222222222222");
287+
// Table output should contain both instances
288+
expect(std.out).toContain("11111111-1111-1111-1111-111111111111");
289+
expect(std.out).toContain("22222222-2222-2222-2222-222222222222");
293290
});
294291

295292
describe("--json", () => {

0 commit comments

Comments
 (0)