Initial commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: agent-runtime-config
|
||||
namespace: swarm-system
|
||||
data:
|
||||
ORCHESTRATOR_URL: "ws://orchestrator-service:8000"
|
||||
WORKSPACE_DIR: "/workspace"
|
||||
GIT_REPO_URL: "git://git-test-server.swarm-system.svc.cluster.local/swarm-test.git"
|
||||
GIT_BASE_BRANCH: "main"
|
||||
AGENT_CAPABILITIES: "code_generation,git,python,filesystem,file-operations,editing,validation,verification,shell,reporting,repository-inspection"
|
||||
ENABLE_SUBTASK_HANDOFF: "false"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: agent-full
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: agent-full
|
||||
component: worker
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: agent-full
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: agent-full
|
||||
component: worker
|
||||
spec:
|
||||
serviceAccountName: orchestrator-sa
|
||||
containers:
|
||||
- name: agent
|
||||
image: python:3.11-slim
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -euxo pipefail
|
||||
apt-get update
|
||||
apt-get install -y git
|
||||
pip install --no-cache-dir -r /app/agent/requirements.txt
|
||||
cd /app
|
||||
python -m agent.main
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: agent-runtime-config
|
||||
env:
|
||||
- name: AGENT_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: OPENAI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: api-key
|
||||
- name: OPENAI_API_BASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: api-base
|
||||
optional: true
|
||||
- name: OPENAI_MODEL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: model
|
||||
optional: true
|
||||
- name: PYTHONUNBUFFERED
|
||||
value: "1"
|
||||
- name: DEBIAN_FRONTEND
|
||||
value: noninteractive
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
volumeMounts:
|
||||
- name: agent-source
|
||||
mountPath: /app/agent
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
volumes:
|
||||
- name: agent-source
|
||||
configMap:
|
||||
name: agent-source
|
||||
- name: workspace
|
||||
emptyDir: {}
|
||||
restartPolicy: Always
|
||||
@@ -0,0 +1,241 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: agent
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: agent
|
||||
component: worker
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: agent
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: agent
|
||||
component: worker
|
||||
spec:
|
||||
serviceAccountName: orchestrator-sa
|
||||
containers:
|
||||
- name: agent
|
||||
image: python:3.11-slim
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
echo "Installing dependencies..."
|
||||
pip install --no-cache-dir \
|
||||
websockets \
|
||||
prometheus-client \
|
||||
gitpython \
|
||||
openai
|
||||
|
||||
echo "Setting up agent code..."
|
||||
mkdir -p /tmp/agent
|
||||
cd /tmp/agent
|
||||
|
||||
echo "Starting agent..."
|
||||
python3 -c "
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from typing import Optional
|
||||
import websockets
|
||||
from websockets.exceptions import ConnectionClosed
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SimpleAgent:
|
||||
def __init__(self, orchestrator_url: str, agent_id: str):
|
||||
self.orchestrator_url = orchestrator_url
|
||||
self.agent_id = agent_id
|
||||
self.api_key = os.getenv('OPENAI_API_KEY')
|
||||
self.api_base = os.getenv('OPENAI_API_BASE', 'https://api.openai.com/v1')
|
||||
self.model = os.getenv('OPENAI_MODEL', 'gpt-4o-mini')
|
||||
self.ws = None
|
||||
self.running = True
|
||||
|
||||
async def connect(self):
|
||||
logger.info(f'Connecting to orchestrator at {self.orchestrator_url}')
|
||||
self.ws = await websockets.connect(self.orchestrator_url)
|
||||
|
||||
# Register with orchestrator
|
||||
register_msg = {
|
||||
'type': 'register',
|
||||
'agent_id': self.agent_id,
|
||||
'capabilities': ['code_generation', 'code_review'],
|
||||
'status': 'idle'
|
||||
}
|
||||
await self.ws.send(json.dumps(register_msg))
|
||||
logger.info(f'Agent {self.agent_id} registered')
|
||||
|
||||
async def send_heartbeat(self):
|
||||
while self.running:
|
||||
try:
|
||||
if self.ws:
|
||||
heartbeat_msg = {
|
||||
'type': 'heartbeat',
|
||||
'agent_id': self.agent_id,
|
||||
'status': 'idle',
|
||||
'timestamp': time.time()
|
||||
}
|
||||
await self.ws.send(json.dumps(heartbeat_msg))
|
||||
logger.debug(f'Heartbeat sent')
|
||||
except Exception as e:
|
||||
logger.error(f'Heartbeat error: {e}')
|
||||
await asyncio.sleep(15)
|
||||
|
||||
async def execute_task(self, task_data):
|
||||
task_id = task_data.get('task_id')
|
||||
description = task_data.get('description')
|
||||
|
||||
logger.info(f'Executing task {task_id}: {description}')
|
||||
|
||||
try:
|
||||
# Use OpenAI-compatible API
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key=self.api_key,
|
||||
base_url=self.api_base
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{'role': 'system', 'content': 'You are a helpful coding assistant. Generate clean, working code.'},
|
||||
{'role': 'user', 'content': description}
|
||||
],
|
||||
max_tokens=2000
|
||||
)
|
||||
|
||||
result = response.choices[0].message.content
|
||||
|
||||
# Send result back
|
||||
result_msg = {
|
||||
'type': 'task_result',
|
||||
'task_id': task_id,
|
||||
'agent_id': self.agent_id,
|
||||
'status': 'completed',
|
||||
'result': result,
|
||||
'timestamp': time.time()
|
||||
}
|
||||
await self.ws.send(json.dumps(result_msg))
|
||||
logger.info(f'Task {task_id} completed')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'Task execution failed: {e}')
|
||||
error_msg = {
|
||||
'type': 'task_result',
|
||||
'task_id': task_id,
|
||||
'agent_id': self.agent_id,
|
||||
'status': 'failed',
|
||||
'error': str(e),
|
||||
'timestamp': time.time()
|
||||
}
|
||||
await self.ws.send(json.dumps(error_msg))
|
||||
|
||||
async def listen(self):
|
||||
while self.running:
|
||||
try:
|
||||
message = await self.ws.recv()
|
||||
data = json.loads(message)
|
||||
msg_type = data.get('type')
|
||||
|
||||
logger.info(f'Received message: {msg_type}')
|
||||
|
||||
if msg_type == 'task_assignment':
|
||||
await self.execute_task(data)
|
||||
elif msg_type == 'ping':
|
||||
pong_msg = {'type': 'pong', 'agent_id': self.agent_id}
|
||||
await self.ws.send(json.dumps(pong_msg))
|
||||
|
||||
except ConnectionClosed:
|
||||
logger.warning('Connection closed, reconnecting...')
|
||||
await asyncio.sleep(5)
|
||||
await self.connect()
|
||||
except Exception as e:
|
||||
logger.error(f'Listen error: {e}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def run(self):
|
||||
await self.connect()
|
||||
|
||||
# Start heartbeat task
|
||||
heartbeat_task = asyncio.create_task(self.send_heartbeat())
|
||||
|
||||
# Start listening
|
||||
await self.listen()
|
||||
|
||||
heartbeat_task.cancel()
|
||||
|
||||
async def main():
|
||||
orchestrator_url = os.getenv('ORCHESTRATOR_URL', 'ws://orchestrator-service:8000/ws')
|
||||
agent_id = os.getenv('AGENT_ID', f'agent-{uuid.uuid4().hex[:8]}')
|
||||
|
||||
logger.info(f'Starting agent {agent_id}')
|
||||
logger.info(f'Orchestrator URL: {orchestrator_url}')
|
||||
|
||||
agent = SimpleAgent(orchestrator_url, agent_id)
|
||||
await agent.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
"
|
||||
env:
|
||||
- name: AGENT_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: ORCHESTRATOR_URL
|
||||
value: "ws://orchestrator-service:8000/ws/$(AGENT_ID)"
|
||||
- name: OPENAI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: api-key
|
||||
- name: OPENAI_API_BASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: api-base
|
||||
optional: true
|
||||
- name: OPENAI_MODEL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: model
|
||||
optional: true
|
||||
- name: PYTHONUNBUFFERED
|
||||
value: "1"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
volumeMounts:
|
||||
- name: agent-source
|
||||
mountPath: /app
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
volumes:
|
||||
- name: agent-source
|
||||
configMap:
|
||||
name: agent-source
|
||||
- name: workspace
|
||||
emptyDir: {}
|
||||
restartPolicy: Always
|
||||
@@ -0,0 +1,225 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: agent
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: agent
|
||||
component: worker
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: agent
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: agent
|
||||
component: worker
|
||||
spec:
|
||||
serviceAccountName: orchestrator-sa
|
||||
containers:
|
||||
- name: agent
|
||||
image: python:3.11-slim
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
echo "Installing dependencies..."
|
||||
pip install --no-cache-dir \
|
||||
websockets \
|
||||
openai \
|
||||
prometheus-client
|
||||
|
||||
echo "Starting agent..."
|
||||
python3 -c "
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from typing import Optional
|
||||
import websockets
|
||||
from websockets.exceptions import ConnectionClosed
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SimpleAgent:
|
||||
def __init__(self, orchestrator_url: str, agent_id: str):
|
||||
self.orchestrator_url = orchestrator_url
|
||||
self.agent_id = agent_id
|
||||
self.api_key = os.getenv('OPENAI_API_KEY')
|
||||
self.api_base = os.getenv('OPENAI_API_BASE', 'https://api.openai.com/v1')
|
||||
self.model = os.getenv('OPENAI_MODEL', 'gpt-4o-mini')
|
||||
self.ws = None
|
||||
self.running = True
|
||||
|
||||
async def connect(self):
|
||||
logger.info(f'Connecting to orchestrator at {self.orchestrator_url}')
|
||||
self.ws = await websockets.connect(self.orchestrator_url)
|
||||
|
||||
# Register with orchestrator
|
||||
register_msg = {
|
||||
'type': 'register',
|
||||
'agent_id': self.agent_id,
|
||||
'capabilities': ['code_generation', 'code_review'],
|
||||
'status': 'idle'
|
||||
}
|
||||
await self.ws.send(json.dumps(register_msg))
|
||||
logger.info(f'Agent {self.agent_id} registered')
|
||||
|
||||
async def send_heartbeat(self):
|
||||
while self.running:
|
||||
try:
|
||||
if self.ws:
|
||||
heartbeat_msg = {
|
||||
'type': 'heartbeat',
|
||||
'agent_id': self.agent_id,
|
||||
'status': 'idle',
|
||||
'timestamp': time.time()
|
||||
}
|
||||
await self.ws.send(json.dumps(heartbeat_msg))
|
||||
logger.debug(f'Heartbeat sent')
|
||||
except Exception as e:
|
||||
logger.error(f'Heartbeat error: {e}')
|
||||
await asyncio.sleep(15)
|
||||
|
||||
async def execute_task(self, task_data):
|
||||
task_id = task_data.get('task_id')
|
||||
description = task_data.get('description')
|
||||
|
||||
logger.info(f'Executing task {task_id}: {description}')
|
||||
|
||||
try:
|
||||
# Use OpenAI-compatible API
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key=self.api_key,
|
||||
base_url=self.api_base
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{'role': 'system', 'content': 'You are a helpful coding assistant. Generate clean, working code.'},
|
||||
{'role': 'user', 'content': description}
|
||||
],
|
||||
max_tokens=2000
|
||||
)
|
||||
|
||||
result = response.choices[0].message.content
|
||||
|
||||
# Send result back
|
||||
result_msg = {
|
||||
'type': 'task_result',
|
||||
'task_id': task_id,
|
||||
'agent_id': self.agent_id,
|
||||
'status': 'completed',
|
||||
'result': result,
|
||||
'timestamp': time.time()
|
||||
}
|
||||
await self.ws.send(json.dumps(result_msg))
|
||||
logger.info(f'Task {task_id} completed')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f'Task execution failed: {e}')
|
||||
error_msg = {
|
||||
'type': 'task_result',
|
||||
'task_id': task_id,
|
||||
'agent_id': self.agent_id,
|
||||
'status': 'failed',
|
||||
'error': str(e),
|
||||
'timestamp': time.time()
|
||||
}
|
||||
await self.ws.send(json.dumps(error_msg))
|
||||
|
||||
async def listen(self):
|
||||
while self.running:
|
||||
try:
|
||||
message = await self.ws.recv()
|
||||
data = json.loads(message)
|
||||
msg_type = data.get('type')
|
||||
|
||||
logger.info(f'Received message: {msg_type}')
|
||||
|
||||
if msg_type == 'task_assignment':
|
||||
await self.execute_task(data)
|
||||
elif msg_type == 'ping':
|
||||
pong_msg = {'type': 'pong', 'agent_id': self.agent_id}
|
||||
await self.ws.send(json.dumps(pong_msg))
|
||||
|
||||
except ConnectionClosed:
|
||||
logger.warning('Connection closed, reconnecting...')
|
||||
await asyncio.sleep(5)
|
||||
await self.connect()
|
||||
except Exception as e:
|
||||
logger.error(f'Listen error: {e}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def run(self):
|
||||
await self.connect()
|
||||
|
||||
# Start heartbeat task
|
||||
heartbeat_task = asyncio.create_task(self.send_heartbeat())
|
||||
|
||||
# Start listening
|
||||
await self.listen()
|
||||
|
||||
heartbeat_task.cancel()
|
||||
|
||||
async def main():
|
||||
orchestrator_url = os.getenv('ORCHESTRATOR_URL', 'ws://orchestrator-service:8000/ws')
|
||||
agent_id = os.getenv('AGENT_ID', f'agent-{uuid.uuid4().hex[:8]}')
|
||||
|
||||
logger.info(f'Starting agent {agent_id}')
|
||||
logger.info(f'Orchestrator URL: {orchestrator_url}')
|
||||
|
||||
agent = SimpleAgent(orchestrator_url, agent_id)
|
||||
await agent.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
"
|
||||
env:
|
||||
- name: AGENT_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: ORCHESTRATOR_URL
|
||||
value: "ws://orchestrator-service:8000/ws/$(AGENT_ID)"
|
||||
- name: OPENAI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: api-key
|
||||
- name: OPENAI_API_BASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: api-base
|
||||
optional: true
|
||||
- name: OPENAI_MODEL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-api-key
|
||||
key: model
|
||||
optional: true
|
||||
- name: PYTHONUNBUFFERED
|
||||
value: "1"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
restartPolicy: Always
|
||||
@@ -0,0 +1,158 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: agent-${AGENT_ID}
|
||||
labels:
|
||||
app: swarm-agent
|
||||
agent-id: ${AGENT_ID}
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
|
||||
# Init container to clone workspace
|
||||
initContainers:
|
||||
- name: git-clone
|
||||
image: alpine/git:latest
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
if [ -n "$GIT_REPO_URL" ]; then
|
||||
echo "Cloning repository from $GIT_REPO_URL"
|
||||
git clone $GIT_REPO_URL /workspace
|
||||
else
|
||||
echo "No GIT_REPO_URL provided, skipping clone"
|
||||
mkdir -p /workspace
|
||||
fi
|
||||
env:
|
||||
- name: GIT_REPO_URL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: swarm-config
|
||||
key: git-repo-url
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
|
||||
containers:
|
||||
- name: agent
|
||||
image: swarm-agent:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
||||
env:
|
||||
# Agent configuration
|
||||
- name: AGENT_ID
|
||||
value: ${AGENT_ID}
|
||||
|
||||
- name: AGENT_CAPABILITIES
|
||||
value: ${AGENT_CAPABILITIES}
|
||||
|
||||
# Orchestrator connection
|
||||
- name: ORCHESTRATOR_URL
|
||||
value: "ws://orchestrator-service:8000"
|
||||
|
||||
# Workspace configuration
|
||||
- name: WORKSPACE_DIR
|
||||
value: "/workspace"
|
||||
|
||||
- name: GIT_REPO_URL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: swarm-config
|
||||
key: git-repo-url
|
||||
optional: true
|
||||
|
||||
# Model API key (OpenAI-compatible) — inject via secret_ref, never commit
|
||||
- name: OPENAI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openai-secret
|
||||
key: api-key
|
||||
|
||||
# Model configuration (OpenAI-compatible)
|
||||
- name: OPENAI_API_BASE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: swarm-config
|
||||
key: openai-api-base
|
||||
optional: true
|
||||
|
||||
- name: OPENAI_MODEL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: swarm-config
|
||||
key: openai-model
|
||||
optional: true
|
||||
|
||||
# Git credentials for push access
|
||||
- name: GIT_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: git-credentials
|
||||
key: username
|
||||
optional: true
|
||||
|
||||
- name: GIT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: git-credentials
|
||||
key: password
|
||||
optional: true
|
||||
|
||||
volumeMounts:
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
|
||||
# Resource limits
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "1000m"
|
||||
|
||||
# Health check
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- python
|
||||
- -c
|
||||
- "import sys; sys.exit(0)"
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
volumes:
|
||||
- name: workspace
|
||||
emptyDir: {}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: swarm-config
|
||||
data:
|
||||
git-repo-url: "" # Set this to your Git repository URL
|
||||
openai-model: "gpt-4o-mini"
|
||||
openai-api-base: "https://api.openai.com/v1" # Override for an OpenAI-compatible endpoint
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: openai-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
api-key: "" # Set via secret_ref / kubectl — do NOT commit a real key
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: git-credentials
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: "" # Set this to your Git username
|
||||
password: "" # Set this to your Git password or token
|
||||
@@ -0,0 +1,90 @@
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: agent-image-puller
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: agent-image-puller
|
||||
component: optimization
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: agent-image-puller
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: agent-image-puller
|
||||
spec:
|
||||
# Run on all nodes including control plane
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
- key: node-role.kubernetes.io/master
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# Use host network to avoid CNI overhead
|
||||
hostNetwork: true
|
||||
|
||||
initContainers:
|
||||
# Pre-pull the agent image
|
||||
- name: pull-agent-image
|
||||
image: ghcr.io/heicode/swarm-agent:latest
|
||||
command: ['sh', '-c', 'echo "Agent image pulled successfully"']
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
|
||||
# Pre-pull common base images
|
||||
- name: pull-python-base
|
||||
image: python:3.11-slim
|
||||
command: ['sh', '-c', 'echo "Python base image pulled"']
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
|
||||
containers:
|
||||
# Keep-alive container (does nothing but keeps DaemonSet running)
|
||||
- name: pause
|
||||
image: gcr.io/google_containers/pause:3.9
|
||||
resources:
|
||||
limits:
|
||||
memory: "32Mi"
|
||||
cpu: "10m"
|
||||
requests:
|
||||
memory: "16Mi"
|
||||
cpu: "5m"
|
||||
|
||||
# Optional: Periodic re-pull to get latest images
|
||||
- name: periodic-puller
|
||||
image: docker:24-cli
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
while true; do
|
||||
echo "Checking for image updates..."
|
||||
# This would require docker socket mount in production
|
||||
# For now, just sleep
|
||||
sleep 3600 # Re-check every hour
|
||||
done
|
||||
resources:
|
||||
limits:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
volumeMounts:
|
||||
- name: docker-socket
|
||||
mountPath: /var/run/docker.sock
|
||||
readOnly: true
|
||||
|
||||
volumes:
|
||||
- name: docker-socket
|
||||
hostPath:
|
||||
path: /var/run/docker.sock
|
||||
type: Socket
|
||||
|
||||
# Ensure DaemonSet runs before agent pods
|
||||
priorityClassName: system-node-critical
|
||||
@@ -0,0 +1,91 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: git-test-server
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: git-test-server
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: git-test-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: git-test-server
|
||||
spec:
|
||||
initContainers:
|
||||
- name: init-repo
|
||||
image: alpine:3.20
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
apk add --no-cache git >/dev/null
|
||||
rm -rf /git/swarm-test.git /tmp/swarm-test-src
|
||||
|
||||
mkdir -p /tmp/swarm-test-src
|
||||
cd /tmp/swarm-test-src
|
||||
git init -b main
|
||||
git config user.name "Swarm Test"
|
||||
git config user.email "swarm-test@example.com"
|
||||
|
||||
cat > hello.py <<'PY'
|
||||
def hello_world():
|
||||
return "hello"
|
||||
PY
|
||||
|
||||
git add hello.py
|
||||
git commit -m "Initial test repository"
|
||||
|
||||
mkdir -p /git
|
||||
git clone --bare /tmp/swarm-test-src /git/swarm-test.git
|
||||
git -C /git/swarm-test.git config daemon.receivepack true
|
||||
touch /git/swarm-test.git/git-daemon-export-ok
|
||||
volumeMounts:
|
||||
- name: git-data
|
||||
mountPath: /git
|
||||
containers:
|
||||
- name: git
|
||||
image: alpine:3.20
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
apk add --no-cache git git-daemon >/dev/null
|
||||
git daemon \
|
||||
--verbose \
|
||||
--reuseaddr \
|
||||
--base-path=/git \
|
||||
--export-all \
|
||||
--enable=receive-pack \
|
||||
/git
|
||||
ports:
|
||||
- containerPort: 9418
|
||||
name: git
|
||||
volumeMounts:
|
||||
- name: git-data
|
||||
mountPath: /git
|
||||
volumes:
|
||||
- name: git-data
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: git-test-server
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: git-test-server
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 9418
|
||||
targetPort: git
|
||||
protocol: TCP
|
||||
name: git
|
||||
selector:
|
||||
app: git-test-server
|
||||
@@ -0,0 +1,488 @@
|
||||
{
|
||||
"dashboard": {
|
||||
"title": "HeiCode Swarm System Health",
|
||||
"tags": ["swarm", "k8s", "agents"],
|
||||
"timezone": "browser",
|
||||
"schemaVersion": 16,
|
||||
"version": 1,
|
||||
"refresh": "10s",
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"panels": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Active Agents",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 0, "y": 0, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "swarm:agents:active:total",
|
||||
"legendFormat": "Active Agents"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {"mode": "thresholds"},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{"value": 0, "color": "green"},
|
||||
{"value": 10, "color": "yellow"},
|
||||
{"value": 20, "color": "red"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Task Completion Rate",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 6, "y": 0, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "swarm:tasks:completion_rate:5m",
|
||||
"legendFormat": "Tasks/sec"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "ops",
|
||||
"decimals": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Average Task Duration",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 12, "y": 0, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "swarm:tasks:duration:avg",
|
||||
"legendFormat": "Avg Duration"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s",
|
||||
"decimals": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Pod Startup Time (P95)",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 18, "y": 0, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "swarm:pod:startup:p95",
|
||||
"legendFormat": "P95 Startup"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s",
|
||||
"decimals": 1,
|
||||
"color": {"mode": "thresholds"},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{"value": 0, "color": "green"},
|
||||
{"value": 30, "color": "yellow"},
|
||||
{"value": 60, "color": "red"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"title": "Agent Count Over Time",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 0, "y": 4, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "swarm:agents:count:by_status",
|
||||
"legendFormat": "{{status}}"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "short", "label": "Agents"},
|
||||
{"format": "short"}
|
||||
],
|
||||
"legend": {"show": true, "alignAsTable": true, "rightSide": false}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"title": "Task Metrics",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 12, "y": 4, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(swarm_tasks_completed_total[5m])",
|
||||
"legendFormat": "Completed"
|
||||
},
|
||||
{
|
||||
"expr": "rate(swarm_tasks_failed_total[5m])",
|
||||
"legendFormat": "Failed"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "ops", "label": "Tasks/sec"},
|
||||
{"format": "short"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"title": "Handoff Latency Distribution",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 0, "y": 12, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.50, rate(swarm_handoff_duration_seconds_bucket[5m]))",
|
||||
"legendFormat": "P50"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.95, rate(swarm_handoff_duration_seconds_bucket[5m]))",
|
||||
"legendFormat": "P95"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, rate(swarm_handoff_duration_seconds_bucket[5m]))",
|
||||
"legendFormat": "P99"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "s", "label": "Latency"},
|
||||
{"format": "short"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"title": "Agent Creation Rate",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 12, "y": 12, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "swarm:agents:creation_rate:5m",
|
||||
"legendFormat": "Creation Rate"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "ops", "label": "Agents/sec"},
|
||||
{"format": "short"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"title": "System Resource Usage",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 0, "y": 20, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(container_cpu_usage_seconds_total{namespace=\"swarm-system\"}[5m]))",
|
||||
"legendFormat": "CPU Usage"
|
||||
},
|
||||
{
|
||||
"expr": "sum(container_memory_working_set_bytes{namespace=\"swarm-system\"}) / 1024 / 1024 / 1024",
|
||||
"legendFormat": "Memory Usage (GB)"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "short", "label": "Resources"},
|
||||
{"format": "short"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"title": "Redis Operations",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 12, "y": 20, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(redis_commands_processed_total[5m])",
|
||||
"legendFormat": "Commands/sec"
|
||||
},
|
||||
{
|
||||
"expr": "redis_connected_clients",
|
||||
"legendFormat": "Connected Clients"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "ops", "label": "Operations"},
|
||||
{"format": "short"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"title": "Pod Status by Phase",
|
||||
"type": "piechart",
|
||||
"gridPos": {"x": 0, "y": 28, "w": 8, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "count by (phase) (kube_pod_status_phase{namespace=\"swarm-system\"})",
|
||||
"legendFormat": "{{phase}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"title": "Error Rate",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 8, "y": 28, "w": 8, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(swarm_errors_total[5m])",
|
||||
"legendFormat": "{{error_type}}"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "ops", "label": "Errors/sec"},
|
||||
{"format": "short"}
|
||||
],
|
||||
"alert": {
|
||||
"conditions": [
|
||||
{
|
||||
"evaluator": {"params": [0.1], "type": "gt"},
|
||||
"operator": {"type": "and"},
|
||||
"query": {"params": ["A", "5m", "now"]},
|
||||
"reducer": {"params": [], "type": "avg"},
|
||||
"type": "query"
|
||||
}
|
||||
],
|
||||
"executionErrorState": "alerting",
|
||||
"frequency": "60s",
|
||||
"handler": 1,
|
||||
"name": "High Error Rate",
|
||||
"noDataState": "no_data",
|
||||
"notifications": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"title": "Network I/O",
|
||||
"type": "graph",
|
||||
"gridPos": {"x": 16, "y": 28, "w": 8, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(container_network_receive_bytes_total{namespace=\"swarm-system\"}[5m])",
|
||||
"legendFormat": "RX {{pod}}"
|
||||
},
|
||||
{
|
||||
"expr": "rate(container_network_transmit_bytes_total{namespace=\"swarm-system\"}[5m])",
|
||||
"legendFormat": "TX {{pod}}"
|
||||
}
|
||||
],
|
||||
"yaxes": [
|
||||
{"format": "Bps", "label": "Bytes/sec"},
|
||||
{"format": "short"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"title": "Top Agents by Duration",
|
||||
"type": "table",
|
||||
"gridPos": {"x": 0, "y": 36, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "topk(10, swarm_agent_duration_seconds)",
|
||||
"format": "table",
|
||||
"instant": true
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {"Time": true},
|
||||
"indexByName": {},
|
||||
"renameByName": {
|
||||
"agent_id": "Agent ID",
|
||||
"task_id": "Task ID",
|
||||
"Value": "Duration (s)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"title": "Recent Failures",
|
||||
"type": "table",
|
||||
"gridPos": {"x": 12, "y": 36, "w": 12, "h": 8},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "topk(10, swarm_agent_failures)",
|
||||
"format": "table",
|
||||
"instant": true
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {"Time": true},
|
||||
"indexByName": {},
|
||||
"renameByName": {
|
||||
"agent_id": "Agent ID",
|
||||
"task_id": "Task ID",
|
||||
"error_type": "Error Type",
|
||||
"Value": "Count"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"title": "Orchestrator Health",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 0, "y": 44, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "up{job=\"orchestrator\"}",
|
||||
"legendFormat": "Status"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "value": "0", "text": "DOWN"},
|
||||
{"type": "value", "value": "1", "text": "UP"}
|
||||
],
|
||||
"color": {"mode": "thresholds"},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{"value": 0, "color": "red"},
|
||||
{"value": 1, "color": "green"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"title": "Redis Health",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 6, "y": 44, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "up{job=\"redis\"}",
|
||||
"legendFormat": "Status"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "value": "0", "text": "DOWN"},
|
||||
{"type": "value", "value": "1", "text": "UP"}
|
||||
],
|
||||
"color": {"mode": "thresholds"},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{"value": 0, "color": "red"},
|
||||
{"value": 1, "color": "green"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"title": "Kubernetes Cluster Health",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 12, "y": 44, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "up{job=\"kubernetes-apiservers\"}",
|
||||
"legendFormat": "API Server"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{"type": "value", "value": "0", "text": "DOWN"},
|
||||
{"type": "value", "value": "1", "text": "UP"}
|
||||
],
|
||||
"color": {"mode": "thresholds"},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{"value": 0, "color": "red"},
|
||||
{"value": 1, "color": "green"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"title": "Total System Cost (Estimated)",
|
||||
"type": "stat",
|
||||
"gridPos": {"x": 18, "y": 44, "w": 6, "h": 4},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(swarm_agent_cost_usd)",
|
||||
"legendFormat": "Total Cost"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "currencyUSD",
|
||||
"decimals": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"name": "namespace",
|
||||
"type": "query",
|
||||
"query": "label_values(swarm_agent_status, namespace)",
|
||||
"current": {"text": "swarm-system", "value": "swarm-system"},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"options": [],
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"sort": 0
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"type": "query",
|
||||
"query": "label_values(swarm_agent_status{namespace=\"$namespace\"}, task_id)",
|
||||
"current": {"text": "All", "value": "$__all"},
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"options": [],
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"sort": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"datasource": "Prometheus",
|
||||
"enable": true,
|
||||
"expr": "ALERTS{alertstate=\"firing\"}",
|
||||
"iconColor": "red",
|
||||
"name": "Alerts",
|
||||
"step": "60s",
|
||||
"tagKeys": "alertname",
|
||||
"textFormat": "{{alertname}}",
|
||||
"titleFormat": "Alert"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: orchestrator-config
|
||||
namespace: swarm-system
|
||||
data:
|
||||
REDIS_HOST: "redis-service"
|
||||
REDIS_PORT: "6379"
|
||||
LOG_LEVEL: "INFO"
|
||||
SWARM_RUNTIME_SOURCE: "heicode-swarm-runtime"
|
||||
SWARM_RUNTIME_PLATFORM: "aks"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: orchestrator
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: orchestrator
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: orchestrator
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: orchestrator
|
||||
spec:
|
||||
serviceAccountName: orchestrator-sa
|
||||
containers:
|
||||
- name: orchestrator
|
||||
image: python:3.11-slim
|
||||
command: ["/bin/bash", "-c"]
|
||||
args:
|
||||
- |
|
||||
apt-get update && apt-get install -y git
|
||||
pip install --no-cache-dir -r /app/orchestrator/requirements.txt
|
||||
cd /app
|
||||
python -m uvicorn orchestrator.main:app --host 0.0.0.0 --port 8000
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: orchestrator-config
|
||||
env:
|
||||
- name: AGNET_RUNTIME_SERVICE_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agnet-runtime-secrets
|
||||
key: runtime-service-token
|
||||
optional: true
|
||||
- name: AGNET_CALLBACK_SERVICE_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agnet-runtime-secrets
|
||||
key: callback-service-token
|
||||
optional: true
|
||||
- name: AGNET_CALLBACK_SIGNING_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: agnet-runtime-secrets
|
||||
key: callback-signing-secret
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: orchestrator-code
|
||||
mountPath: /app/orchestrator
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
volumes:
|
||||
- name: orchestrator-code
|
||||
configMap:
|
||||
name: orchestrator-source
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: orchestrator-service
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: orchestrator
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- port: 8000
|
||||
targetPort: 8000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: orchestrator
|
||||
@@ -0,0 +1,73 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: orchestrator-service
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: orchestrator
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8000
|
||||
targetPort: 8000
|
||||
name: http
|
||||
selector:
|
||||
app: orchestrator
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: orchestrator
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: orchestrator
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: orchestrator
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: orchestrator
|
||||
spec:
|
||||
serviceAccountName: swarm-orchestrator
|
||||
containers:
|
||||
- name: orchestrator
|
||||
image: swarm-orchestrator:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
env:
|
||||
- name: REDIS_HOST
|
||||
value: "redis-service"
|
||||
- name: REDIS_PORT
|
||||
value: "6379"
|
||||
- name: REDIS_DB
|
||||
value: "0"
|
||||
- name: LOG_LEVEL
|
||||
value: "INFO"
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,348 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prometheus-config
|
||||
namespace: swarm-system
|
||||
data:
|
||||
prometheus.yml: |
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
external_labels:
|
||||
cluster: 'swarm-k8s'
|
||||
environment: 'production'
|
||||
|
||||
# Alerting configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
|
||||
# Scrape configurations
|
||||
scrape_configs:
|
||||
# Prometheus itself
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
# Orchestrator metrics
|
||||
- job_name: 'orchestrator'
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
namespaces:
|
||||
names:
|
||||
- swarm-system
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_pod_label_app]
|
||||
action: keep
|
||||
regex: orchestrator
|
||||
- source_labels: [__meta_kubernetes_pod_name]
|
||||
target_label: pod
|
||||
- source_labels: [__meta_kubernetes_namespace]
|
||||
target_label: namespace
|
||||
- source_labels: [__address__]
|
||||
target_label: __address__
|
||||
regex: ([^:]+)(?::\d+)?
|
||||
replacement: $1:8000
|
||||
|
||||
# Agent pods metrics
|
||||
- job_name: 'agents'
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
namespaces:
|
||||
names:
|
||||
- swarm-system
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_pod_label_app]
|
||||
action: keep
|
||||
regex: swarm-agent
|
||||
- source_labels: [__meta_kubernetes_pod_name]
|
||||
target_label: pod
|
||||
- source_labels: [__meta_kubernetes_pod_label_task_id]
|
||||
target_label: task_id
|
||||
- source_labels: [__meta_kubernetes_pod_label_agent_id]
|
||||
target_label: agent_id
|
||||
- source_labels: [__meta_kubernetes_namespace]
|
||||
target_label: namespace
|
||||
- source_labels: [__address__]
|
||||
target_label: __address__
|
||||
regex: ([^:]+)(?::\d+)?
|
||||
replacement: $1:8080
|
||||
|
||||
# Redis metrics (via redis_exporter)
|
||||
- job_name: 'redis'
|
||||
static_configs:
|
||||
- targets: ['redis-exporter:9121']
|
||||
labels:
|
||||
service: 'redis'
|
||||
|
||||
# Kubernetes API server
|
||||
- job_name: 'kubernetes-apiservers'
|
||||
kubernetes_sd_configs:
|
||||
- role: endpoints
|
||||
scheme: https
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
|
||||
action: keep
|
||||
regex: default;kubernetes;https
|
||||
|
||||
# Kubernetes nodes
|
||||
- job_name: 'kubernetes-nodes'
|
||||
kubernetes_sd_configs:
|
||||
- role: node
|
||||
scheme: https
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
relabel_configs:
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_node_label_(.+)
|
||||
|
||||
# Kubernetes pods (general)
|
||||
- job_name: 'kubernetes-pods'
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
|
||||
action: keep
|
||||
regex: true
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
|
||||
action: replace
|
||||
target_label: __metrics_path__
|
||||
regex: (.+)
|
||||
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
|
||||
action: replace
|
||||
regex: ([^:]+)(?::\d+)?;(\d+)
|
||||
replacement: $1:$2
|
||||
target_label: __address__
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_pod_label_(.+)
|
||||
- source_labels: [__meta_kubernetes_namespace]
|
||||
target_label: kubernetes_namespace
|
||||
- source_labels: [__meta_kubernetes_pod_name]
|
||||
target_label: kubernetes_pod_name
|
||||
|
||||
# Recording rules for aggregations
|
||||
rule_files:
|
||||
- /etc/prometheus/rules/*.yml
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prometheus-rules
|
||||
namespace: swarm-system
|
||||
data:
|
||||
swarm_rules.yml: |
|
||||
groups:
|
||||
- name: swarm_metrics
|
||||
interval: 30s
|
||||
rules:
|
||||
# Agent count by status
|
||||
- record: swarm:agents:count:by_status
|
||||
expr: count by (status) (swarm_agent_status)
|
||||
|
||||
# Total active agents
|
||||
- record: swarm:agents:active:total
|
||||
expr: count(swarm_agent_status{status="running"})
|
||||
|
||||
# Average task duration
|
||||
- record: swarm:tasks:duration:avg
|
||||
expr: avg(swarm_task_duration_seconds)
|
||||
|
||||
# Task completion rate (last 5m)
|
||||
- record: swarm:tasks:completion_rate:5m
|
||||
expr: rate(swarm_tasks_completed_total[5m])
|
||||
|
||||
# Agent creation rate (last 5m)
|
||||
- record: swarm:agents:creation_rate:5m
|
||||
expr: rate(swarm_agents_created_total[5m])
|
||||
|
||||
# Handoff latency p95
|
||||
- record: swarm:handoff:latency:p95
|
||||
expr: histogram_quantile(0.95, rate(swarm_handoff_duration_seconds_bucket[5m]))
|
||||
|
||||
# Pod startup time p95
|
||||
- record: swarm:pod:startup:p95
|
||||
expr: histogram_quantile(0.95, rate(swarm_pod_startup_seconds_bucket[5m]))
|
||||
|
||||
- name: swarm_alerts
|
||||
interval: 30s
|
||||
rules:
|
||||
# Alert if too many agents are failing
|
||||
- alert: HighAgentFailureRate
|
||||
expr: rate(swarm_agents_failed_total[5m]) > 0.1
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High agent failure rate detected"
|
||||
description: "Agent failure rate is {{ $value }} failures/sec"
|
||||
|
||||
# Alert if orchestrator is down
|
||||
- alert: OrchestratorDown
|
||||
expr: up{job="orchestrator"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Orchestrator is down"
|
||||
description: "Orchestrator has been down for more than 1 minute"
|
||||
|
||||
# Alert if Redis is down
|
||||
- alert: RedisDown
|
||||
expr: up{job="redis"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Redis is down"
|
||||
description: "Redis has been down for more than 1 minute"
|
||||
|
||||
# Alert if pod startup is slow
|
||||
- alert: SlowPodStartup
|
||||
expr: swarm:pod:startup:p95 > 60
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Pod startup time is slow"
|
||||
description: "P95 pod startup time is {{ $value }}s (target: <30s)"
|
||||
|
||||
# Alert if handoff latency is high
|
||||
- alert: HighHandoffLatency
|
||||
expr: swarm:handoff:latency:p95 > 5
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High handoff latency detected"
|
||||
description: "P95 handoff latency is {{ $value }}s"
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: prometheus
|
||||
namespace: swarm-system
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: prometheus
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: prometheus
|
||||
spec:
|
||||
serviceAccountName: prometheus
|
||||
containers:
|
||||
- name: prometheus
|
||||
image: prom/prometheus:v2.45.0
|
||||
args:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--storage.tsdb.retention.time=30d'
|
||||
- '--web.enable-lifecycle'
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
name: web
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/prometheus
|
||||
- name: rules
|
||||
mountPath: /etc/prometheus/rules
|
||||
- name: storage
|
||||
mountPath: /prometheus
|
||||
resources:
|
||||
requests:
|
||||
memory: "2Gi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "4Gi"
|
||||
cpu: "2000m"
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: prometheus-config
|
||||
- name: rules
|
||||
configMap:
|
||||
name: prometheus-rules
|
||||
- name: storage
|
||||
persistentVolumeClaim:
|
||||
claimName: prometheus-storage
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: prometheus
|
||||
namespace: swarm-system
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 9090
|
||||
targetPort: 9090
|
||||
name: web
|
||||
selector:
|
||||
app: prometheus
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: prometheus-storage
|
||||
namespace: swarm-system
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: prometheus
|
||||
namespace: swarm-system
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: prometheus
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- nodes
|
||||
- nodes/proxy
|
||||
- services
|
||||
- endpoints
|
||||
- pods
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- ingresses
|
||||
verbs: ["get", "list", "watch"]
|
||||
- nonResourceURLs: ["/metrics"]
|
||||
verbs: ["get"]
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: prometheus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: prometheus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: prometheus
|
||||
namespace: swarm-system
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: orchestrator-role
|
||||
namespace: default
|
||||
labels:
|
||||
app: heicode-swarm
|
||||
component: orchestrator
|
||||
rules:
|
||||
# Pod management permissions
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["create", "delete", "list", "watch", "get"]
|
||||
|
||||
# Pod log access
|
||||
- apiGroups: [""]
|
||||
resources: ["pods/log"]
|
||||
verbs: ["get"]
|
||||
|
||||
# Pod status monitoring
|
||||
- apiGroups: [""]
|
||||
resources: ["pods/status"]
|
||||
verbs: ["get"]
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: orchestrator-rolebinding
|
||||
namespace: default
|
||||
labels:
|
||||
app: heicode-swarm
|
||||
component: orchestrator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: orchestrator-sa
|
||||
namespace: default
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: orchestrator-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: orchestrator-sa
|
||||
namespace: default
|
||||
labels:
|
||||
app: heicode-swarm
|
||||
component: orchestrator
|
||||
@@ -0,0 +1,85 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-service
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
name: redis
|
||||
clusterIP: None
|
||||
selector:
|
||||
app: redis
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: swarm-system
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
serviceName: redis-service
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
name: redis
|
||||
command:
|
||||
- redis-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
- --appendfsync
|
||||
- everysec
|
||||
- --maxmemory-policy
|
||||
- allkeys-lru
|
||||
- --maxmemory
|
||||
- 256mb
|
||||
volumeMounts:
|
||||
- name: redis-data
|
||||
mountPath: /data
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 6379
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- redis-cli
|
||||
- ping
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: redis-data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -0,0 +1,235 @@
|
||||
# Test namespace and RBAC for HeiCode-Swarm testing
|
||||
# Provides isolation and resource limits for test runs
|
||||
|
||||
---
|
||||
# Test namespace
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: heicode-swarm-test
|
||||
labels:
|
||||
environment: test
|
||||
purpose: agent-testing
|
||||
|
||||
---
|
||||
# Resource quota to prevent runaway tests
|
||||
apiVersion: v1
|
||||
kind: ResourceQuota
|
||||
metadata:
|
||||
name: test-resource-quota
|
||||
namespace: heicode-swarm-test
|
||||
spec:
|
||||
hard:
|
||||
# Limit total pods to prevent cluster overload
|
||||
pods: "25"
|
||||
# CPU limits (enough for 20 agents + orchestrator + redis)
|
||||
requests.cpu: "22"
|
||||
limits.cpu: "25"
|
||||
# Memory limits
|
||||
requests.memory: "44Gi"
|
||||
limits.memory: "50Gi"
|
||||
# Storage limits
|
||||
persistentvolumeclaims: "5"
|
||||
requests.storage: "10Gi"
|
||||
|
||||
---
|
||||
# Limit range for individual pods
|
||||
apiVersion: v1
|
||||
kind: LimitRange
|
||||
metadata:
|
||||
name: test-limit-range
|
||||
namespace: heicode-swarm-test
|
||||
spec:
|
||||
limits:
|
||||
# Default limits for containers
|
||||
- type: Container
|
||||
default:
|
||||
cpu: "1"
|
||||
memory: "2Gi"
|
||||
defaultRequest:
|
||||
cpu: "500m"
|
||||
memory: "1Gi"
|
||||
max:
|
||||
cpu: "2"
|
||||
memory: "4Gi"
|
||||
min:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
# Pod limits
|
||||
- type: Pod
|
||||
max:
|
||||
cpu: "2"
|
||||
memory: "4Gi"
|
||||
|
||||
---
|
||||
# ServiceAccount for test orchestrator
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test-orchestrator-sa
|
||||
namespace: heicode-swarm-test
|
||||
labels:
|
||||
component: orchestrator
|
||||
environment: test
|
||||
|
||||
---
|
||||
# Role for orchestrator pod management
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: test-orchestrator-role
|
||||
namespace: heicode-swarm-test
|
||||
labels:
|
||||
component: orchestrator
|
||||
environment: test
|
||||
rules:
|
||||
# Pod management permissions
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
|
||||
# Pod logs access
|
||||
- apiGroups: [""]
|
||||
resources: ["pods/log"]
|
||||
verbs: ["get", "list"]
|
||||
# Pod exec for debugging (test only)
|
||||
- apiGroups: [""]
|
||||
resources: ["pods/exec"]
|
||||
verbs: ["create"]
|
||||
# ConfigMaps for agent configuration
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Secrets for API keys
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list"]
|
||||
# Services for orchestrator/redis
|
||||
- apiGroups: [""]
|
||||
resources: ["services"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
---
|
||||
# RoleBinding to grant orchestrator permissions
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: test-orchestrator-rolebinding
|
||||
namespace: heicode-swarm-test
|
||||
labels:
|
||||
component: orchestrator
|
||||
environment: test
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: test-orchestrator-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: test-orchestrator-sa
|
||||
namespace: heicode-swarm-test
|
||||
|
||||
---
|
||||
# ServiceAccount for test agents
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test-agent-sa
|
||||
namespace: heicode-swarm-test
|
||||
labels:
|
||||
component: agent
|
||||
environment: test
|
||||
|
||||
---
|
||||
# Role for agent pods (minimal permissions)
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: test-agent-role
|
||||
namespace: heicode-swarm-test
|
||||
labels:
|
||||
component: agent
|
||||
environment: test
|
||||
rules:
|
||||
# Agents can only read their own pod info
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get"]
|
||||
resourceNames: [] # Will be restricted to self via admission controller
|
||||
# Read ConfigMaps for configuration
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["get", "list"]
|
||||
# Read Secrets for API keys
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get"]
|
||||
|
||||
---
|
||||
# RoleBinding for agent permissions
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: test-agent-rolebinding
|
||||
namespace: heicode-swarm-test
|
||||
labels:
|
||||
component: agent
|
||||
environment: test
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: test-agent-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: test-agent-sa
|
||||
namespace: heicode-swarm-test
|
||||
|
||||
---
|
||||
# NetworkPolicy to isolate test namespace
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: test-isolation-policy
|
||||
namespace: heicode-swarm-test
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
# Allow traffic within namespace
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
environment: test
|
||||
# Allow traffic from control plane (for kubectl exec, logs)
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: kube-system
|
||||
egress:
|
||||
# Allow DNS
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: kube-system
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
# Allow traffic within namespace
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
environment: test
|
||||
# Allow external API calls (model API, OpenAI-compatible, over HTTPS)
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
# Allow Git operations
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 22
|
||||
- protocol: TCP
|
||||
port: 9418
|
||||
Reference in New Issue
Block a user