Skip to content

Conversation

@tinovyatkin
Copy link

@tinovyatkin tinovyatkin commented Jan 11, 2026

Summary

This PR adds support for Finch and other nerdctl-based container runtimes, enabling the VS Code Docker extension to work with these alternative container engines.

  • NerdctlClient - Configurable container client for nerdctl-based CLIs (Finch, nerdctl, etc.)
  • NerdctlComposeClient - Compose support with overrides for nerdctl-specific limitations
  • Event streaming - Handles containerd native event format with client-side filtering
  • File operations - Read/write support using temp file workaround
  • Full E2E test coverage - All operations tested and passing for Docker, Podman, and Finch

Architecture

Following reviewer feedback, the implementation uses a generic NerdctlClient with configurable command name rather than a Finch-specific client. This allows supporting any nerdctl-based CLI:

// For Finch
const finchClient = new NerdctlClient('finch', 'Finch', 'Runs container commands using the Finch CLI');

// For nerdctl directly
const nerdctlClient = new NerdctlClient(); // defaults to 'nerdctl'

What is Finch?

Finch is an open source tool for local container development, originally released by AWS in November 2022. It uses containerd and nerdctl under the hood.

Documentation:

Key Implementation Details

containerd Native Events

Finch/nerdctl outputs containerd native events (NOT Docker-compatible):

{
  "Timestamp": "2026-01-10T23:38:26.737324778Z",
  "Topic": "/containers/create",
  "Event": "{\"id\":\"...\",\"image\":\"...\"}"
}

The client translates between these formats and performs client-side filtering since nerdctl doesn't support --since/--until flags.

Compose Limitations

NerdctlComposeClient overrides command generation to exclude unsupported flags:

  • up: excludes --timeout, --no-start, --wait, --watch
  • down: excludes --timeout
  • config: only --services is supported (not --images, --profiles, --volumes)

File Operations Workaround

Nerdctl's cp command advertises stdin/stdout support (-) but it's not fully implemented. This PR uses a temp file workaround.

Test Results

All E2E tests pass for all three container clients:

Client Passing Pending
Docker 55 2
Podman 46 11
Finch 47 10

Test Coverage

  • Container lifecycle (create, start, stop, remove, exec, attach, logs)
  • Image operations (pull, build, tag, push, remove, inspect)
  • Network and volume management
  • File system operations (stat, read, write)
  • Event streaming with containerd format parsing
  • Compose operations (up, down, start, stop, restart, logs, config)

Known Limitations (reflected in pending tests)

  • Context commands skipped (Docker-only feature)
  • Some compose config options skipped for Podman/Finch (unsupported flags)
  • Login/logout skipped (requires registry credentials)

🤖 Generated with Claude Code

This adds full support for AWS Finch as a container runtime, enabling the
VS Code Docker extension to work with Finch-managed containers.

## What is Finch?

Finch is an open source container development tool from AWS that provides
a simple, native client for building, running, and managing containers.
It uses containerd and nerdctl under the hood.

- Homepage: https://aws.amazon.com/finch/
- GitHub: https://github.com/runfinch/finch
- Documentation: https://runfinch.com/docs/

## Implementation Details

### Core Client (`FinchClient.ts`)
- Extends PodmanLikeClient since Finch/nerdctl share similar CLI patterns
- Handles containerd-native event format (different from Docker's format)
- Implements file read/write using temp file workaround (Finch's cp doesn't
  support stdio streaming yet)
- Custom port exposure argument handling for nerdctl compatibility

### Event Stream Support
Finch outputs containerd native events, NOT Docker-compatible events:
- Uses `Topic` field (e.g., `/containers/create`) instead of `Type`/`Action`
- Event payload is a nested JSON string in the `Event` field
- Client-side filtering for types, actions, since/until parameters

### Zod Schemas for CLI Output Parsing
- `FinchListContainerRecord` - Container listing
- `FinchListImageRecord` - Image listing
- `FinchListNetworkRecord` - Network listing
- `FinchInspectContainerRecord` - Container inspection
- `FinchInspectImageRecord` - Image inspection with date validation
- `FinchInspectNetworkRecord` - Network inspection
- `FinchInspectVolumeRecord` - Volume inspection
- `FinchVersionRecord` - Version information
- `FinchEventRecord` - containerd native event format

### Compose Support (`FinchComposeClient.ts`)
Uses Finch's built-in compose command (`finch compose`) which is provided
by nerdctl's compose implementation.

## Testing

E2E tests updated to support Finch via `CONTAINER_CLIENT=finch` env var.
All container operations tested and passing:
- Container lifecycle (create, start, stop, remove, exec, attach, logs)
- Image operations (pull, build, tag, push, remove, inspect)
- Network and volume management
- File system operations (stat, read, write) using temp file workaround
- Event streaming with containerd format parsing
- Compose operations (up, down, start, stop, restart, logs, config)
- Login/logout with registry credentials

### Known Limitations
- Context commands skipped (Docker-only feature)
- File streaming uses temp files (Finch cp doesn't support stdin/stdout)
- Event filtering done client-side (Finch doesn't support --since/--until)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tinovyatkin tinovyatkin requested a review from a team as a code owner January 11, 2026 01:19
@tinovyatkin
Copy link
Author

@microsoft-github-policy-service agree

Fixes based on CODEX_REVIEW.md recommendations:

**High-Priority Fixes:**
1. Fix parseEventTimestamp - relative time math was inverted
   - "1m" now correctly means 1 minute ago (in the past)
   - "-1s" now correctly means 1 second from now (in the future)

2. Fix inspect parsing for multi-target calls
   - Added parseJsonArrayOrLines() helper to handle both:
     - JSON arrays (nerdctl default behavior)
     - Newline-separated JSON objects (when --format used)

3. Fix shell injection risks in readFile/writeFile
   - Use /bin/sh instead of bash for portability
   - Added shellEscapeSingleQuote() for proper path escaping
   - Properly escape container paths and command names

**Medium-Priority Improvements:**
4. Fix incorrect override parameter types
   - Changed parseInspectNetworksCommandOutput to use InspectNetworksCommandOptions
   - Changed parseInspectVolumesCommandOutput to use InspectVolumesCommandOptions

5. Improve epoch fallbacks
   - Use new Date() instead of new Date(0) as fallback (less misleading)
   - In strict mode, throw errors for missing/invalid dates

6. Fix volume label parsing
   - Use parseDockerLikeLabels() to properly handle "key=value" string format

7. Deduplicate size parsing
   - Use tryParseSize() utility in FinchListImageRecord for consistency

8. Handle unsupported labels filter in getEventStream
   - Throw CommandNotSupportedError when labels filter is provided
   - Document limitation clearly in JSDoc

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bwateratmsft
Copy link
Collaborator

Given that Finch is basically just nerdctl, I think it would make more sense for this to be a nerdctl client, but with the ability to accept a different configured command name (i.e. finch instead of nerdctl). DockerClient supports something similar to this now, by way of accepting the base command to run as a constructor argument. This is primarily to allow users to set a specific absolute path instead of resolving docker from the PATH env var, but this same capability could be used for nerdctl vs finch.

nerdctl support has also been requested previously: microsoft/vscode-containers#119

Can you rename the classes/etc. accordingly?

Since Finch is essentially a wrapper around nerdctl, rename the client
to NerdctlClient for broader applicability. The client now:

- Defaults to 'nerdctl' command (was 'finch')
- Accepts configurable command name via constructor parameter
- Supports both nerdctl and finch (via `new NerdctlClient('finch')`)

This change addresses review feedback and enables support for both
direct nerdctl users and AWS Finch users with the same codebase.

Renamed files:
- FinchClient → NerdctlClient
- FinchComposeClient → NerdctlComposeClient
- All supporting record/schema files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tinovyatkin tinovyatkin force-pushed the feature/add-finch-support branch from f49d1cc to 63b293a Compare January 12, 2026 23:49
tinovyatkin and others added 2 commits January 13, 2026 01:18
- NerdctlComposeClient: Override getUpCommandArgs to exclude unsupported
  flags (--timeout, --no-start, --wait, --watch)
- NerdctlComposeClient: Override getDownCommandArgs to exclude --timeout
- Export withCommonOrchestratorArgs and withComposeArg from base class
- Update orchestrator E2E tests:
  - Fix version check regex to match docker/podman/nerdctl compose outputs
  - Skip --no-start tests for finch (not supported)
  - Skip config --images/--profiles/--volumes for non-Docker clients
  - Use SIGTERM-responsive containers for faster shutdown

Test results:
- Docker: 55 passing, 2 pending
- Podman: 46 passing, 11 pending
- Finch: 40 passing, 10 pending, 7 failing (pre-existing nerdctl issues)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In nerdctl/finch, containers that exit immediately lose their port
mappings and can't be used for exec/filesystem operations. Fixed by:

- Add entrypoint/command to keep test containers running
- Use SIGTERM trap for fast graceful shutdown
- Fix LogsForContainerCommand to use simple echo entrypoint

All tests now pass for Docker, Podman, and Finch.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tinovyatkin
Copy link
Author

Thanks @bwateratmsft! Great suggestion - you're right that Finch is essentially nerdctl under the hood.

I've refactored as suggested:

  • Renamed FinchClientNerdctlClient with configurable command name (defaults to 'nerdctl')
  • Renamed FinchComposeClientNerdctlComposeClient with the same pattern
  • Updated all record schemas and related files accordingly

The client can now support any nerdctl-based CLI:

// For Finch
new NerdctlClient('finch', 'Finch', 'Runs container commands using the Finch CLI');

// For nerdctl directly  
new NerdctlClient(); // defaults to 'nerdctl'

I also added overrides in NerdctlComposeClient for compose flags that nerdctl doesn't support (--timeout for up/down, --no-start, --wait, --watch).

All E2E tests pass for Docker, Podman, and Finch.

@tinovyatkin tinovyatkin changed the title Add Finch container client support Add NerdctlClient for Finch and nerdctl support Jan 13, 2026
tinovyatkin and others added 3 commits January 13, 2026 15:05
- Use Zod transform to parse nested Event JSON string during schema validation
- Replace passthrough() with looseObject() per Zod v4 migration guide
- Rename parseContainerdEventPayload to getActorFromEventPayload since Event is now pre-parsed
- Simplify actor extraction logic using nullish coalescing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create ZodTransforms.ts with reusable transformation schemas:
- dateStringSchema / dateStringWithFallbackSchema for date parsing
- booleanStringSchema for "true"/"false" string to boolean
- labelsStringSchema / labelsSchema for Docker-like label parsing
- osTypeStringSchema, architectureStringSchema for enum normalization
- protocolStringSchema, numericStringSchema, containerStateStringSchema

Apply transforms to Nerdctl record files:
- NerdctlListNetworkRecord: boolean strings, labels, dates
- NerdctlInspectVolumeRecord: labels, dates
- NerdctlInspectNetworkRecord: dates
- NerdctlListContainerRecord: labels
- NerdctlListImageRecord: dates
- NerdctlInspectContainerRecord: dates
- NerdctlInspectImageRecord: dates, architecture, OS

This moves transformation logic from normalize functions into Zod schemas,
leveraging Zod's transform() for cleaner, more declarative parsing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use UTC fallback in dateStringWithFallbackSchema for consistency
- Validate EventType and EventAction against schemas before returning
  from parseContainerdTopic to ensure type safety

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bwateratmsft bwateratmsft linked an issue Jan 16, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nerdctl/Finch Containers Support

2 participants