mirror of
https://github.com/yusing/godoxy.git
synced 2026-02-24 03:14:50 +01:00
282 lines
6.2 KiB
Markdown
282 lines
6.2 KiB
Markdown
# Agent Pool
|
|
|
|
Thread-safe pool for managing remote Docker agent connections.
|
|
|
|
## Overview
|
|
|
|
The agentpool package provides a centralized pool for storing and retrieving remote agent configurations. It enables GoDoxy to connect to Docker hosts via agent connections instead of direct socket access, enabling secure remote container management.
|
|
|
|
### Primary consumers
|
|
|
|
- `internal/route/provider` - Creates agent-based route providers
|
|
- `internal/docker` - Manages agent-based Docker client connections
|
|
- Configuration loading during startup
|
|
|
|
### Non-goals
|
|
|
|
- Agent lifecycle management (handled by `agent/pkg/agent`)
|
|
- Agent health monitoring
|
|
- Agent authentication/authorization
|
|
|
|
### Stability
|
|
|
|
Stable internal package. The pool uses `xsync.Map` for lock-free concurrent access.
|
|
|
|
## Public API
|
|
|
|
### Exported types
|
|
|
|
```go
|
|
type Agent struct {
|
|
*agent.AgentConfig
|
|
httpClient *http.Client
|
|
fasthttpHcClient *fasthttp.Client
|
|
}
|
|
```
|
|
|
|
### Exported functions
|
|
|
|
```go
|
|
func Add(cfg *agent.AgentConfig) (added bool)
|
|
```
|
|
|
|
Adds an agent to the pool. Returns `true` if added, `false` if already exists. Uses `LoadOrCompute` to prevent duplicates.
|
|
|
|
```go
|
|
func Has(cfg *agent.AgentConfig) bool
|
|
```
|
|
|
|
Checks if an agent exists in the pool.
|
|
|
|
```go
|
|
func Remove(cfg *agent.AgentConfig)
|
|
```
|
|
|
|
Removes an agent from the pool.
|
|
|
|
```go
|
|
func RemoveAll()
|
|
```
|
|
|
|
Removes all agents from the pool. Called during configuration reload.
|
|
|
|
```go
|
|
func Get(agentAddrOrDockerHost string) (*Agent, bool)
|
|
```
|
|
|
|
Retrieves an agent by address or Docker host URL. Automatically detects if the input is an agent address or Docker host URL and resolves accordingly.
|
|
|
|
```go
|
|
func GetAgent(name string) (*Agent, bool)
|
|
```
|
|
|
|
Retrieves an agent by name. O(n) iteration over pool contents.
|
|
|
|
```go
|
|
func List() []*Agent
|
|
```
|
|
|
|
Returns all agents as a slice. Creates a new copy for thread safety.
|
|
|
|
```go
|
|
func Iter() iter.Seq2[string, *Agent]
|
|
```
|
|
|
|
Returns an iterator over all agents. Uses `xsync.Map.Range`.
|
|
|
|
```go
|
|
func Num() int
|
|
```
|
|
|
|
Returns the number of agents in the pool.
|
|
|
|
```go
|
|
func (agent *Agent) HTTPClient() *http.Client
|
|
```
|
|
|
|
Returns an HTTP client configured for the agent.
|
|
|
|
## Architecture
|
|
|
|
### Core components
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[Agent Config] --> B[Add to Pool]
|
|
B --> C[xsync.Map Storage]
|
|
C --> D{Get Request}
|
|
D -->|By Address| E[Load from map]
|
|
D -->|By Docker Host| F[Resolve agent addr]
|
|
D -->|By Name| G[Iterate & match]
|
|
|
|
H[Docker Client] --> I[Get Agent]
|
|
I --> C
|
|
I --> J[HTTP Client]
|
|
J --> K[Agent Connection]
|
|
|
|
L[Route Provider] --> M[List Agents]
|
|
M --> C
|
|
```
|
|
|
|
### Thread safety model
|
|
|
|
The pool uses `xsync.Map[string, *Agent]` for concurrent-safe operations:
|
|
|
|
- `Add`: `LoadOrCompute` prevents race conditions and duplicates
|
|
- `Get`: Lock-free read operations
|
|
- `Iter`: Consistent snapshot iteration via `Range`
|
|
- `Remove`: Thread-safe deletion
|
|
|
|
### Test mode
|
|
|
|
When running tests (binary ends with `.test`), a test agent is automatically added:
|
|
|
|
```go
|
|
func init() {
|
|
if strings.HasSuffix(os.Args[0], ".test") {
|
|
agentPool.Store("test-agent", &Agent{
|
|
AgentConfig: &agent.AgentConfig{
|
|
Addr: "test-agent",
|
|
},
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
## Configuration Surface
|
|
|
|
No direct configuration. Agents are added via configuration loading from `config/config.yml`:
|
|
|
|
```yaml
|
|
providers:
|
|
agents:
|
|
- addr: agent.example.com:443
|
|
name: remote-agent
|
|
tls:
|
|
ca_file: /path/to/ca.pem
|
|
cert_file: /path/to/cert.pem
|
|
key_file: /path/to/key.pem
|
|
```
|
|
|
|
## Dependency and Integration Map
|
|
|
|
### Internal dependencies
|
|
|
|
- `agent/pkg/agent` - Agent configuration and connection settings
|
|
- `xsync/v4` - Concurrent map implementation
|
|
|
|
### External dependencies
|
|
|
|
- `valyala/fasthttp` - Fast HTTP client for agent communication
|
|
|
|
### Integration points
|
|
|
|
```go
|
|
// Docker package uses agent pool for remote connections
|
|
if agent.IsDockerHostAgent(host) {
|
|
a, ok := agentpool.Get(host)
|
|
if !ok {
|
|
panic(fmt.Errorf("agent %q not found", host))
|
|
}
|
|
opt := []client.Opt{
|
|
client.WithHost(agent.DockerHost),
|
|
client.WithHTTPClient(a.HTTPClient()),
|
|
}
|
|
}
|
|
```
|
|
|
|
## Observability
|
|
|
|
### Logs
|
|
|
|
No specific logging in the agentpool package. Client creation/destruction is logged in the docker package.
|
|
|
|
### Metrics
|
|
|
|
No metrics are currently exposed.
|
|
|
|
## Security Considerations
|
|
|
|
- TLS configuration is loaded from agent configuration
|
|
- Connection credentials are not stored in the pool after agent creation
|
|
- HTTP clients are created per-request to ensure credential freshness
|
|
|
|
## Failure Modes and Recovery
|
|
|
|
| Failure | Behavior | Recovery |
|
|
| -------------------- | -------------------- | ---------------------------- |
|
|
| Agent not found | Returns `nil, false` | Add agent to pool before use |
|
|
| Duplicate add | Returns `false` | Existing agent is preserved |
|
|
| Test mode activation | Test agent added | Only during test binaries |
|
|
|
|
## Performance Characteristics
|
|
|
|
- O(1) lookup by address
|
|
- O(n) iteration for name-based lookup
|
|
- Pre-sized to 10 entries via `xsync.WithPresize(10)`
|
|
- No locks required for read operations
|
|
- HTTP clients are created per-call to ensure fresh connections
|
|
|
|
## Usage Examples
|
|
|
|
### Adding an agent
|
|
|
|
```go
|
|
agentConfig := &agent.AgentConfig{
|
|
Addr: "agent.example.com:443",
|
|
Name: "my-agent",
|
|
}
|
|
|
|
added := agentpool.Add(agentConfig)
|
|
if !added {
|
|
log.Println("Agent already exists")
|
|
}
|
|
```
|
|
|
|
### Retrieving an agent
|
|
|
|
```go
|
|
// By address
|
|
agent, ok := agentpool.Get("agent.example.com:443")
|
|
if !ok {
|
|
log.Fatal("Agent not found")
|
|
}
|
|
|
|
// By Docker host URL
|
|
agent, ok := agentpool.Get("http://docker-host:2375")
|
|
if !ok {
|
|
log.Fatal("Agent not found")
|
|
}
|
|
|
|
// By name
|
|
agent, ok := agentpool.GetAgent("my-agent")
|
|
if !ok {
|
|
log.Fatal("Agent not found")
|
|
}
|
|
```
|
|
|
|
### Iterating over all agents
|
|
|
|
```go
|
|
for addr, agent := range agentpool.Iter() {
|
|
log.Printf("Agent: %s at %s", agent.Name, addr)
|
|
}
|
|
```
|
|
|
|
### Using with Docker client
|
|
|
|
```go
|
|
// When creating a Docker client with an agent host
|
|
if agent.IsDockerHostAgent(host) {
|
|
a, ok := agentpool.Get(host)
|
|
if !ok {
|
|
panic(fmt.Errorf("agent %q not found", host))
|
|
}
|
|
opt := []client.Opt{
|
|
client.WithHost(agent.DockerHost),
|
|
client.WithHTTPClient(a.HTTPClient()),
|
|
}
|
|
dockerClient, err := client.New(opt...)
|
|
}
|
|
```
|