# internal/route/provider

Discovers and loads routes from Docker containers, YAML files, and remote agents.

## Overview

The `internal/route/provider` package implements route discovery and loading for GoDoxy. It supports multiple provider types (Docker, File, Agent) and manages route lifecycle including validation, start/stop, and event handling.

### Primary Consumers

- **Main entry point**: Creates providers during startup
- **Route layer**: Provides route instances to registry
- **Watcher system**: Receives container/config change events

### Non-goals

- Does not implement route execution (handled by route package)
- Does not manage global configuration
- Does not provide authentication

### Stability

Internal package with stable provider interface.

## Public API

### Exported Types

```go
type Provider struct {
    ProviderImpl
    t        provider.Type
    routes   route.Routes
    routesMu sync.RWMutex
    watcher  W.Watcher
}

type ProviderImpl interface {
    fmt.Stringer
    ShortName() string
    IsExplicitOnly() bool
    loadRoutesImpl() (route.Routes, error)
    NewWatcher() W.Watcher
    Logger() *zerolog.Logger
}
```

### Exported Functions

```go
// Create a file-based provider
func NewFileProvider(filename string) (p *Provider, err error)

// Create a Docker-based provider
func NewDockerProvider(name string, dockerCfg types.DockerProviderConfig) *Provider

// Create an agent-based provider
func NewAgentProvider(cfg *agent.AgentConfig) *Provider
```

### Provider Methods

```go
func (p *Provider) GetType() provider.Type
func (p *Provider) Start(parent task.Parent) error
func (p *Provider) LoadRoutes() error
func (p *Provider) IterRoutes(yield func(string, types.Route) bool)
func (p *Provider) GetRoute(alias string) (types.Route, bool)
func (p *Provider) FindService(project, service string) (types.Route, bool)
```

## Architecture

### Core Components

```mermaid
classDiagram
    class Provider {
        +ProviderImpl
        +t provider.Type
        +routes route.Routes
        +watcher W.Watcher
        +Start(parent) error
        +LoadRoutes() error
        +IterRoutes(yield)
    }

    class ProviderImpl {
        <<interface>>
        +String() string
        +ShortName() string
        +IsExplicitOnly() bool
        +loadRoutesImpl() (route.Routes, error)
        +NewWatcher() W.Watcher
        +Logger() *zerolog.Logger
    }

    class DockerProviderImpl {
        +name string
        +dockerCfg types.DockerProviderConfig
        +ShortName() string
        +loadRoutesImpl() (route.Routes, error)
    }

    class FileProviderImpl {
        +filename string
        +ShortName() string
        +loadRoutesImpl() (route.Routes, error)
    }

    class AgentProviderImpl {
        +*agent.AgentConfig
        +docker DockerProviderImpl
        +ShortName() string
        +loadRoutesImpl() (route.Routes, error)
    }

    Provider --> ProviderImpl : wraps
    ProviderImpl <|-- DockerProviderImpl
    ProviderImpl <|-- FileProviderImpl
    ProviderImpl <|-- AgentProviderImpl
```

### Provider Types

```mermaid
graph TD
    A[Provider] --> B{Docker}
    A --> C{File}
    A --> D{Agent}

    B --> E[DockerWatcher]
    C --> F[ConfigFileWatcher]
    D --> G[DockerWatcher]

    E --> H[Container Labels]
    F --> I[YAML Files]
    G --> J[Remote Agent]
```

### Route Loading Flow

```mermaid
sequenceDiagram
    participant Main as Application
    participant Provider as Provider
    participant Impl as ProviderImpl
    participant Watcher as Watcher
    participant Routes as Route Registry

    Main->>Provider: Start()
    Provider->>Provider: LoadRoutes()
    Provider->>Impl: loadRoutesImpl()
    Impl-->>Provider: Routes
    Provider->>Routes: Validate & Add Routes

    loop Event Loop
        Watcher->>Provider: Container/Config Changed
        Provider->>Provider: Reload Routes
        Provider->>Impl: loadRoutesImpl()
        Provider->>Routes: Update Registry
    end
```

### Docker Provider Features

- Lists all containers and reads their labels
- Supports Docker Compose project/service discovery
- Handles host network mode with port detection
- Supports alias references (`#1`, `#2` for container aliases)
- Explicit-only mode for providers ending with `!`

### File Provider Features

- Reads YAML files from the config directory
- Validates routes on load
- Supports embedded presets

### Agent Provider Features

- Connects to a remote agent via Unix socket or TCP
- Delegates to a Docker provider internally
- Supports the same Docker label-based route discovery

## Configuration Surface

### Docker Provider Labels

```yaml
labels:
  proxy.aliases: app1,app2
  proxy.app1.listen: http://0.0.0.0:8080
  proxy.app1.target: http://app:3000
  proxy.app1.rules: |
    - name: default
      do: pass
```

### File Provider Configuration

```yaml
# config/routes/myapp.yml
routes:
  myapp:
    target: http://localhost:8080
    rules:
      - name: default
        do: pass
```

### Agent Provider Configuration

```yaml
agents:
  remote:
    socket: /run/godoxy-agent.sock
    name: remote-agent
```

## Dependency and Integration Map

| Dependency                       | Purpose                    |
| -------------------------------- | -------------------------- |
| `internal/route`                 | Route types and validation |
| `internal/route/routes`          | Route registry             |
| `internal/docker`                | Docker API integration     |
| `internal/serialization`         | YAML parsing               |
| `internal/watcher`               | Container/config watching  |
| `internal/watcher/events`        | Event queue handling       |
| `agent/pkg/agent`                | Agent configuration        |
| `github.com/yusing/goutils/errs` | Error handling             |

## Observability

### Logs

- **INFO**: Provider start/stop, route count
- **DEBUG**: Route loading details, label parsing
- **ERROR**: Container fetch errors, parse failures

Log context includes: `provider`, `alias`, `route_count`

### Metrics

- `routes_loaded_total` by provider
- `provider_events_total` by type

## Security Considerations

- Docker provider requires socket access
- Agent provider uses Unix socket or TCP with auth
- Route validation prevents SSRF via URL validation
- Container labels are validated before use

## Failure Modes and Recovery

| Failure                   | Behavior                     | Recovery                |
| ------------------------- | ---------------------------- | ----------------------- |
| Docker socket unavailable | Provider fails to start      | Fix socket permissions  |
| Container not found       | Route excluded with error    | Verify container exists |
| YAML parse error          | Route excluded, error logged | Fix configuration file  |
| Agent connection lost     | Routes removed, reconnection | Fix agent connectivity  |
| Watcher error             | Provider finishes with error | Check watcher logs      |

## Usage Examples

### Creating a Docker Provider

```go
provider := provider.NewDockerProvider("default", types.DockerProviderConfig{
    URL: "unix:///var/run/docker.sock",
})

if err := provider.LoadRoutes(); err != nil {
    return err
}

if err := provider.Start(parentTask); err != nil {
    return err
}
```

### Creating a File Provider

```go
provider, err := provider.NewFileProvider("routes/myapp.yml")
if err != nil {
    return err
}

if err := provider.Start(parentTask); err != nil {
    return err
}
```

### Iterating Over Routes

```go
for alias, r := range provider.IterRoutes {
    log.Printf("Route: %s -> %s", alias, r.Name())
}
```

### Finding a Service

```go
route, ok := provider.FindService("myproject", "myservice")
if ok {
    log.Printf("Found service: %s", route.Name())
}
```

## Testing Notes

- Docker provider tests use test containers
- File provider tests use temp directories
- Agent provider tests use mock agents
- Integration tests cover event handling
