add release orchestrator docs and sprints gaps fills
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
# Router Plugins
|
||||
|
||||
> Traffic router plugins for progressive delivery (Nginx, AWS ALB, and custom implementations).
|
||||
|
||||
**Status:** Planned (not yet implemented)
|
||||
**Source:** [Architecture Advisory Section 11.4](../../../product/advisories/09-Jan-2026%20-%20Stella%20Ops%20Orchestrator%20Architecture.md)
|
||||
**Related Modules:** [Progressive Delivery Module](../modules/progressive-delivery.md), [Plugin System](../modules/plugin-system.md)
|
||||
**Sprint:** [110_004 Router Plugins](../../../../implplan/SPRINT_20260110_110_004_PROGDL_nginx_router.md)
|
||||
|
||||
## Overview
|
||||
|
||||
Router plugins enable traffic shifting for progressive delivery. The orchestrator ships with an Nginx router plugin for v1, with HAProxy, Traefik, and AWS ALB available as additional plugins.
|
||||
|
||||
---
|
||||
|
||||
## Router Plugin Interface
|
||||
|
||||
All router plugins implement the `TrafficRouterPlugin` interface:
|
||||
|
||||
```typescript
|
||||
interface TrafficRouterPlugin {
|
||||
// Configuration
|
||||
configureRoute(config: RouteConfig): Promise<void>;
|
||||
|
||||
// Traffic operations
|
||||
shiftTraffic(from: string, to: string, percentage: number): Promise<void>;
|
||||
getTrafficDistribution(): Promise<TrafficDistribution>;
|
||||
|
||||
// Health
|
||||
validateConfig(): Promise<ValidationResult>;
|
||||
reload(): Promise<void>;
|
||||
}
|
||||
|
||||
interface RouteConfig {
|
||||
upstream: string;
|
||||
serverName: string;
|
||||
variations: Variation[];
|
||||
splitType: "weight" | "header" | "cookie";
|
||||
headerName?: string;
|
||||
headerValueB?: string;
|
||||
stickySession?: boolean;
|
||||
stickyDuration?: number;
|
||||
}
|
||||
|
||||
interface Variation {
|
||||
name: string;
|
||||
targets: string[];
|
||||
weight: number;
|
||||
}
|
||||
|
||||
interface TrafficDistribution {
|
||||
variations: {
|
||||
name: string;
|
||||
percentage: number;
|
||||
targets: string[];
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nginx Router Plugin (v1 Built-in)
|
||||
|
||||
The Nginx plugin generates and manages Nginx configuration for traffic splitting.
|
||||
|
||||
### Implementation
|
||||
|
||||
```typescript
|
||||
class NginxRouterPlugin implements TrafficRouterPlugin {
|
||||
async configureRoute(config: RouteConfig): Promise<void> {
|
||||
const upstreamConfig = this.generateUpstreamConfig(config);
|
||||
const serverConfig = this.generateServerConfig(config);
|
||||
|
||||
// Write configuration files
|
||||
await this.writeConfig(
|
||||
`/etc/nginx/conf.d/upstream-${config.upstream}.conf`,
|
||||
upstreamConfig
|
||||
);
|
||||
await this.writeConfig(
|
||||
`/etc/nginx/conf.d/server-${config.upstream}.conf`,
|
||||
serverConfig
|
||||
);
|
||||
|
||||
// Validate configuration
|
||||
const validation = await this.validateConfig();
|
||||
if (!validation.valid) {
|
||||
throw new Error(`Nginx config validation failed: ${validation.error}`);
|
||||
}
|
||||
|
||||
// Reload nginx
|
||||
await this.reload();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Upstream Configuration
|
||||
|
||||
```typescript
|
||||
private generateUpstreamConfig(config: RouteConfig): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
for (const variation of config.variations) {
|
||||
lines.push(`upstream ${config.upstream}_${variation.name} {`);
|
||||
|
||||
for (const target of variation.targets) {
|
||||
lines.push(` server ${target};`);
|
||||
}
|
||||
|
||||
lines.push(`}`);
|
||||
lines.push(``);
|
||||
}
|
||||
|
||||
// Combined upstream with weights (for percentage-based routing)
|
||||
if (config.splitType === "weight") {
|
||||
lines.push(`upstream ${config.upstream} {`);
|
||||
|
||||
for (const variation of config.variations) {
|
||||
const weight = variation.weight;
|
||||
for (const target of variation.targets) {
|
||||
lines.push(` server ${target} weight=${weight};`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(`}`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
```
|
||||
|
||||
### Server Configuration
|
||||
|
||||
```typescript
|
||||
private generateServerConfig(config: RouteConfig): string {
|
||||
if (config.splitType === "header" || config.splitType === "cookie") {
|
||||
// Split block based on header/cookie
|
||||
return `
|
||||
map $http_${config.headerName || "x-variation"} $${config.upstream}_backend {
|
||||
default ${config.upstream}_A;
|
||||
"${config.headerValueB || "B"}" ${config.upstream}_B;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name ${config.serverName};
|
||||
|
||||
location / {
|
||||
proxy_pass http://$${config.upstream}_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
`;
|
||||
} else {
|
||||
// Weight-based (default)
|
||||
return `
|
||||
server {
|
||||
listen 80;
|
||||
server_name ${config.serverName};
|
||||
|
||||
location / {
|
||||
proxy_pass http://${config.upstream};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Traffic Shifting
|
||||
|
||||
```typescript
|
||||
async shiftTraffic(from: string, to: string, percentage: number): Promise<void> {
|
||||
const config = await this.getCurrentConfig();
|
||||
|
||||
// Update weights
|
||||
for (const variation of config.variations) {
|
||||
if (variation.name === to) {
|
||||
variation.weight = percentage;
|
||||
} else {
|
||||
variation.weight = 100 - percentage;
|
||||
}
|
||||
}
|
||||
|
||||
await this.configureRoute(config);
|
||||
}
|
||||
|
||||
async getTrafficDistribution(): Promise<TrafficDistribution> {
|
||||
// Parse current nginx config to get weights
|
||||
const config = await this.parseCurrentConfig();
|
||||
|
||||
return {
|
||||
variations: config.variations.map(v => ({
|
||||
name: v.name,
|
||||
percentage: v.weight,
|
||||
targets: v.targets,
|
||||
})),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AWS ALB Router Plugin
|
||||
|
||||
The AWS ALB plugin manages weighted target groups for traffic splitting.
|
||||
|
||||
### Implementation
|
||||
|
||||
```typescript
|
||||
class AWSALBRouterPlugin implements TrafficRouterPlugin {
|
||||
private alb: AWS.ELBv2;
|
||||
|
||||
async configureRoute(config: RouteConfig): Promise<void> {
|
||||
const listenerArn = config.listenerArn;
|
||||
|
||||
// Create/update target groups for each variation
|
||||
const targetGroupArns: Record<string, string> = {};
|
||||
|
||||
for (const variation of config.variations) {
|
||||
const tgArn = await this.ensureTargetGroup(
|
||||
`${config.upstream}-${variation.name}`,
|
||||
variation.targets
|
||||
);
|
||||
targetGroupArns[variation.name] = tgArn;
|
||||
}
|
||||
|
||||
// Update listener rule with weighted target groups
|
||||
await this.alb.modifyRule({
|
||||
RuleArn: config.ruleArn,
|
||||
Actions: [{
|
||||
Type: "forward",
|
||||
ForwardConfig: {
|
||||
TargetGroups: config.variations.map(v => ({
|
||||
TargetGroupArn: targetGroupArns[v.name],
|
||||
Weight: v.weight,
|
||||
})),
|
||||
TargetGroupStickinessConfig: {
|
||||
Enabled: config.stickySession || false,
|
||||
DurationSeconds: config.stickyDuration || 3600,
|
||||
},
|
||||
},
|
||||
}],
|
||||
}).promise();
|
||||
}
|
||||
|
||||
async shiftTraffic(from: string, to: string, percentage: number): Promise<void> {
|
||||
const rule = await this.getRule();
|
||||
const forwardConfig = rule.Actions[0].ForwardConfig;
|
||||
|
||||
// Update weights
|
||||
for (const tg of forwardConfig.TargetGroups) {
|
||||
if (tg.TargetGroupArn.includes(`-${to}`)) {
|
||||
tg.Weight = percentage;
|
||||
} else {
|
||||
tg.Weight = 100 - percentage;
|
||||
}
|
||||
}
|
||||
|
||||
await this.alb.modifyRule({
|
||||
RuleArn: rule.RuleArn,
|
||||
Actions: rule.Actions,
|
||||
}).promise();
|
||||
}
|
||||
|
||||
async getTrafficDistribution(): Promise<TrafficDistribution> {
|
||||
const rule = await this.getRule();
|
||||
const forwardConfig = rule.Actions[0].ForwardConfig;
|
||||
|
||||
const variations = [];
|
||||
for (const tg of forwardConfig.TargetGroups) {
|
||||
const targets = await this.getTargetGroupTargets(tg.TargetGroupArn);
|
||||
const name = tg.TargetGroupArn.split("-").pop();
|
||||
|
||||
variations.push({
|
||||
name,
|
||||
percentage: tg.Weight,
|
||||
targets: targets.map(t => t.Id),
|
||||
});
|
||||
}
|
||||
|
||||
return { variations };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Router Plugin Catalog
|
||||
|
||||
| Plugin | Status | Description |
|
||||
|--------|--------|-------------|
|
||||
| Nginx | v1 Built-in | Configuration-based weight/header routing |
|
||||
| HAProxy | Plugin | Runtime API for traffic management |
|
||||
| Traefik | Plugin | Dynamic configuration via API |
|
||||
| AWS ALB | Plugin | Weighted target groups |
|
||||
| Envoy | Planned | xDS API integration |
|
||||
|
||||
---
|
||||
|
||||
## Creating Custom Router Plugins
|
||||
|
||||
To create a custom router plugin:
|
||||
|
||||
1. **Implement Interface:** Create a class implementing `TrafficRouterPlugin`
|
||||
2. **Register Plugin:** Add to plugin registry with capabilities
|
||||
3. **Configuration Schema:** Define JSON Schema for plugin config
|
||||
4. **Health Checks:** Implement connection testing
|
||||
5. **Rollback Support:** Handle traffic reversion on failures
|
||||
|
||||
### Example Plugin Manifest
|
||||
|
||||
```yaml
|
||||
plugin:
|
||||
name: my-router
|
||||
version: 1.0.0
|
||||
type: router
|
||||
|
||||
capabilities:
|
||||
- traffic-routing
|
||||
- weight-based
|
||||
- header-based
|
||||
|
||||
config:
|
||||
type: object
|
||||
properties:
|
||||
endpoint:
|
||||
type: string
|
||||
description: Router API endpoint
|
||||
auth:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
enum: [basic, token]
|
||||
credentialRef:
|
||||
type: string
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Progressive Delivery Module](../modules/progressive-delivery.md)
|
||||
- [Plugin System](../modules/plugin-system.md)
|
||||
- [Canary Controller](canary.md)
|
||||
- [A/B Release Models](ab-releases.md)
|
||||
Reference in New Issue
Block a user