Skip to content

Commit e96edce

Browse files
feat(a2a): Add service discovery configuration to Gateway CRD (#16)
* feat(a2a): Add service discovery configuration to Gateway CRD - Add A2AServiceDiscovery struct with enabled, namespace, labelSelector, and pollingInterval fields - Update A2AServersSpec to include ServiceDiscovery configuration - Update Gateway controller to pass service discovery config as environment variables - Supports YAML configuration as specified: a2a: serviceDiscovery: enabled: true namespace: 'agents' labelSelector: 'inference-gateway.com/a2a-agent=true' pollingInterval: '30s' 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Eden Reich <edenreich@users.noreply.github.com> * test(gateway): Add tests for A2A service discovery configuration Add unit tests to verify that A2A service discovery environment variables are correctly set in the Gateway deployment when configured. Tests cover: - Full service discovery configuration with custom values - Minimal configuration using default values 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Eden Reich <edenreich@users.noreply.github.com> * fix(test): Regenerate CRDs and fix test environment setup - Regenerate Gateway CRD to include new serviceDiscovery field - Fix getFirstFoundEnvTestBinaryDir() to respect KUBEBUILDER_ASSETS env var - Update test environment setup to properly locate envtest binaries - All 42 unit tests now pass successfully 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Eden Reich <edenreich@users.noreply.github.com> * chore: Remove inline comments Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat(a2a): Add DeepCopy methods for A2AServiceDiscovery Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat(a2a): Add service discovery configuration to A2A and Gateway CRDs Signed-off-by: Eden Reich <eden.reich@gmail.com> * feat(docs): Add A2A service discovery examples and documentation - Add new example file gateway-a2a-service-discovery.yaml showing complete service discovery configuration - Update gateway-complete.yaml to include service discovery configuration section - Update README.md with comprehensive service discovery documentation including: - Feature descriptions in Extensions section - Detailed A2A Service Discovery Configuration section - Agent service requirements and examples - Advanced configuration examples Co-authored-by: Eden Reich <edenreich@users.noreply.github.com> * style: Clean up whitespace and formatting in gateway-a2a-service-discovery.yaml Signed-off-by: Eden Reich <eden.reich@gmail.com> --------- Signed-off-by: Eden Reich <eden.reich@gmail.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Eden Reich <edenreich@users.noreply.github.com>
1 parent e038f10 commit e96edce

11 files changed

Lines changed: 425 additions & 1 deletion

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ Support for multiple AI/ML providers with flexible configuration:
135135

136136
- **MCP (Model Context Protocol)**: Integration with MCP servers for tool access
137137
- **A2A (Agent-to-Agent)**: Distributed agent communication and polling
138+
- **Service Discovery**: Automatic discovery of A2A agents via Kubernetes label selectors
139+
- **Dynamic Agent Registration**: Real-time detection and configuration of new agents
138140
- **Health Checks**: Automated health monitoring for external services
139141

140142
### Networking
@@ -485,6 +487,53 @@ spec:
485487
- "ai-gateway.company.com"
486488
```
487489
490+
#### A2A Service Discovery Configuration
491+
492+
```yaml
493+
apiVersion: core.inference-gateway.com/v1alpha1
494+
kind: Gateway
495+
metadata:
496+
name: gateway-with-service-discovery
497+
namespace: default
498+
spec:
499+
a2a:
500+
enabled: true
501+
# Service Discovery Configuration
502+
serviceDiscovery:
503+
enabled: true
504+
namespace: "agents" # Namespace to search for A2A agents
505+
labelSelector: "inference-gateway.com/a2a-agent=true" # Label selector for agent discovery
506+
pollingInterval: "30s" # How often to check for new agents
507+
# Manual agents can still be configured alongside service discovery
508+
agents:
509+
- name: static-agent
510+
url: "http://static-agent:8080"
511+
```
512+
513+
**Service Discovery Features:**
514+
515+
- **Automatic Agent Discovery**: Automatically discovers A2A agents based on Kubernetes service labels
516+
- **Dynamic Configuration**: Agents are added/removed in real-time as services are created/deleted
517+
- **Label-Based Selection**: Uses configurable label selectors to identify A2A agent services
518+
- **Namespace Scoping**: Can search for agents in specific namespaces
519+
- **Configurable Polling**: Adjustable discovery polling interval
520+
521+
**Agent Service Requirements:**
522+
523+
For a service to be discovered as an A2A agent, it must have the appropriate label:
524+
525+
```yaml
526+
apiVersion: v1
527+
kind: Service
528+
metadata:
529+
name: my-agent
530+
namespace: agents
531+
labels:
532+
inference-gateway.com/a2a-agent: "true" # Required for discovery
533+
spec:
534+
# Service configuration
535+
```
536+
488537
#### Complete Configuration
489538

490539
See `examples/gateway-complete.yaml` for a comprehensive configuration example with all features enabled.
@@ -499,6 +548,9 @@ kubectl apply -f https://raw.githubusercontent.com/inference-gateway/operator/ma
499548

500549
# Deploy minimal gateway for development
501550
kubectl apply -f https://raw.githubusercontent.com/inference-gateway/operator/main/examples/gateway-minimal.yaml
551+
552+
# Deploy gateway with A2A service discovery
553+
kubectl apply -f https://raw.githubusercontent.com/inference-gateway/operator/main/examples/gateway-a2a-service-discovery.yaml
502554
```
503555

504556
### ✅ Configuration Validation

api/v1alpha1/gateway_types.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ type A2AServersSpec struct {
356356
// +optional
357357
Polling *A2APolling `json:"polling,omitempty"`
358358

359+
// Service Discovery configuration
360+
// +optional
361+
ServiceDiscovery *A2AServiceDiscovery `json:"serviceDiscovery,omitempty"`
362+
359363
// A2A agents configuration
360364
// +optional
361365
Agents []A2AAgent `json:"agents,omitempty"`
@@ -394,6 +398,29 @@ type A2APolling struct {
394398
MaxAttempts int32 `json:"maxAttempts,omitempty"`
395399
}
396400

401+
// A2AServiceDiscovery contains service discovery configuration for A2A
402+
type A2AServiceDiscovery struct {
403+
// Enable service discovery
404+
// +optional
405+
// +kubebuilder:default=false
406+
Enabled bool `json:"enabled,omitempty"`
407+
408+
// Namespace to search for A2A agents
409+
// +optional
410+
// +kubebuilder:default="default"
411+
Namespace string `json:"namespace,omitempty"`
412+
413+
// Label selector for A2A agents
414+
// +optional
415+
// +kubebuilder:default="inference-gateway.com/a2a-agent=true"
416+
LabelSelector string `json:"labelSelector,omitempty"`
417+
418+
// Polling interval for service discovery
419+
// +optional
420+
// +kubebuilder:default="30s"
421+
PollingInterval string `json:"pollingInterval,omitempty"`
422+
}
423+
397424
// A2AAgent contains A2A agent configuration
398425
type A2AAgent struct {
399426
// Agent name

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/core.inference-gateway.com_gateways.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
apiVersion: v1
3+
kind: Namespace
4+
metadata:
5+
name: inference-gateway
6+
labels:
7+
inference-gateway.com/managed: "true"
8+
---
9+
apiVersion: core.inference-gateway.com/v1alpha1
10+
kind: Gateway
11+
metadata:
12+
name: gateway-with-a2a-service-discovery
13+
namespace: inference-gateway
14+
spec:
15+
# Core configuration
16+
replicas: 2
17+
environment: development
18+
19+
# Enable telemetry for monitoring
20+
telemetry:
21+
enabled: true
22+
metrics:
23+
enabled: true
24+
port: 9464
25+
26+
# Simple provider configuration
27+
providers:
28+
- name: openai
29+
enabled: true
30+
env:
31+
- name: OPENAI_API_KEY
32+
valueFrom:
33+
secretKeyRef:
34+
name: ai-providers-secret
35+
key: OPENAI_API_KEY
36+
37+
# Agent-to-Agent configuration with Service Discovery
38+
a2a:
39+
enabled: true
40+
expose: false
41+
42+
# Service Discovery Configuration
43+
serviceDiscovery:
44+
enabled: true
45+
namespace: "agents" # Look for agents in 'agents' namespace
46+
labelSelector: "inference-gateway.com/a2a-agent=true" # Use default label selector
47+
pollingInterval: "30s" # Check for new agents every 30 seconds
48+
49+
# Optional: Manual agent configuration (agents found via service discovery will be added automatically)
50+
agents:
51+
- name: calendar-agent
52+
url: "http://calendar-agent.agents.svc.cluster.local:8080"
53+
healthCheck:
54+
enabled: true
55+
path: "/health"
56+
interval: "30s"
57+
58+
# Resource configuration
59+
resources:
60+
requests:
61+
cpu: "100m"
62+
memory: "128Mi"
63+
limits:
64+
cpu: "500m"
65+
memory: "256Mi"
66+
---
67+
# Example secret for AI providers
68+
apiVersion: v1
69+
kind: Secret
70+
metadata:
71+
name: ai-providers-secret
72+
namespace: inference-gateway
73+
type: Opaque
74+
stringData:
75+
OPENAI_API_KEY: "your-openai-api-key-here"
76+
---
77+
# Example A2A agent service that will be discovered automatically
78+
apiVersion: v1
79+
kind: Service
80+
metadata:
81+
name: email-agent
82+
namespace: agents
83+
labels:
84+
inference-gateway.com/a2a-agent: "true" # This label makes it discoverable
85+
app: email-agent
86+
spec:
87+
selector:
88+
app: email-agent
89+
ports:
90+
- port: 8080
91+
targetPort: 8080
92+
type: ClusterIP
93+
---
94+
# Example A2A agent deployment
95+
apiVersion: apps/v1
96+
kind: Deployment
97+
metadata:
98+
name: email-agent
99+
namespace: agents
100+
labels:
101+
app: email-agent
102+
spec:
103+
replicas: 1
104+
selector:
105+
matchLabels:
106+
app: email-agent
107+
template:
108+
metadata:
109+
labels:
110+
app: email-agent
111+
spec:
112+
containers:
113+
- name: email-agent
114+
image: my-registry/email-agent:latest
115+
ports:
116+
- containerPort: 8080
117+
env:
118+
- name: PORT
119+
value: "8080"
120+
resources:
121+
requests:
122+
cpu: "50m"
123+
memory: "64Mi"
124+
limits:
125+
cpu: "200m"
126+
memory: "128Mi"

examples/gateway-complete.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ spec:
201201
interval: "2s"
202202
timeout: "60s"
203203
maxAttempts: 30
204+
# Service Discovery configuration for automatic agent discovery
205+
serviceDiscovery:
206+
enabled: false
207+
namespace: "agents" # Search for agents in 'agents' namespace
208+
labelSelector: "inference-gateway.com/a2a-agent=true" # Default label selector
209+
pollingInterval: "30s" # Poll for new agents every 30 seconds
204210
agents:
205211
- name: google-calendar-agent
206212
url: "http://google-calendar-agent.agents.svc.cluster.local:8080"

internal/controller/gateway_controller.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,42 @@ func (r *GatewayReconciler) buildContainer(gateway *corev1alpha1.Gateway, contai
505505
}(),
506506
},
507507
)
508+
509+
if gateway.Spec.A2A.ServiceDiscovery != nil {
510+
envVars = append(envVars,
511+
corev1.EnvVar{
512+
Name: "A2A_SERVICE_DISCOVERY_ENABLED",
513+
Value: fmt.Sprintf("%t", gateway.Spec.A2A.ServiceDiscovery.Enabled),
514+
},
515+
corev1.EnvVar{
516+
Name: "A2A_SERVICE_DISCOVERY_NAMESPACE",
517+
Value: func() string {
518+
if gateway.Spec.A2A.ServiceDiscovery.Namespace != "" {
519+
return gateway.Spec.A2A.ServiceDiscovery.Namespace
520+
}
521+
return "default"
522+
}(),
523+
},
524+
corev1.EnvVar{
525+
Name: "A2A_SERVICE_DISCOVERY_LABEL_SELECTOR",
526+
Value: func() string {
527+
if gateway.Spec.A2A.ServiceDiscovery.LabelSelector != "" {
528+
return gateway.Spec.A2A.ServiceDiscovery.LabelSelector
529+
}
530+
return "inference-gateway.com/a2a-agent=true"
531+
}(),
532+
},
533+
corev1.EnvVar{
534+
Name: "A2A_SERVICE_DISCOVERY_POLLING_INTERVAL",
535+
Value: func() string {
536+
if gateway.Spec.A2A.ServiceDiscovery.PollingInterval != "" {
537+
return gateway.Spec.A2A.ServiceDiscovery.PollingInterval
538+
}
539+
return "30s"
540+
}(),
541+
},
542+
)
543+
}
508544
}
509545

510546
providerEnvVars := []corev1.EnvVar{}

0 commit comments

Comments
 (0)