Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions helm/codeapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ verifier through environment variables on the api component, e.g.:

```yaml
api:
extraEnv:
- name: CODEAPI_AUTH_PROVIDER
value: librechat-jwt
- name: CODEAPI_JWT_PUBLIC_KEY # single PEM/base64-DER verifier key
valueFrom:
secretKeyRef:
name: codeapi-jwt-verifier
key: public-key
- name: CODEAPI_JWT_KID
value: my-key-id
extraEnv:
- name: CODEAPI_AUTH_PROVIDER
value: librechat-jwt
- name: CODEAPI_JWT_PUBLIC_KEY # single PEM/base64-DER verifier key
valueFrom:
secretKeyRef:
name: codeapi-jwt-verifier
key: public-key
- name: CODEAPI_JWT_KID
value: my-key-id
```

`CODEAPI_JWT_PUBLIC_KEYS_DIR` (a mounted directory of PEM files) and
Expand All @@ -78,14 +78,35 @@ For development only, `LOCAL_MODE=true` bypasses authentication β€” see
**TLS to Redis.** Set `REDIS_TLS=true` via `extraEnv` on each component when
your Redis (e.g. a managed cache) requires TLS.

**Redis Cluster mode (GCP Memorystore cluster, AWS ElastiCache cluster).** Set
`redis.cluster.enabled=true` and provide the comma-separated startup nodes in
`redis.cluster.nodes`. The chart will set `USE_REDIS_CLUSTER=true` and populate
`REDIS_HOST` with the node list on every component. For TLS with a CA certificate
Comment on lines +81 to +84

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document disabling the bundled Redis for clusters

Following this new cluster-mode README snippet with the chart defaults leaves redis.enabled as true; codeapi.redis.host then gives precedence to the bundled standalone Redis service while USE_REDIS_CLUSTER=true, so the pods instantiate an ioredis Cluster client against <release>-redis-master and ignore the provided startup nodes. Include redis.enabled=false in the example or make the helper/validation reject this conflicting configuration.

Useful? React with πŸ‘Β / πŸ‘Ž.

(recommended for GCP Memorystore), also set:

```yaml
redis:
tls:
enabled: true
caSecretName: my-memorystore-secret # Secret that holds the CA cert
caKey: ca # Key inside the Secret
caMountPath: /etc/redis-tls/ca.crt # Mount path in each pod
useAlternativeDnsLookup: true # Required for GCP Memorystore cluster
```

The CA certificate is mounted from the Secret into every component pod and
passed to ioredis via `REDIS_CA`. No sidecar is needed.

## Quick Start (Local Development)

### 1. Start Minikube

```bash
minikube start --cpus=4 --memory=8192
```

### 2. Build Images Inside Minikube

```bash
# Point docker to minikube's daemon
eval $(minikube docker-env)
Expand All @@ -100,6 +121,7 @@ docker build -t codeapi-package-init:latest -f docker/Dockerfile.package-init .
```

### 3. Install Dependencies & Deploy

```bash
cd helm/codeapi

Expand Down Expand Up @@ -151,6 +173,7 @@ kubectl rollout restart deployment/codeapi-sandbox-runner
```

### 5. Access the API

```bash
# Port forward (in another terminal)
kubectl port-forward svc/codeapi-api 3112:3112
Expand All @@ -164,6 +187,7 @@ curl http://localhost:3112/v1/health
## Commands Reference

### Startup

```bash
# Start minikube
minikube start
Expand All @@ -176,6 +200,7 @@ kubectl port-forward svc/codeapi-api 3112:3112
```

### Check Status

```bash
# View all pods
kubectl get pods
Expand All @@ -190,6 +215,7 @@ kubectl describe pod <pod-name>
```

### Scaling

```bash
# Scale the sandbox execution tier
kubectl scale deployment/codeapi-sandbox-runner --replicas=10
Expand All @@ -200,6 +226,7 @@ helm upgrade codeapi ./helm/codeapi -f ./helm/codeapi/values-local.yaml \
```

### Update After Code Changes

```bash
# Rebuild images (must be in minikube docker env)
eval $(minikube docker-env)
Expand All @@ -212,6 +239,7 @@ kubectl rollout restart deployment/codeapi-sandbox-runner
```

### Teardown

```bash
# Uninstall the Helm release (removes all K8s resources)
helm uninstall codeapi
Expand All @@ -228,12 +256,14 @@ minikube delete
## Testing

### Health Check

```bash
curl http://localhost:3112/v1/health
# Expected: OK
```

### Execute Python Code

```bash
curl -X POST http://localhost:3112/v1/exec \
-H "Content-Type: application/json" \
Expand All @@ -242,6 +272,7 @@ curl -X POST http://localhost:3112/v1/exec \
```

### Verify Horizontal Scaling

```bash
# Check which service-worker processed the job
kubectl logs deployment/codeapi-service-worker --tail=5
Expand Down Expand Up @@ -299,6 +330,7 @@ kubectl logs deployment/codeapi-service-worker --tail=5
## Troubleshooting

### Pod stuck in `ErrImageNeverPull`

```bash
# Images must be built inside minikube's docker
eval $(minikube docker-env)
Expand All @@ -307,13 +339,15 @@ kubectl rollout restart deployment/<deployment-name>
```

### Pod stuck in `CrashLoopBackOff`

```bash
# Check logs
kubectl logs <pod-name> --previous
kubectl describe pod <pod-name>
```

### "runtime is unknown" error

```bash
# Language packages PVC is empty. Check if the init job ran:
kubectl get jobs -l app.kubernetes.io/component=package-init
Expand All @@ -327,12 +361,14 @@ kubectl rollout restart deployment/codeapi-sandbox-runner
```

### Connection refused on port 3112

```bash
# Make sure port-forward is running
kubectl port-forward svc/codeapi-api 3112:3112
```

### MinIO `ImagePullBackOff` (production values)

```bash
# The Bitnami MinIO chart may reference unavailable image tags.
# For local dev, values-local.yaml uses minio.useSimple=true which
Expand Down
70 changes: 69 additions & 1 deletion helm/codeapi/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,15 @@ app.kubernetes.io/component: tool-call-server
{{- end }}

{{/*
Redis host - either from subchart or external
Redis host - either from subchart or external.
In cluster mode this renders the comma-separated node list from redis.cluster.nodes
(falling back to redis.external.host for single-node external deployments).
*/}}
{{- define "codeapi.redis.host" -}}
{{- if .Values.redis.enabled }}
{{- printf "%s-redis-master" .Release.Name }}
{{- else if and .Values.redis.cluster.enabled .Values.redis.cluster.nodes }}
{{- .Values.redis.cluster.nodes }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Probe one Redis startup node in cluster mode

With the documented Helm setting redis.cluster.nodes="n1:6379,n2:6379,n3:6379", this helper renders the entire comma-separated list into the worker wait-for-redis initContainer command (nc -z {{ include "codeapi.redis.host" . }} {{ include "codeapi.redis.port" . }}). nc -h documents the syntax as [destination] [port], so the init container tries to resolve a literal destination like n1:6379,n2:6379,n3:6379 and never lets worker pods start; render/probe a single startup node or skip this probe for cluster mode.

Useful? React with πŸ‘Β / πŸ‘Ž.

{{- else }}
{{- .Values.redis.external.host }}
{{- end }}
Expand All @@ -173,6 +177,70 @@ Redis port
{{- end }}
{{- end }}

{{/*
USE_REDIS_CLUSTER value – "true" when redis.cluster.enabled or when
redis.cluster.nodes contains a comma (auto-detect multiple nodes).
*/}}
{{- define "codeapi.redis.clusterEnabled" -}}
{{- if .Values.redis.cluster.enabled }}
{{- "true" }}
{{- else if and .Values.redis.cluster.nodes (contains "," .Values.redis.cluster.nodes) }}
{{- "true" }}
{{- else }}
{{- "false" }}
{{- end }}
{{- end }}

{{/*
Emit the Redis TLS + CA environment variables and volume mount for each
component that needs it. Renders nothing when redis.tls.enabled is false.
Usage: {{ include "codeapi.redis.tlsEnv" . }}
*/}}
{{- define "codeapi.redis.tlsEnv" -}}
{{- if .Values.redis.tls.enabled }}
- name: REDIS_TLS
value: "true"
Comment on lines +200 to +202

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate Redis TLS env on external Redis

The new values block says Redis TLS is for external managed Redis and is ignored when the bundled Redis is enabled, but this helper emits REDIS_TLS=true whenever redis.tls.enabled is set. If an operator enables that TLS block while leaving the chart default redis.enabled=true, every component tries to speak TLS to the plain in-cluster Bitnami Redis service and fails to connect; either suppress these env vars when redis.enabled is true or configure TLS on the bundled subchart too.

Useful? React with πŸ‘Β / πŸ‘Ž.

{{- if .Values.redis.tls.caSecretName }}
- name: REDIS_CA
value: {{ .Values.redis.tls.caMountPath | quote }}
{{- end }}
{{- end }}
{{- if .Values.redis.useAlternativeDnsLookup }}
- name: REDIS_USE_ALTERNATIVE_DNS_LOOKUP
value: "true"
{{- end }}
{{- end }}

{{/*
Volume definition for the Redis CA certificate secret.
Renders nothing when redis.tls.caSecretName is empty.
Usage: {{ include "codeapi.redis.caVolume" . }}
*/}}
{{- define "codeapi.redis.caVolume" -}}
{{- if and .Values.redis.tls.enabled .Values.redis.tls.caSecretName }}
- name: redis-ca
secret:
secretName: {{ .Values.redis.tls.caSecretName }}
items:
- key: {{ .Values.redis.tls.caKey }}
path: ca.crt
{{- end }}
{{- end }}

{{/*
VolumeMount for the Redis CA certificate inside a container.
Renders nothing when redis.tls.caSecretName is empty.
Usage: {{ include "codeapi.redis.caVolumeMount" . }}
*/}}
{{- define "codeapi.redis.caVolumeMount" -}}
{{- if and .Values.redis.tls.enabled .Values.redis.tls.caSecretName }}
- name: redis-ca
mountPath: {{ .Values.redis.tls.caMountPath | quote }}
subPath: ca.crt
readOnly: true
{{- end }}
{{- end }}

{{/*
Redis NetworkPolicy egress. Kubernetes NetworkPolicy cannot match DNS names,
so external Redis can be scoped with CIDRs when available; otherwise the chart
Expand Down
12 changes: 11 additions & 1 deletion helm/codeapi/templates/api-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
# Service URLs
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
- name: FILE_SERVER_URL
value: "http://{{ include "codeapi.fullname" . }}-file-server:{{ .Values.fileServer.service.port }}"
- name: TOOL_CALL_SERVER_URL
Expand Down Expand Up @@ -87,6 +89,14 @@ spec:
periodSeconds: 10
resources:
{{- toYaml .Values.api.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
11 changes: 11 additions & 0 deletions helm/codeapi/templates/egress-gateway-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
livenessProbe:
httpGet:
path: /live
Expand All @@ -83,6 +86,14 @@ spec:
periodSeconds: 10
resources:
{{- toYaml .Values.egressGateway.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
11 changes: 11 additions & 0 deletions helm/codeapi/templates/file-server-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
- name: CODEAPI_INTERNAL_SERVICE_TOKEN
valueFrom:
secretKeyRef:
Expand All @@ -135,6 +138,14 @@ spec:
timeoutSeconds: 5
resources:
{{- toYaml .Values.fileServer.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
11 changes: 11 additions & 0 deletions helm/codeapi/templates/tool-call-server-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
- name: CODEAPI_INTERNAL_SERVICE_TOKEN
valueFrom:
secretKeyRef:
Expand All @@ -70,6 +73,14 @@ spec:
periodSeconds: 10
resources:
{{- toYaml .Values.toolCallServer.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
Loading
Loading