2025-11-17 15:08:00 +00:00
|
|
|
|
# Docker Module - Development Hints
|
|
|
|
|
|
|
2025-11-24 12:20:30 +00:00
|
|
|
|
## OOP Refactoring - Clean Architecture (2025-11-24)
|
|
|
|
|
|
|
|
|
|
|
|
### Architecture Changes
|
|
|
|
|
|
The module has been restructured to follow a clean OOP Facade pattern:
|
|
|
|
|
|
- **DockerHost** is now the single entry point for all Docker operations
|
|
|
|
|
|
- All resource classes extend abstract `DockerResource` base class
|
|
|
|
|
|
- Static methods are prefixed with `_` to indicate internal use
|
|
|
|
|
|
- Public API is exclusively through DockerHost methods
|
|
|
|
|
|
|
|
|
|
|
|
### Key Changes
|
|
|
|
|
|
|
|
|
|
|
|
**1. Factory Pattern**
|
|
|
|
|
|
- All resource creation/retrieval goes through DockerHost:
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// Old (deprecated):
|
|
|
|
|
|
const container = await DockerContainer.getContainers(dockerHost);
|
|
|
|
|
|
const network = await DockerNetwork.createNetwork(dockerHost, descriptor);
|
|
|
|
|
|
|
|
|
|
|
|
// New (clean API):
|
2025-11-24 13:27:10 +00:00
|
|
|
|
const containers = await dockerHost.listContainers();
|
2025-11-24 12:20:30 +00:00
|
|
|
|
const network = await dockerHost.createNetwork(descriptor);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**2. Container Management Methods Added**
|
|
|
|
|
|
The DockerContainer class now has full CRUD and streaming operations:
|
|
|
|
|
|
|
|
|
|
|
|
**Lifecycle:**
|
|
|
|
|
|
- `container.start()` - Start container
|
|
|
|
|
|
- `container.stop(options?)` - Stop container
|
|
|
|
|
|
- `container.remove(options?)` - Remove container
|
|
|
|
|
|
- `container.refresh()` - Reload state
|
|
|
|
|
|
|
|
|
|
|
|
**Information:**
|
|
|
|
|
|
- `container.inspect()` - Get detailed info
|
|
|
|
|
|
- `container.logs(options)` - Get logs as string (one-shot)
|
|
|
|
|
|
- `container.stats(options)` - Get stats
|
|
|
|
|
|
|
|
|
|
|
|
**Streaming & Interactive:**
|
|
|
|
|
|
- `container.streamLogs(options)` - Stream logs continuously (follow mode)
|
|
|
|
|
|
- `container.attach(options)` - Attach to main process (PID 1) with bidirectional stream
|
|
|
|
|
|
- `container.exec(command, options)` - Execute commands in container interactively
|
|
|
|
|
|
|
|
|
|
|
|
**Example - Stream Logs:**
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const container = await dockerHost.getContainerById('abc123');
|
|
|
|
|
|
const logStream = await container.streamLogs({ timestamps: true });
|
|
|
|
|
|
|
|
|
|
|
|
logStream.on('data', (chunk) => {
|
|
|
|
|
|
console.log(chunk.toString());
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Example - Attach to Container:**
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const { stream, close } = await container.attach({
|
|
|
|
|
|
stdin: true,
|
|
|
|
|
|
stdout: true,
|
|
|
|
|
|
stderr: true
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Pipe to/from process
|
|
|
|
|
|
process.stdin.pipe(stream);
|
|
|
|
|
|
stream.pipe(process.stdout);
|
|
|
|
|
|
|
|
|
|
|
|
// Later: detach
|
|
|
|
|
|
await close();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Example - Execute Command:**
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const { stream, close } = await container.exec('ls -la /app', {
|
|
|
|
|
|
tty: true
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
stream.on('data', (chunk) => {
|
|
|
|
|
|
console.log(chunk.toString());
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
stream.on('end', async () => {
|
|
|
|
|
|
await close();
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**3. DockerResource Base Class**
|
|
|
|
|
|
All resource classes now extend `DockerResource`:
|
|
|
|
|
|
- Consistent `dockerHost` property (not `dockerHostRef`)
|
|
|
|
|
|
- Required `refresh()` method
|
|
|
|
|
|
- Standardized constructor pattern
|
|
|
|
|
|
|
|
|
|
|
|
**4. ImageStore Encapsulation**
|
|
|
|
|
|
- `dockerHost.imageStore` is now private
|
|
|
|
|
|
- Use `dockerHost.storeImage(name, stream)` instead
|
|
|
|
|
|
- Use `dockerHost.retrieveImage(name)` instead
|
|
|
|
|
|
|
|
|
|
|
|
**5. Creation Descriptors Support Both Primitives and Instances**
|
|
|
|
|
|
Interfaces now accept both strings and class instances:
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// Both work:
|
|
|
|
|
|
await dockerHost.createService({
|
|
|
|
|
|
image: 'nginx:latest', // String
|
|
|
|
|
|
networks: ['my-network'], // String array
|
|
|
|
|
|
secrets: ['my-secret'] // String array
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await dockerHost.createService({
|
|
|
|
|
|
image: imageInstance, // DockerImage instance
|
|
|
|
|
|
networks: [networkInstance], // DockerNetwork array
|
|
|
|
|
|
secrets: [secretInstance] // DockerSecret array
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Migration Guide
|
|
|
|
|
|
Replace all static method calls with dockerHost methods:
|
2025-11-24 13:27:10 +00:00
|
|
|
|
- `DockerContainer.getContainers(host)` → `dockerHost.listContainers()`
|
2025-11-24 12:20:30 +00:00
|
|
|
|
- `DockerImage.createFromRegistry(host, opts)` → `dockerHost.createImageFromRegistry(opts)`
|
|
|
|
|
|
- `DockerService.createService(host, desc)` → `dockerHost.createService(desc)`
|
|
|
|
|
|
- `dockerHost.imageStore.storeImage(...)` → `dockerHost.storeImage(...)`
|
|
|
|
|
|
|
2025-11-17 15:08:00 +00:00
|
|
|
|
## smartrequest v5+ Migration (2025-11-17)
|
|
|
|
|
|
|
|
|
|
|
|
### Breaking Change
|
|
|
|
|
|
smartrequest v5.0.0+ returns web `ReadableStream` objects (Web Streams API) instead of Node.js streams.
|
|
|
|
|
|
|
|
|
|
|
|
### Solution Implemented
|
|
|
|
|
|
All streaming methods now convert web ReadableStreams to Node.js streams using:
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
plugins.smartstream.nodewebhelpers.convertWebReadableToNodeReadable(webStream)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Files Modified
|
|
|
|
|
|
- `ts/classes.host.ts`:
|
|
|
|
|
|
- `requestStreaming()` - Converts web stream to Node.js stream before returning
|
|
|
|
|
|
- `getEventObservable()` - Works with converted Node.js stream
|
|
|
|
|
|
|
|
|
|
|
|
- `ts/classes.image.ts`:
|
|
|
|
|
|
- `createFromTarStream()` - Uses converted Node.js stream for event handling
|
|
|
|
|
|
- `exportToTarStream()` - Uses converted Node.js stream for backpressure management
|
|
|
|
|
|
|
|
|
|
|
|
### Testing
|
|
|
|
|
|
- Build: All 11 type errors resolved
|
|
|
|
|
|
- Tests: Node.js tests pass (DockerHost, DockerContainer, DockerImage, DockerImageStore)
|
|
|
|
|
|
|
|
|
|
|
|
### Notes
|
|
|
|
|
|
- The conversion maintains backward compatibility with existing code expecting Node.js stream methods (`.on()`, `.emit()`, `.pause()`, `.resume()`)
|
|
|
|
|
|
- smartstream's `nodewebhelpers` module provides bidirectional conversion utilities between web and Node.js streams
|