Skip to content

Commit 142d3aa

Browse files
authored
feat(helm): add copilot (#1833)
* Add helm for copilot * Remove otel and log level * Change repo name * improvement(helm): enhance copilot chart with HA support and validation * refactor(helm): consolidate copilot secrets and fix postgres volume mount
1 parent 7c6e6d1 commit 142d3aa

File tree

10 files changed

+1014
-1
lines changed

10 files changed

+1014
-1
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Enable the copilot service
2+
copilot:
3+
enabled: true
4+
5+
# Server configuration
6+
server:
7+
image:
8+
repository: simstudioai/copilot
9+
tag: latest
10+
pullPolicy: Always
11+
12+
replicaCount: 2
13+
14+
# Node scheduling (OPTIONAL)
15+
# By default, copilot runs on the same nodes as the main Sim platform
16+
nodeSelector: {}
17+
# nodeSelector:
18+
# workload-type: copilot
19+
20+
resources:
21+
limits:
22+
memory: "2Gi"
23+
cpu: "1000m"
24+
requests:
25+
memory: "1Gi"
26+
cpu: "500m"
27+
28+
# Required secrets (set via values or provide your own secret)
29+
env:
30+
PORT: "8080"
31+
SERVICE_NAME: "copilot"
32+
ENVIRONMENT: "production"
33+
AGENT_API_DB_ENCRYPTION_KEY: "" # openssl rand -hex 32
34+
INTERNAL_API_SECRET: "" # reuse Sim INTERNAL_API_SECRET
35+
LICENSE_KEY: "" # Provided by Sim team
36+
OPENAI_API_KEY_1: "" # At least one provider key required
37+
ANTHROPIC_API_KEY_1: "" # Optional secondary provider
38+
SIM_BASE_URL: "https://sim.example.com" # Base URL for Sim deployment
39+
SIM_AGENT_API_KEY: "" # Must match SIM-side COPILOT_API_KEY
40+
REDIS_URL: "redis://default:password@redis:6379"
41+
# Optional configuration
42+
LOG_LEVEL: "info"
43+
CORS_ALLOWED_ORIGINS: "https://sim.example.com"
44+
OTEL_EXPORTER_OTLP_ENDPOINT: ""
45+
46+
# Create a Secret from the values above. Set create=false to reference an existing secret instead.
47+
secret:
48+
create: true
49+
name: ""
50+
annotations: {}
51+
52+
extraEnv: []
53+
extraEnvFrom: []
54+
55+
service:
56+
type: ClusterIP
57+
port: 8080
58+
targetPort: 8080
59+
60+
# Internal PostgreSQL database (disable to use an external database)
61+
postgresql:
62+
enabled: true
63+
64+
image:
65+
repository: postgres
66+
tag: 16-alpine
67+
pullPolicy: IfNotPresent
68+
69+
auth:
70+
username: copilot
71+
password: "" # REQUIRED - set via --set copilot.postgresql.auth.password
72+
database: copilot
73+
74+
nodeSelector: {}
75+
# nodeSelector:
76+
# workload-type: copilot
77+
78+
resources:
79+
limits:
80+
memory: "1Gi"
81+
cpu: "500m"
82+
requests:
83+
memory: "512Mi"
84+
cpu: "250m"
85+
86+
persistence:
87+
enabled: true
88+
size: 10Gi
89+
90+
# External database configuration (only used when postgresql.enabled=false)
91+
database:
92+
existingSecretName: ""
93+
secretKey: DATABASE_URL
94+
url: ""
95+
96+
# Migration job
97+
migrations:
98+
enabled: true
99+
100+
resources:
101+
limits:
102+
memory: "512Mi"
103+
cpu: "500m"
104+
requests:
105+
memory: "256Mi"
106+
cpu: "100m"
107+
108+
# Optional: Configure ingress to expose copilot service
109+
# Uncomment if you need external access to copilot
110+
# ingress:
111+
# enabled: true
112+
# className: nginx
113+
# annotations:
114+
# cert-manager.io/cluster-issuer: letsencrypt-prod
115+
# nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
116+
# copilot:
117+
# host: copilot.yourdomain.com
118+
# paths:
119+
# - path: /
120+
# pathType: Prefix
121+
# tls:
122+
# enabled: true
123+
# secretName: copilot-tls-secret
124+
125+
# If using private Docker Hub repository
126+
# global:
127+
# imagePullSecrets:
128+
# - name: dockerhub-secret
129+

helm/sim/templates/_helpers.tpl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,69 @@ Affinity
300300
affinity:
301301
{{- toYaml .affinity | nindent 2 }}
302302
{{- end }}
303+
{{- end }}
304+
305+
{{/*
306+
Copilot environment secret name
307+
*/}}
308+
{{- define "sim.copilot.envSecretName" -}}
309+
{{- if and .Values.copilot.server.secret.name (ne .Values.copilot.server.secret.name "") -}}
310+
{{- .Values.copilot.server.secret.name -}}
311+
{{- else -}}
312+
{{- printf "%s-copilot-env" (include "sim.fullname" .) -}}
313+
{{- end -}}
314+
{{- end }}
315+
316+
{{/*
317+
Copilot database secret name
318+
*/}}
319+
{{- define "sim.copilot.databaseSecretName" -}}
320+
{{- if .Values.copilot.postgresql.enabled -}}
321+
{{- printf "%s-copilot-postgresql-secret" (include "sim.fullname" .) -}}
322+
{{- else if and .Values.copilot.database.existingSecretName (ne .Values.copilot.database.existingSecretName "") -}}
323+
{{- .Values.copilot.database.existingSecretName -}}
324+
{{- else -}}
325+
{{- printf "%s-copilot-database-secret" (include "sim.fullname" .) -}}
326+
{{- end -}}
327+
{{- end }}
328+
329+
{{/*
330+
Copilot database secret key
331+
*/}}
332+
{{- define "sim.copilot.databaseSecretKey" -}}
333+
{{- default "DATABASE_URL" .Values.copilot.database.secretKey -}}
334+
{{- end }}
335+
336+
{{/*
337+
Validate Copilot configuration
338+
*/}}
339+
{{- define "sim.copilot.validate" -}}
340+
{{- if .Values.copilot.enabled -}}
341+
{{- if and (not .Values.copilot.server.secret.create) (or (not .Values.copilot.server.secret.name) (eq .Values.copilot.server.secret.name "")) -}}
342+
{{- fail "copilot.server.secret.name must be provided when copilot.server.secret.create=false" -}}
343+
{{- end -}}
344+
{{- if .Values.copilot.server.secret.create -}}
345+
{{- $env := .Values.copilot.server.env -}}
346+
{{- $required := list "AGENT_API_DB_ENCRYPTION_KEY" "INTERNAL_API_SECRET" "LICENSE_KEY" "SIM_BASE_URL" "SIM_AGENT_API_KEY" "REDIS_URL" -}}
347+
{{- range $key := $required -}}
348+
{{- if not (and $env (index $env $key) (ne (index $env $key) "")) -}}
349+
{{- fail (printf "copilot.server.env.%s is required when copilot is enabled" $key) -}}
350+
{{- end -}}
351+
{{- end -}}
352+
{{- $hasOpenAI := and $env (ne (default "" (index $env "OPENAI_API_KEY_1")) "") -}}
353+
{{- $hasAnthropic := and $env (ne (default "" (index $env "ANTHROPIC_API_KEY_1")) "") -}}
354+
{{- if not (or $hasOpenAI $hasAnthropic) -}}
355+
{{- fail "Set at least one of copilot.server.env.OPENAI_API_KEY_1 or copilot.server.env.ANTHROPIC_API_KEY_1" -}}
356+
{{- end -}}
357+
{{- end -}}
358+
{{- if .Values.copilot.postgresql.enabled -}}
359+
{{- if or (not .Values.copilot.postgresql.auth.password) (eq .Values.copilot.postgresql.auth.password "") -}}
360+
{{- fail "copilot.postgresql.auth.password is required when copilot.postgresql.enabled=true" -}}
361+
{{- end -}}
362+
{{- else -}}
363+
{{- if and (or (not .Values.copilot.database.existingSecretName) (eq .Values.copilot.database.existingSecretName "")) (or (not .Values.copilot.database.url) (eq .Values.copilot.database.url "")) -}}
364+
{{- fail "Provide copilot.database.existingSecretName or copilot.database.url when copilot.postgresql.enabled=false" -}}
365+
{{- end -}}
366+
{{- end -}}
367+
{{- end -}}
303368
{{- end }}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
{{- if .Values.copilot.enabled }}
2+
{{- include "sim.copilot.validate" . }}
3+
apiVersion: v1
4+
kind: Service
5+
metadata:
6+
name: {{ include "sim.fullname" . }}-copilot
7+
namespace: {{ .Release.Namespace }}
8+
labels:
9+
{{- include "sim.labels" . | nindent 4 }}
10+
app.kubernetes.io/component: copilot
11+
spec:
12+
type: {{ .Values.copilot.server.service.type }}
13+
ports:
14+
- port: {{ .Values.copilot.server.service.port }}
15+
targetPort: {{ .Values.copilot.server.service.targetPort }}
16+
protocol: TCP
17+
name: http
18+
selector:
19+
app.kubernetes.io/name: {{ include "sim.name" . }}
20+
app.kubernetes.io/instance: {{ .Release.Name }}
21+
app.kubernetes.io/component: copilot
22+
---
23+
apiVersion: apps/v1
24+
kind: Deployment
25+
metadata:
26+
name: {{ include "sim.fullname" . }}-copilot
27+
namespace: {{ .Release.Namespace }}
28+
labels:
29+
{{- include "sim.labels" . | nindent 4 }}
30+
app.kubernetes.io/component: copilot
31+
spec:
32+
replicas: {{ .Values.copilot.server.replicaCount }}
33+
selector:
34+
matchLabels:
35+
app.kubernetes.io/name: {{ include "sim.name" . }}
36+
app.kubernetes.io/instance: {{ .Release.Name }}
37+
app.kubernetes.io/component: copilot
38+
template:
39+
metadata:
40+
annotations:
41+
{{- with .Values.podAnnotations }}
42+
{{- toYaml . | nindent 8 }}
43+
{{- end }}
44+
labels:
45+
app.kubernetes.io/name: {{ include "sim.name" . }}
46+
app.kubernetes.io/instance: {{ .Release.Name }}
47+
app.kubernetes.io/component: copilot
48+
{{- with .Values.podLabels }}
49+
{{- toYaml . | nindent 8 }}
50+
{{- end }}
51+
spec:
52+
{{- with .Values.global.imagePullSecrets }}
53+
imagePullSecrets:
54+
{{- toYaml . | nindent 8 }}
55+
{{- end }}
56+
{{- with .Values.copilot.server.podSecurityContext }}
57+
securityContext:
58+
{{- toYaml . | nindent 8 }}
59+
{{- end }}
60+
{{- with .Values.copilot.server.nodeSelector }}
61+
nodeSelector:
62+
{{- toYaml . | nindent 8 }}
63+
{{- end }}
64+
{{- with .Values.tolerations }}
65+
tolerations:
66+
{{- toYaml . | nindent 8 }}
67+
{{- end }}
68+
{{- with .Values.affinity }}
69+
affinity:
70+
{{- toYaml . | nindent 8 }}
71+
{{- end }}
72+
containers:
73+
- name: copilot
74+
image: {{ include "sim.image" (dict "context" . "image" .Values.copilot.server.image) }}
75+
imagePullPolicy: {{ .Values.copilot.server.image.pullPolicy }}
76+
ports:
77+
- name: http
78+
containerPort: {{ .Values.copilot.server.service.targetPort }}
79+
protocol: TCP
80+
envFrom:
81+
- secretRef:
82+
name: {{ include "sim.copilot.envSecretName" . }}
83+
- secretRef:
84+
name: {{ include "sim.copilot.databaseSecretName" . }}
85+
{{- with .Values.copilot.server.extraEnvFrom }}
86+
{{- toYaml . | nindent 12 }}
87+
{{- end }}
88+
{{- with .Values.copilot.server.extraEnv }}
89+
env:
90+
{{- toYaml . | nindent 12 }}
91+
{{- end }}
92+
{{- if .Values.copilot.server.livenessProbe }}
93+
livenessProbe:
94+
{{- toYaml .Values.copilot.server.livenessProbe | nindent 12 }}
95+
{{- end }}
96+
{{- if .Values.copilot.server.readinessProbe }}
97+
readinessProbe:
98+
{{- toYaml .Values.copilot.server.readinessProbe | nindent 12 }}
99+
{{- end }}
100+
{{- with .Values.copilot.server.resources }}
101+
resources:
102+
{{- toYaml . | nindent 12 }}
103+
{{- end }}
104+
{{- with .Values.copilot.server.securityContext }}
105+
securityContext:
106+
{{- toYaml . | nindent 12 }}
107+
{{- end }}
108+
{{- end }}
109+

helm/sim/templates/ingress.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ spec:
2121
{{- if .Values.realtime.enabled }}
2222
- {{ .Values.ingress.realtime.host }}
2323
{{- end }}
24+
{{- if and .Values.copilot.enabled .Values.ingress.copilot }}
25+
- {{ .Values.ingress.copilot.host }}
26+
{{- end }}
2427
secretName: {{ .Values.ingress.tls.secretName }}
2528
{{- end }}
2629
rules:
@@ -52,4 +55,19 @@ spec:
5255
number: {{ $.Values.realtime.service.port }}
5356
{{- end }}
5457
{{- end }}
58+
{{- if and .Values.copilot.enabled .Values.ingress.copilot }}
59+
# Copilot service ingress rule
60+
- host: {{ .Values.ingress.copilot.host }}
61+
http:
62+
paths:
63+
{{- range .Values.ingress.copilot.paths }}
64+
- path: {{ .path }}
65+
pathType: {{ .pathType }}
66+
backend:
67+
service:
68+
name: {{ include "sim.fullname" $ }}-copilot
69+
port:
70+
number: {{ $.Values.copilot.server.service.port }}
71+
{{- end }}
72+
{{- end }}
5573
{{- end }}

0 commit comments

Comments
 (0)