309 lines
11 KiB
Markdown
309 lines
11 KiB
Markdown
# GitOps Module Architecture
|
|
|
|
**Objective:** Event-driven GitOps orchestration with adaptive execution based on available capabilities.
|
|
|
|
---
|
|
|
|
## Core Design Principles
|
|
|
|
1. **Single Entry Point**: All external access through `GitOpsEngine`
|
|
2. **Minimal Public API**: Only 3 public types exposed to consumers
|
|
3. **Internal Coupling Acceptable**: Internal modules can depend on each other; this doesn't affect external users
|
|
4. **Capability-Driven Execution**: Choose HOW to execute based on WHAT'S available
|
|
|
|
---
|
|
|
|
## Module Tiers
|
|
|
|
### Core Tier (No Dependencies)
|
|
```
|
|
error.rs → Error types and result type
|
|
config.rs → Configuration structures
|
|
```
|
|
|
|
**Purpose**: Foundational types used by all other modules.
|
|
|
|
### Event Tier
|
|
```
|
|
event/ → Event definitions and sources
|
|
├─ Depends on: error
|
|
└─ Used by: engine, flow
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Define event sources (Git, webhooks, alerts, schedules, health checks, n8n, manual)
|
|
- Provide event matching interface
|
|
- Broadcast events through async channels
|
|
|
|
### Provider Tier
|
|
```
|
|
provider/ → Git provider abstractions
|
|
├─ Depends on: error
|
|
└─ Used by: integration, event handlers
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Unified interface for GitHub, GitLab, Gitea, Forgejo
|
|
- Repository operations, webhook registration
|
|
- Event parsing from git providers
|
|
|
|
### Rule Tier
|
|
```
|
|
rule/ → Rule definition and registry
|
|
├─ Depends on: error, serialization
|
|
└─ Used by: engine, flow, executor, generator
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Define rule structure (When/Then clauses)
|
|
- Rule parsing from YAML/TOML/KCL
|
|
- Rule matching against events
|
|
|
|
### Environment Tier
|
|
```
|
|
environment/ → Capability detection
|
|
├─ Depends on: error, observability
|
|
└─ Used by: flow, engine
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Auto-detect available deployment tools
|
|
- Kubernetes (kubeconfig, API access)
|
|
- Docker/Podman (daemon socket)
|
|
- ArgoCD (API endpoint)
|
|
- Flux (CRDs in cluster)
|
|
- systemd (service support)
|
|
- n8n (workflow API)
|
|
- Prometheus (metrics endpoint)
|
|
- Maintain capability cache
|
|
- Provide refresh mechanism
|
|
|
|
### Executor Tier (Internal)
|
|
```
|
|
executor/ → Action execution implementations
|
|
├─ Depends on: error, rule
|
|
└─ Used by: flow, engine (indirectly through flow)
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Implement execution for each tool (ArgoCD, Flux, Kubernetes, Docker, systemd, n8n, script)
|
|
- Translate rule actions to tool-specific commands
|
|
- Handle execution results
|
|
|
|
### Generator Tier (Internal)
|
|
```
|
|
generator/ → Configuration generation
|
|
├─ Depends on: error, rule
|
|
└─ Used by: executor, flow (indirectly)
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Generate tool-specific configurations from rules
|
|
- Support output formats: Kubernetes YAML, ArgoCD CRDs, Flux CRDs, Docker Compose, systemd units, CI/CD workflows
|
|
- Validate generated configurations
|
|
|
|
### Flow Tier (Internal)
|
|
```
|
|
flow/ → Execution flow resolution
|
|
├─ Depends on: error, rule, environment, executor
|
|
└─ Used by: engine
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Map actions to available executors
|
|
- Define execution fallback chains
|
|
- deploy: ArgoCD → Flux → Kubernetes → Docker → systemd
|
|
- rollback: Kubernetes → Docker → systemd
|
|
- notify: n8n → webhook
|
|
- etc.
|
|
- Select primary and fallback executors
|
|
|
|
### Orchestration Tier
|
|
```
|
|
engine/ → Central coordinator
|
|
├─ Depends on: error, rule, environment, flow
|
|
└─ Public entry point for all external access
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Initialize the GitOps system
|
|
- Load and validate rules
|
|
- Detect environment capabilities
|
|
- Coordinate rule execution
|
|
- Main event loop (listens for events, matches rules, executes actions)
|
|
|
|
### Integration Tier
|
|
```
|
|
integration/ → External integrations
|
|
├─ Depends on: config, error, (optional) other modules
|
|
└─ Used by: external code, optional features
|
|
```
|
|
|
|
**Responsibility**:
|
|
- Syntaxis integration for configuration management
|
|
- Custom integrations for specific tools
|
|
- Optional extensions
|
|
|
|
---
|
|
|
|
## Dependency Graph
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ External Users │
|
|
└──────────────────────────┬──────────────────────────────┘
|
|
│
|
|
(public API only)
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ engine │
|
|
└──────┬───────┘
|
|
│
|
|
┌──────────────────┼──────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────┐ ┌─────────────┐ ┌──────────────┐
|
|
│ rule │ │ environment │ │ flow │
|
|
└────┬────┘ └─────────────┘ └──────┬───────┘
|
|
│ │
|
|
│ ┌────────┼────────┐
|
|
│ │ │ │
|
|
▼ ▼ ▼ ▼
|
|
┌─────────┐ ┌─────────┬────────┬────────┐
|
|
│ event │ │executor │ query │provider│
|
|
└─────────┘ └────────┬┘ └────────┘
|
|
│
|
|
▼
|
|
┌──────────┐
|
|
│generator │
|
|
└──────────┘
|
|
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Core (no internal dependencies) │
|
|
│ error.rs, config.rs, deps/mod.rs │
|
|
└─────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Internal Coupling Analysis
|
|
|
|
**Current State**: `gitops::mod` shows 25 internal dependencies
|
|
|
|
**Why This Is Acceptable**:
|
|
1. **External Users See Only 3 Public Types**:
|
|
- `GitOpsEngine` - main entry point
|
|
- `GitOpsError`/`GitOpsResult` - error handling
|
|
- `Config` - configuration
|
|
|
|
2. **Internal Modules Can Depend on Each Other**:
|
|
- `flow` depends on `rule`, `environment`, `executor` - this is necessary for its responsibility
|
|
- `event` depends on `rule` for matching - expected
|
|
- `executor` and `generator` both use `rule` - appropriate
|
|
|
|
3. **Coupling Doesn't Propagate to External API**:
|
|
- External code doesn't need to know about flow, executor, generator, environment
|
|
- All access goes through `GitOpsEngine`
|
|
|
|
4. **Implementation Details Are Marked `pub(crate)`**:
|
|
- executor, generator, flow, event (mostly), provider, rule - all internal
|
|
- This prevents external code from depending on implementation details
|
|
- Makes refactoring easier in the future
|
|
|
|
---
|
|
|
|
## Public API Surface
|
|
|
|
The intentionally minimal public API:
|
|
|
|
```rust
|
|
// Stable external interface
|
|
pub use engine::GitOpsEngine; // Main entry point
|
|
pub use error::{GitOpsError, GitOpsResult}; // Error handling
|
|
pub use config::Config; // Configuration
|
|
|
|
// Everything else is internal
|
|
pub(crate) mod event; // Implementation detail
|
|
pub(crate) mod rule; // Implementation detail
|
|
pub(crate) mod environment; // Implementation detail
|
|
pub(crate) mod executor; // Implementation detail
|
|
pub(crate) mod generator; // Implementation detail
|
|
pub(crate) mod flow; // Implementation detail
|
|
pub mod provider; // Interface but limited use
|
|
pub mod integration; // Optional extensions
|
|
```
|
|
|
|
---
|
|
|
|
## Usage Pattern
|
|
|
|
For external users:
|
|
|
|
```rust
|
|
// Step 1: Load rules
|
|
let rules = RuleRegistry::from_yaml("rules.yaml")?;
|
|
|
|
// Step 2: Create engine (auto-detects environment)
|
|
let engine = GitOpsEngine::new(rules).await?;
|
|
|
|
// Step 3: Run the engine
|
|
engine.run().await?;
|
|
```
|
|
|
|
No need to know about flow, executor, generator, environment, event internals.
|
|
|
|
---
|
|
|
|
## Future Optimizations
|
|
|
|
If internal coupling becomes problematic, consider:
|
|
|
|
1. **Mediator Pattern**: Central event bus for module communication
|
|
- Modules send commands/queries to mediator
|
|
- Reduces direct module-to-module dependencies
|
|
- Trade-off: More complex, requires routing layer
|
|
|
|
2. **Command/Query Separation**: Explicit command handlers
|
|
- `ExecuteCommand` → flow resolves → executor runs
|
|
- `QueryCapabilities` → environment responds
|
|
- More structured, but more boilerplate
|
|
|
|
3. **Layer Enforcement**: Strict dependency constraints
|
|
- Tier N modules can only depend on Tier N-1
|
|
- Prevents some current dependencies (e.g., flow → executor)
|
|
- More rigid architecture
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
1. **Unit Tests**: Test each module in isolation with mocks
|
|
2. **Integration Tests**: Test complete flow from event to execution
|
|
3. **End-to-End Tests**: Full engine lifecycle with real rules
|
|
|
|
Module mocking strategy:
|
|
- External integration points (git providers, executors) are traits
|
|
- Use mockall for creating test implementations
|
|
- Internal modules use pub(crate) so tests can access them
|
|
|
|
---
|
|
|
|
## Coupling Metrics
|
|
|
|
| Metric | Target | Current | Status |
|
|
|--------|--------|---------|--------|
|
|
| External API Size | < 5 types | 3 types | ✅ Optimal |
|
|
| Public Module Count | < 4 | 3 (engine, provider, integration) | ✅ Good |
|
|
| Module Visibility | All internal use pub(crate) | Most do | ✅ Good |
|
|
| Circular Dependencies | 0 | 0 | ✅ Good |
|
|
| External Coupling | Minimal | GitOpsEngine only | ✅ Good |
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- The 25 internal dependencies reported by `cargo coupling` are intentional and don't affect external users
|
|
- Making all modules `pub(crate)` provides clear API surface without forcing a major internal refactoring
|
|
- Future versions can implement mediator pattern if internal complexity grows
|
|
- Current design is pragmatic balance between simplicity and decoupling
|