diff --git a/.github/workflows/helm-lint-test.yaml b/.github/workflows/helm-lint-test.yaml new file mode 100644 index 00000000..3ce2b98a --- /dev/null +++ b/.github/workflows/helm-lint-test.yaml @@ -0,0 +1,43 @@ +name: Lint and Test Helm Chart + +on: + pull_request: + branches: + - main + push: + branches: + - '**' + +permissions: + contents: read + +jobs: + lint-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v4.3.0 + with: + version: v3.12.0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.7.0 + + - name: Run chart-testing (lint) + run: ct lint --config helm/ct.yaml + + - name: Create kind cluster + uses: helm/kind-action@v1.8.0 + + - name: Run chart-testing (install) + run: ct install --config helm/ct.yaml diff --git a/.github/workflows/helm-release-oci.yaml b/.github/workflows/helm-release-oci.yaml new file mode 100644 index 00000000..5d4782d7 --- /dev/null +++ b/.github/workflows/helm-release-oci.yaml @@ -0,0 +1,58 @@ +name: Release Helm Chart + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + packages: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Set up Helm + uses: azure/setup-helm@v4.3.0 + with: + version: v3.12.0 + + - name: Add dependency chart repos + run: | + helm repo add bitnami https://charts.bitnami.com/bitnami + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.5.0 + with: + charts_dir: helm + config: helm/cr.yaml + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + CR_SKIP_EXISTING: "true" + + - name: Login to GHCR + uses: docker/login-action@v3.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push charts to GHCR + run: | + for pkg in .cr-release-packages/*.tgz; do + if [ -f "${pkg}" ]; then + helm push "${pkg}" "oci://ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/helm-charts" + fi + done diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 00000000..bbb4fd8e --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,27 @@ +apiVersion: v2 +name: open-notebook +description: An Open Source implementation of Notebook LM with more flexibility and features +type: application +version: 1.0.0 +appVersion: "0.0.1" +keywords: + - notebook + - notebooklm + - knowledge-management + - surrealdb +home: https://github.com/lfnovo/open-notebook +sources: + - https://github.com/lfnovo/open-notebook +maintainers: + - name: lfnovo + url: https://github.com/lfnovo + - name: thuanpham582002 + url: https://github.com/thuanpham582002 +annotations: + category: Analytics + licenses: MIT + +dependencies: + - name: common + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.x.x diff --git a/helm/README.md b/helm/README.md new file mode 100644 index 00000000..0ef12b11 --- /dev/null +++ b/helm/README.md @@ -0,0 +1,632 @@ +# Open Notebook Helm Chart + +Open Notebook - AI-powered note-taking application with SurrealDB backend. + +## Introduction + +This chart bootstraps an [Open Notebook](https://github.com/lfnovo/open-notebook) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Open Notebook is a privacy-focused, AI-powered research and note-taking tool that helps you: +- Organize research across multiple notebooks +- Chat with your documents using AI +- Support 17 AI providers (OpenAI, Anthropic, Google, Azure, Ollama, and more) +- Create AI-generated podcasts from your content +- Works with PDFs, web links, videos, audio files, and more + +## Prerequisites + +- Kubernetes 1.23+ +- Helm 3.0+ +- PV provisioner support in the underlying infrastructure (for persistence) + +## Installing the Chart + +To install the chart with the release name `open-notebook`: + +```bash +helm install open-notebook ./helm/open-notebook +``` + +## Uninstalling the Chart + +To uninstall/delete the `open-notebook` deployment: + +```bash +helm uninstall open-notebook +``` + +## Configuration + +See [values.yaml](values.yaml) for comprehensive configuration with Bitnami-style documentation. Key parameters include: + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `image.registry` | Image registry | `docker.io` | +| `image.repository` | Image repository | `lfnovo/open_notebook` | +| `image.tag` | Image tag (overrides appVersion) | `""` | +| `image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `surrealdb.enabled` | Deploy internal SurrealDB | `true` | +| `surrealdb.external.enabled` | Use external SurrealDB | `false` | +| `surrealdb.auth.user` | SurrealDB username | `root` | +| `surrealdb.auth.password` | SurrealDB password | `root` | +| `app.replicaCount` | Number of replicas | `1` | +| `app.ports.http` | HTTP frontend port | `8502` | +| `app.ports.api` | API server port | `5055` | +| `service.type` | Kubernetes service type | `ClusterIP` | +| `ingress.enabled` | Enable ingress | `false` | +| `secrets.create` | Create secrets automatically | `true` | +| `config.aiProviders.*` | AI provider configurations | See below | + +## AI Provider Configuration + +Open Notebook supports 17 AI providers across LLM, embedding, speech-to-text, and text-to-speech: + +### LLM Providers (10) +- **OpenAI** - GPT-4, GPT-4 Turbo, GPT-3.5 +- **Anthropic** - Claude 3 Opus, Sonnet, Haiku +- **Google Gemini** - Gemini Pro, Gemini Vision +- **Vertex AI** - Google Cloud Vertex AI models +- **Mistral** - Mistral Large, Medium, Small +- **DeepSeek** - DeepSeek Chat, DeepSeek Coder +- **Ollama** - Local OpenAI-compatible models +- **OpenRouter** - Multiple open-source models +- **Groq** - Llama 3, Mixtral, Gemma +- **XAI** - Grok models + +### Multi-Modal Providers (2) +- **Azure OpenAI** - Different deployments for LLM/embedding/STT/TTS +- **OpenAI-Compatible** - Azure, FPT Cloud, or other compatible APIs + +### Specialized Providers (5) +- **ElevenLabs** - Text-to-speech for podcast generation +- **Voyage AI** - Specialized embeddings +- **Firecrawl** - Web scraping and content extraction +- **Jina** - Content processing and embeddings +- **LangChain** - Debugging and tracing + +### Quick Configuration + +```yaml +config: + aiProviders: + # OpenAI + openai: + apiKey: "sk-your-openai-key" + + # Anthropic + anthropic: + apiKey: "sk-ant-your-anthropic-key" + + # Google Gemini + google: + apiKey: "your-google-api-key" + geminiBaseUrl: "" # Optional: custom endpoint + + # Azure OpenAI + azureOpenai: + apiKey: "your-azure-key" + endpoint: "https://your-resource.openai.azure.com" + apiVersion: "2024-12-01-preview" + + # OpenAI-compatible (e.g., FPT Cloud) + openaiCompatible: + baseUrl: "https://api.example.com/v1" + apiKey: "your-api-key" +``` + +### Automatic Secret Creation + +Enable automatic secret creation for API keys: + +```yaml +secrets: + create: true + +config: + aiProviders: + openai: + apiKey: "sk-your-key" # Will be added to secret automatically +``` + +## Usage Examples + +### Basic Installation with OpenAI + +```bash +helm install open-notebook ./helm/open-notebook \ + --set config.aiProviders.openai.apiKey=sk-xxx +``` + +### Multiple AI Providers + +```bash +helm install open-notebook ./helm/open-notebook \ + --set config.aiProviders.openai.apiKey=sk-xxx \ + --set config.aiProviders.anthropic.apiKey=sk-ant-xxx \ + --set config.aiProviders.google.apiKey=your-google-key +``` + +### Using OpenAI-Compatible Endpoint (Azure/FPT Cloud) + +```yaml +config: + aiProviders: + openaiCompatible: + baseUrl: "https://mkp-api.fptcloud.com/v1" + apiKey: "sk-your-key" +``` + +### Vertex AI Configuration + +```yaml +config: + aiProviders: + vertexai: + project: "your-gcp-project" + credentials: "base64-encoded-service-account-key" + location: "us-east5" +``` + +### Using External SurrealDB + +```bash +helm install open-notebook ./helm/open-notebook \ + --set surrealdb.enabled=false \ + --set surrealdb.external.enabled=true \ + --set surrealdb.external.host=surrealdb.example.com \ + --set surrealdb.external.port=8000 +``` + +### With NodePort Service + +```yaml +service: + type: NodePort + ports: + http: 8502 + api: 5055 + httpNodePort: 30852 + apiNodePort: 30555 + +config: + api: + url: "http://your-node-ip:30555" +``` + +### With Ingress and TLS + +```bash +helm install open-notebook ./helm/open-notebook \ + --set ingress.enabled=true \ + --set ingress.className=nginx \ + --set ingress.hosts[0].host=notebook.example.com \ + --set ingress.tls[0].secretName=notebook-tls \ + --set ingress.annotations."cert-manager.io/cluster-issuer"=letsencrypt-prod \ + --set config.api.url=https://notebook.example.com +``` + +### Custom Resources + +```yaml +app: + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 500m + memory: 512Mi + +surrealdb: + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi +``` + +### Prometheus Monitoring + +Enable ServiceMonitor for Prometheus Operator: + +```yaml +serviceMonitor: + enabled: true + namespace: monitoring + interval: 30s +``` + +### Horizontal Scaling + +Requires external SurrealDB: + +```yaml +app: + replicaCount: 3 + +surrealdb: + enabled: false + external: + enabled: true + host: external-surrealdb.example.com + port: 8000 +``` + +## Automatic Secret Management + +The chart can automatically create secrets for sensitive data: + +```yaml +secrets: + create: true # Enable automatic secret creation + +config: + aiProviders: + openai: + apiKey: "sk-xxx" # Automatically added to secret + anthropic: + apiKey: "sk-ant-xxx" # Automatically added to secret +``` + +Created secret: `open-notebook-secret` containing: +- `OPENAI_API_KEY` +- `ANTHROPIC_API_KEY` +- `GOOGLE_API_KEY` +- `SURREAL_PASSWORD` +- And all other configured keys + +### Using Existing Secrets + +For production, use existing secrets: + +```bash +# Create secret manually +kubectl create secret generic open-notebook-secrets \ + --from-literal=OPENAI_API_KEY=sk-xxx \ + --from-literal=ANTHROPIC_API_KEY=sk-ant-xxx + +# Reference the secret +helm install open-notebook ./helm/open-notebook \ + --set secrets.create=false \ + --set secrets.existingSecret=open-notebook-secrets +``` + +## Persistence + +The chart supports persistent storage for both the application and SurrealDB: + +- **App Data**: `/app/data` - Stores notebooks, sources, notes, and generated content +- **SurrealDB Data**: `/mydata` - Stores the SurrealDB database files + +Both are enabled by default with 8Gi PVCs. Customize the size and storage class: + +```yaml +app: + persistence: + enabled: true + size: 20Gi + storageClass: fast-ssd + mountPath: /app/data + +surrealdb: + persistence: + enabled: true + size: 20Gi + storageClass: fast-ssd + mountPath: /mydata +``` + +### Using Existing PVCs + +```yaml +app: + persistence: + existingClaim: open-notebook-data + +surrealdb: + persistence: + existingClaim: surrealdb-data +``` + +## Security + +### Password Protection + +Enable password protection: + +```bash +helm install open-notebook ./helm/open-notebook \ + --set config.auth.password=your-secure-password +``` + +### Security Contexts + +Pod and container security contexts are configurable: + +```yaml +app: + podSecurityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + + containerSecurityContext: + enabled: true + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL +``` + +### SSL/TLS Configuration + +```yaml +config: + ssl: + caBundle: /path/to/ca-bundle.crt + verify: true +``` + +## Health Checks + +Configurable probes for application health monitoring: + +```yaml +app: + livenessProbe: + enabled: true + path: /health + port: api + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + readinessProbe: + enabled: true + path: /health + port: api + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + startupProbe: + enabled: false + path: /health + port: api + initialDelaySeconds: 0 + failureThreshold: 30 +``` + +## Networking + +### Ingress Configuration + +Enable ingress to expose Open Notebook via a domain name: + +```yaml +ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" + hosts: + - host: notebook.example.com + paths: + - path: / + pathType: Prefix + service: http + tls: + - secretName: notebook-tls + hosts: + - notebook.example.com +``` + +### Network Policy + +Enable network policies to control pod traffic: + +```yaml +networkPolicy: + enabled: true + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: {} + egress: + - to: + - podSelector: {} +``` + +## Advanced Configuration + +### Worker Configuration + +Configure background worker behavior: + +```yaml +config: + worker: + maxTasks: 5 + retry: + enabled: true + maxAttempts: 3 + waitStrategy: exponential_jitter + waitMin: 1 + waitMax: 30 +``` + +### Pod Disruption Budget + +Ensure availability during disruptions: + +```yaml +app: + podDisruptionBudget: + enabled: true + minAvailable: 1 + # or + maxUnavailable: 1 +``` + +### Service Account + +```yaml +app: + serviceAccount: + create: true + name: open-notebook + automountServiceAccountToken: false + annotations: {} +``` + +### Node Selector and Tolerations + +```yaml +app: + nodeSelector: + workload-type: gpu + + tolerations: + - key: "nvidia.com/gpu" + operator: "Exists" + effect: "NoSchedule" +``` + +## Upgrading + +To upgrade your Helm release: + +```bash +helm upgrade open-notebook ./helm/open-notebook +``` + +To upgrade with custom values: + +```bash +helm upgrade open-notebook ./helm/open-notebook \ + --set config.aiProviders.openai.apiKey=new-key \ + --set image.tag=v1.1.0 +``` + +### Upgrade Strategy + +Control deployment update behavior: + +```yaml +app: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 +``` + +## Rollback + +To rollback to a previous revision: + +```bash +helm rollback open-notebook +``` + +Or rollback to a specific revision: + +```bash +helm rollback open-notebook 2 +``` + +## Troubleshooting + +### Check Pod Status + +```bash +kubectl get pods -l app.kubernetes.io/name=open-notebook +kubectl get pods -l app.kubernetes.io/name=open-notebook-surrealdb +``` + +### View Logs + +```bash +# Application logs +kubectl logs -l app.kubernetes.io/name=open-notebook --tail=100 -f + +# SurrealDB logs +kubectl logs -l app.kubernetes.io/name=open-notebook-surrealdb --tail=100 -f +``` + +### Port Forward to Access + +```bash +# Forward UI +kubectl port-forward svc/open-notebook 8502:8502 + +# Forward API +kubectl port-forward svc/open-notebook 5055:5055 + +# Access at http://localhost:8502 +``` + +### SurrealDB Connection Issues + +If the application can't connect to SurrealDB: + +```bash +# Check SurrealDB is running +kubectl get pods -l app.kubernetes.io/name=open-notebook-surrealdb + +# Port forward to SurrealDB +kubectl port-forward svc/open-notebook-surrealdb 8000:8000 + +# Test connection +surreal sql --endpoint ws://localhost:8000/rpc --namespace open_notebook --database production --user root --pass root +``` + +### Check Secrets + +```bash +# List secrets +kubectl get secrets -n open-notebook + +# View secret (base64 decoded) +kubectl get secret open-notebook-secret -o jsonpath='{.data.OPENAI_API_KEY}' | base64 -d +``` + +## Metrics and Monitoring + +### Prometheus Monitoring + +Enable ServiceMonitor for Prometheus Operator: + +```yaml +serviceMonitor: + enabled: true + namespace: monitoring + interval: 30s + scrapeTimeout: 10s +``` + +Metrics will be available at: +- `http://open-notebook:8502/metrics` (application metrics) +- SurrealDB metrics can be enabled via experimental features + +## License + +MIT License + +## Support + +- GitHub: https://github.com/lfnovo/open-notebook +- Issues: https://github.com/lfnovo/open-notebook/issues +- Discord: https://discord.gg/37XJPXfz2w + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. diff --git a/helm/charts/common-2.31.4.tgz b/helm/charts/common-2.31.4.tgz new file mode 100644 index 00000000..8c7e4d54 Binary files /dev/null and b/helm/charts/common-2.31.4.tgz differ diff --git a/helm/cr.yaml b/helm/cr.yaml new file mode 100644 index 00000000..b9b9946f --- /dev/null +++ b/helm/cr.yaml @@ -0,0 +1,5 @@ +sign: false + +# Enable automatic generation of release notes using GitHub's release notes generator. +# see: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +generate-release-notes: true diff --git a/helm/ct.yaml b/helm/ct.yaml new file mode 100644 index 00000000..72fefc41 --- /dev/null +++ b/helm/ct.yaml @@ -0,0 +1,9 @@ +# See https://github.com/helm/chart-testing#configuration +remote: origin +target-branch: main +chart-dirs: + - helm +chart-repos: + - bitnami=https://charts.bitnami.com/bitnami +helm-extra-args: --timeout 600s +validate-maintainers: false diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt new file mode 100644 index 00000000..246fb110 --- /dev/null +++ b/helm/templates/NOTES.txt @@ -0,0 +1,110 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} + +{{- $svcPort := .Values.service.ports.http -}} +{{- $apiPort := .Values.service.ports.api -}} +{{- $svcName := include "common.names.fullname" . -}} +{{- $svcNamespace := include "common.names.namespace" . -}} +{{- $releaseName := .Release.Name -}} +{{- $chartName := include "common.names.chart" . -}} + +Thank you for installing {{ $chartName }}! + +Your release is named {{ $releaseName }}. + +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ $svcNamespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ $svcName }}) + export NODE_IP=$(kubectl get nodes --namespace {{ $svcNamespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status by running 'kubectl get --namespace {{ $svcNamespace }} svc -w {{ $svcName }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ $svcNamespace }} {{ $svcName }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ $svcPort }} + echo API URL: http://$SERVICE_IP:{{ $apiPort }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ $svcNamespace }} -l "app.kubernetes.io/name={{ include "common.names.name" . }},app.kubernetes.io/instance={{ $releaseName }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ $svcNamespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ $svcNamespace }} port-forward $POD_NAME 8080:{{ $svcPort }} + echo "API available at http://127.0.0.1:5055" + kubectl --namespace {{ $svcNamespace }} port-forward $POD_NAME 5055:{{ $apiPort }} +{{- end }} + +{{- if .Values.ingress.enabled }} + +2. Access the application via Ingress: +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }} +{{- end }} +{{- end }} + +{{- if .Values.surrealdb.enabled }} + +SurrealDB Information: +- Internal Service: {{ $svcName }}-surrealdb.{{ $svcNamespace }}.svc.{{ .Values.global.clusterDomain }}:8000 +- WebSocket URL: ws://{{ $svcName }}-surrealdb.{{ $svcNamespace }}.svc.{{ .Values.global.clusterDomain }}:8000/rpc +- User: {{ .Values.surrealdb.auth.user }} +{{- if .Values.surrealdb.auth.existingSecret }} +- Password: *** (from secret {{ .Values.surrealdb.auth.existingSecret }}) +{{- else if .Values.surrealdb.auth.password }} +- Password: *** (set in values.yaml) +{{- else }} +- WARNING: No password set! Please set surrealdb.auth.password or surrealdb.auth.existingSecret +{{- end }} +- Namespace: {{ .Values.surrealdb.database.namespace }} +- Database: {{ .Values.surrealdb.database.database }} + +To connect to SurrealDB directly: + kubectl port-forward -n {{ $svcNamespace }} svc/{{ $svcName }}-surrealdb 8000:8000 +{{- if or .Values.surrealdb.auth.password .Values.surrealdb.auth.existingSecret }} + surreal sql --endpoint ws://localhost:8000/rpc --namespace {{ .Values.surrealdb.database.namespace }} --database {{ .Values.surrealdb.database.database }} --user {{ .Values.surrealdb.auth.user }} --pass *** + (Password is stored in secret: {{ include "open-notebook.surrealdb.secretName" . }}) +{{- else }} + surreal sql --endpoint ws://localhost:8000/rpc --namespace {{ .Values.surrealdb.database.namespace }} --database {{ .Values.surrealdb.database.database }} --user {{ .Values.surrealdb.auth.user }} + (WARNING: No password set!) +{{- end }} + +{{- end }} + +{{- if .Values.config.auth.password }} + +SECURITY NOTE: +Your Open Notebook instance is protected with a password. Make sure to keep the password secure! + +{{- end }} + +Configuration: +- API Provider Configuration: Configure your AI providers in the values.yaml file or via Helm --set flags +- Data Persistence: {{ if .Values.app.persistence.enabled }}Enabled (PVC: {{ $svcName }}){{ else }}Disabled{{ end }} +- SurrealDB: {{ if .Values.surrealdb.enabled }}Internal (StatefulSet: {{ $svcName }}-surrealdb){{ else if .Values.surrealdb.external.enabled }}External ({{ if .Values.surrealdb.external.secure }}wss://{{ else }}ws://{{ end }}{{ .Values.surrealdb.external.host }}:{{ .Values.surrealdb.external.port }}){{ else }}Disabled{{ end }} + +To upgrade your installation: + helm upgrade {{ $releaseName }} . + +To uninstall your installation: + helm uninstall {{ $releaseName }} + +For more information, visit: + - Documentation: https://github.com/lfnovo/open-notebook + - Issue Tracker: https://github.com/lfnovo/open-notebook/issues diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 00000000..dd30c9f3 --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,446 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} + +{{- /* +Default labels for Open Notebook resources +*/ -}} +{{- define "open-notebook.labels" -}} +{{- include "common.labels.standard" . | nindent 0 }} +{{- end }} + +{{- /* +Selector labels for Open Notebook resources +*/ -}} +{{- define "open-notebook.selectorLabels" -}} +{{- include "common.labels.matchLabels" . | nindent 0 }} +{{ end }} + +{{- /* +SurrealDB labels +*/ -}} +{{- define "open-notebook.surrealdb.labels" -}} +helm.sh/chart: {{ include "common.names.chart" . }} +{{ include "open-notebook.surrealdb.selectorLabels" . }} +{{- if .Chart.AppVersion -}} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{- /* +SurrealDB selector labels +*/ -}} +{{- define "open-notebook.surrealdb.selectorLabels" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }}-surrealdb +app.kubernetes.io/instance: {{ .Release.Name }} +{{ end }} + +{{- /* +Return the secret name for SurrealDB +*/ -}} +{{- define "open-notebook.surrealdb.secretName" -}} +{{- if .Values.surrealdb.auth.existingSecret -}} +{{- .Values.surrealdb.auth.existingSecret -}} +{{- else -}} +{{- printf "%s-surrealdb" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{- /* +Return the secret name for application +*/ -}} +{{- define "open-notebook.secretName" -}} +{{- if .Values.config.auth.existingSecret -}} +{{- .Values.config.auth.existingSecret -}} +{{- else -}} +{{- include "common.names.fullname" . -}} +{{- end -}} +{{- end -}} + +{{- /* +Get the image registry +*/ -}} +{{- define "open-notebook.imageRegistry" -}} +{{- if .Values.global.imageRegistry -}} +{{- .Values.global.imageRegistry -}} +{{- else if .Values.image.registry -}} +{{- .Values.image.registry -}} +{{- end }} +{{- end }} + +{{- /* +Get the application image +*/ -}} +{{- define "open-notebook.image" -}} +{{- $registry := include "open-notebook.imageRegistry" . -}} +{{- $tag := default .Chart.AppVersion .Values.image.tag | default "v1-latest" -}} +{{- if .Values.image.useGHCR -}} +{{- $ghcrTag := default .Chart.AppVersion .Values.image.ghcr.tag | default "v1-latest" -}} +{{- printf "%s/%s:%s" .Values.image.ghcr.registry .Values.image.ghcr.repository $ghcrTag -}} +{{- else -}} +{{- printf "%s/%s:%s" $registry .Values.image.repository $tag -}} +{{- end }} +{{- end }} + +{{- /* +Get the SurrealDB image +*/ -}} +{{- define "open-notebook.surrealdb.image" -}} +{{- $registry := .Values.surrealdb.image.registry -}} +{{- $tag := default "v2" .Values.surrealdb.image.tag -}} +{{- printf "%s/%s:%s" $registry .Values.surrealdb.image.repository $tag -}} +{{- end }} + +{{- /* +Get the SurrealDB URL +*/ -}} +{{- define "open-notebook.surrealdb.url" -}} +{{- if .Values.surrealdb.external.enabled -}} +{{- if .Values.surrealdb.external.secure -}} +wss://{{ .Values.surrealdb.external.host }}:{{ .Values.surrealdb.external.port }}/rpc +{{- else -}} +ws://{{ .Values.surrealdb.external.host }}:{{ .Values.surrealdb.external.port }}/rpc +{{- end }} +{{- else -}} +ws://{{ include "common.names.fullname" . }}-surrealdb.{{ .Release.Namespace }}.svc.{{ .Values.global.clusterDomain }}:8000/rpc +{{- end }} +{{- end }} + +{{- /* +Get the API URL for browser access +*/ -}} +{{- define "open-notebook.api.url" -}} +{{- if .Values.config.api.url -}} +{{- .Values.config.api.url -}} +{{- else if .Values.ingress.enabled -}} +{{- if .Values.ingress.tls }} +https://{{ (index .Values.ingress.hosts 0).host }} +{{- else -}} +http://{{ (index .Values.ingress.hosts 0).host }} +{{- end }} +{{- else if eq .Values.service.type "LoadBalancer" -}} +http://localhost:{{ .Values.app.ports.api }} +{{- else -}} +http://localhost:{{ .Values.app.ports.api }} +{{- end }} +{{- end }} + +{{- /* +Get the internal API URL +*/ -}} +{{- define "open-notebook.api.internalUrl" -}} +{{- if .Values.config.api.internalUrl -}} +{{- .Values.config.api.internalUrl -}} +{{- else -}} +http://localhost:{{ .Values.app.ports.api }} +{{- end }} +{{- end }} + +{{- /* +Render environment variables for the application +*/ -}} +{{- define "open-notebook.envVars" -}} +- name: API_URL + value: {{ include "open-notebook.api.url" . | quote }} +- name: INTERNAL_API_URL + value: {{ include "open-notebook.api.internalUrl" . | quote }} +- name: API_CLIENT_TIMEOUT + value: {{ .Values.config.api.clientTimeout | quote }} +- name: ESPERANTO_LLM_TIMEOUT + value: {{ .Values.config.api.esperantoTimeout | quote }} +- name: SURREAL_URL + value: {{ include "open-notebook.surrealdb.url" . | quote }} +- name: SURREAL_USER + value: {{ .Values.surrealdb.auth.user | quote }} +{{ if or .Values.surrealdb.auth.password .Values.surrealdb.auth.existingSecret -}} +- name: SURREAL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "open-notebook.surrealdb.secretName" . }} + key: {{ .Values.surrealdb.auth.secretKey.password | default "password" }} +{{- end }} +- name: SURREAL_NAMESPACE + value: {{ .Values.surrealdb.database.namespace | quote }} +- name: SURREAL_DATABASE + value: {{ .Values.surrealdb.database.database | quote }} +{{ if .Values.config.ssl.caBundle -}} +- name: ESPERANTO_SSL_CA_BUNDLE + value: {{ .Values.config.ssl.caBundle | quote }} +{{- end }} +- name: ESPERANTO_SSL_VERIFY + value: {{ .Values.config.ssl.verify | quote }} +- name: SURREAL_COMMANDS_MAX_TASKS + value: {{ .Values.config.worker.maxTasks | quote }} +- name: SURREAL_COMMANDS_RETRY_ENABLED + value: {{ .Values.config.worker.retry.enabled | quote }} +- name: SURREAL_COMMANDS_RETRY_MAX_ATTEMPTS + value: {{ .Values.config.worker.retry.maxAttempts | quote }} +- name: SURREAL_COMMANDS_RETRY_WAIT_STRATEGY + value: {{ .Values.config.worker.retry.waitStrategy | quote }} +- name: SURREAL_COMMANDS_RETRY_WAIT_MIN + value: {{ .Values.config.worker.retry.waitMin | quote }} +- name: SURREAL_COMMANDS_RETRY_WAIT_MAX + value: {{ .Values.config.worker.retry.waitMax | quote }} +- name: TTS_BATCH_SIZE + value: {{ .Values.config.tts.batchSize | quote }} +{{ if or .Values.config.aiProviders.openai.apiKey .Values.config.aiProviders.openai.existingSecret -}} +{{ if .Values.config.aiProviders.openai.existingSecret -}} +- name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.openai.existingSecret }} + key: {{ .Values.config.aiProviders.openai.secretKey }} +{{- else -}} +- name: OPENAI_API_KEY + value: {{ .Values.config.aiProviders.openai.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.anthropic.apiKey .Values.config.aiProviders.anthropic.existingSecret -}} +{{ if .Values.config.aiProviders.anthropic.existingSecret -}} +- name: ANTHROPIC_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.anthropic.existingSecret }} + key: {{ .Values.config.aiProviders.anthropic.secretKey }} +{{- else -}} +- name: ANTHROPIC_API_KEY + value: {{ .Values.config.aiProviders.anthropic.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.google.apiKey .Values.config.aiProviders.google.existingSecret -}} +{{ if .Values.config.aiProviders.google.existingSecret -}} +- name: GOOGLE_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.google.existingSecret }} + key: {{ .Values.config.aiProviders.google.secretKey }} +{{- else -}} +- name: GOOGLE_API_KEY + value: {{ .Values.config.aiProviders.google.apiKey | quote }} +{{- end }} +{{- end }} + +{{ if .Values.config.aiProviders.google.geminiBaseUrl -}} +- name: GEMINI_API_BASE_URL + value: {{ .Values.config.aiProviders.google.geminiBaseUrl | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.vertexai.project -}} +- name: VERTEX_PROJECT + value: {{ .Values.config.aiProviders.vertexai.project | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.vertexai.credentials -}} +- name: GOOGLE_APPLICATION_CREDENTIALS + value: {{ .Values.config.aiProviders.vertexai.credentials | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.vertexai.location -}} +- name: VERTEX_LOCATION + value: {{ .Values.config.aiProviders.vertexai.location | quote }} +{{- end }} + +{{- if or .Values.config.aiProviders.mistral.apiKey .Values.config.aiProviders.mistral.existingSecret -}} +{{ if .Values.config.aiProviders.mistral.existingSecret -}} +- name: MISTRAL_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.mistral.existingSecret }} + key: {{ .Values.config.aiProviders.mistral.secretKey }} +{{- else -}} +- name: MISTRAL_API_KEY + value: {{ .Values.config.aiProviders.mistral.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.deepseek.apiKey .Values.config.aiProviders.deepseek.existingSecret -}} +{{ if .Values.config.aiProviders.deepseek.existingSecret -}} +- name: DEEPSEEK_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.deepseek.existingSecret }} + key: {{ .Values.config.aiProviders.deepseek.secretKey }} +{{- else -}} +- name: DEEPSEEK_API_KEY + value: {{ .Values.config.aiProviders.deepseek.apiKey | quote }} +{{- end }} +{{- end }} + +{{ if .Values.config.aiProviders.ollama.baseUrl -}} +- name: OLLAMA_API_BASE + value: {{ .Values.config.aiProviders.ollama.baseUrl | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.openrouter.baseUrl -}} +- name: OPENROUTER_BASE_URL + value: {{ .Values.config.aiProviders.openrouter.baseUrl | quote }} +{{- end }} + +{{- if or .Values.config.aiProviders.openrouter.apiKey .Values.config.aiProviders.openrouter.existingSecret -}} +{{ if .Values.config.aiProviders.openrouter.existingSecret -}} +- name: OPENROUTER_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.openrouter.existingSecret }} + key: {{ .Values.config.aiProviders.openrouter.secretKey }} +{{- else -}} +- name: OPENROUTER_API_KEY + value: {{ .Values.config.aiProviders.openrouter.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.groq.apiKey .Values.config.aiProviders.groq.existingSecret -}} +{{ if .Values.config.aiProviders.groq.existingSecret -}} +- name: GROQ_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.groq.existingSecret }} + key: {{ .Values.config.aiProviders.groq.secretKey }} +{{- else -}} +- name: GROQ_API_KEY + value: {{ .Values.config.aiProviders.groq.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.xai.apiKey .Values.config.aiProviders.xai.existingSecret -}} +{{ if .Values.config.aiProviders.xai.existingSecret -}} +- name: XAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.xai.existingSecret }} + key: {{ .Values.config.aiProviders.xai.secretKey }} +{{- else -}} +- name: XAI_API_KEY + value: {{ .Values.config.aiProviders.xai.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.elevenlabs.apiKey .Values.config.aiProviders.elevenlabs.existingSecret -}} +{{ if .Values.config.aiProviders.elevenlabs.existingSecret -}} +- name: ELEVENLABS_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.elevenlabs.existingSecret }} + key: {{ .Values.config.aiProviders.elevenlabs.secretKey }} +{{- else -}} +- name: ELEVENLABS_API_KEY + value: {{ .Values.config.aiProviders.elevenlabs.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.voyage.apiKey .Values.config.aiProviders.voyage.existingSecret -}} +{{ if .Values.config.aiProviders.voyage.existingSecret -}} +- name: VOYAGE_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.voyage.existingSecret }} + key: {{ .Values.config.aiProviders.voyage.secretKey }} +{{- else -}} +- name: VOYAGE_API_KEY + value: {{ .Values.config.aiProviders.voyage.apiKey | quote }} +{{- end }} +{{- end }} + +{{ if .Values.config.aiProviders.openaiCompatible.baseUrl -}} +- name: OPENAI_COMPATIBLE_BASE_URL + value: {{ .Values.config.aiProviders.openaiCompatible.baseUrl | quote }} +{{- end }} + +{{- if or .Values.config.aiProviders.azureOpenai.apiKey .Values.config.aiProviders.azureOpenai.existingSecret -}} +{{ if .Values.config.aiProviders.azureOpenai.existingSecret -}} +- name: AZURE_OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.azureOpenai.existingSecret }} + key: {{ .Values.config.aiProviders.azureOpenai.secretKey | default "azure-openai-api-key" }} +{{- else -}} +- name: AZURE_OPENAI_API_KEY + value: {{ .Values.config.aiProviders.azureOpenai.apiKey | quote }} +{{- end }} +{{- end }} + +{{ if .Values.config.aiProviders.azureOpenai.endpoint -}} +- name: AZURE_OPENAI_ENDPOINT + value: {{ .Values.config.aiProviders.azureOpenai.endpoint | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.azureOpenai.apiVersion -}} +- name: AZURE_OPENAI_API_VERSION + value: {{ .Values.config.aiProviders.azureOpenai.apiVersion | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.langchain.tracingV2 -}} +- name: LANGCHAIN_TRACING_V2 + value: {{ .Values.config.aiProviders.langchain.tracingV2 | quote }} +{{- end }} + +{{ if .Values.config.aiProviders.langchain.endpoint -}} +- name: LANGCHAIN_ENDPOINT + value: {{ .Values.config.aiProviders.langchain.endpoint | quote }} +{{- end }} + +{{- if or .Values.config.aiProviders.langchain.apiKey .Values.config.aiProviders.langchain.existingSecret -}} +{{ if .Values.config.aiProviders.langchain.existingSecret -}} +- name: LANGCHAIN_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.langchain.existingSecret }} + key: {{ .Values.config.aiProviders.langchain.secretKey }} +{{- else -}} +- name: LANGCHAIN_API_KEY + value: {{ .Values.config.aiProviders.langchain.apiKey | quote }} +{{- end }} +{{- end }} + +{{ if .Values.config.aiProviders.langchain.project -}} +- name: LANGCHAIN_PROJECT + value: {{ .Values.config.aiProviders.langchain.project | quote }} +{{- end }} + +{{- if or .Values.config.aiProviders.firecrawl.apiKey .Values.config.aiProviders.firecrawl.existingSecret -}} +{{ if .Values.config.aiProviders.firecrawl.existingSecret -}} +- name: FIRECRAWL_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.firecrawl.existingSecret }} + key: {{ .Values.config.aiProviders.firecrawl.secretKey }} +{{- else -}} +- name: FIRECRAWL_API_KEY + value: {{ .Values.config.aiProviders.firecrawl.apiKey | quote }} +{{- end }} +{{- end }} + +{{- if or .Values.config.aiProviders.jina.apiKey .Values.config.aiProviders.jina.existingSecret -}} +{{ if .Values.config.aiProviders.jina.existingSecret -}} +- name: JINA_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.config.aiProviders.jina.existingSecret }} + key: {{ .Values.config.aiProviders.jina.secretKey }} +{{- else -}} +- name: JINA_API_KEY + value: {{ .Values.config.aiProviders.jina.apiKey | quote }} +{{- end }} +{{- end }} +{{- end }} diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml new file mode 100644 index 00000000..caa0bdb8 --- /dev/null +++ b/helm/templates/configmap.yaml @@ -0,0 +1,32 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} +{{- if and .Values.app.extraEnvVarsCM .Values.app.extraEnvVars }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.app.extraEnvVarsCM }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} +data: + app-extra-env-vars: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.app.extraEnvVars "context" $ ) | nindent 4 }} +{{- end }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 00000000..80dd02c0 --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,195 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} +apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }} +kind: Deployment +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.global.annotations "context" $ ) | nindent 4 }} +spec: + replicas: {{ .Values.app.replicaCount }} + selector: + matchLabels: {{- include "open-notebook.selectorLabels" . | nindent 6 }} + {{- if .Values.app.updateStrategy.type }} + strategy: + type: {{ .Values.app.updateStrategy.type }} + {{- if eq .Values.app.updateStrategy.type "RollingUpdate" }} + rollingUpdate: + maxSurge: {{ .Values.app.updateStrategy.rollingUpdate.maxSurge }} + maxUnavailable: {{ .Values.app.updateStrategy.rollingUpdate.maxUnavailable }} + {{- end }} + {{- end }} + template: + metadata: + labels: {{- include "open-notebook.selectorLabels" . | nindent 8 }} + {{- if .Values.app.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.app.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- if .Values.app.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.app.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "common.images.pullSecrets" . | nindent 6 }} + {{- if .Values.app.serviceAccount.create }} + serviceAccountName: {{ include "open-notebook.serviceAccountName" . | default (include "common.names.fullname" .) }} + {{- end }} + {{- if .Values.app.podSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.app.podSecurityContext "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.app.priorityClassName }} + priorityClassName: {{ .Values.app.priorityClassName | quote }} + {{- end }} + initContainers: + {{- if and .Values.app.persistence.enabled .Values.app.persistence.existingClaim }} + - name: init-permissions + image: {{ include "open-notebook.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + runAsUser: 0 + command: + - /bin/sh + - -cx + - | + mkdir -p {{ .Values.app.persistence.mountPath }} + chown -R {{ .Values.app.podSecurityContext.fsGroup }}:{{ .Values.app.podSecurityContext.fsGroup }} {{ .Values.app.persistence.mountPath }} + volumeMounts: + - name: app-data + mountPath: {{ .Values.app.persistence.mountPath }} + {{- end }} + containers: + - name: open-notebook + image: {{ include "open-notebook.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.app.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.app.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.app.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.app.args "context" $) | nindent 12 }} + {{- end }} + env: + {{- include "open-notebook.envVars" . | nindent 12 }} + {{- if .Values.app.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.app.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.app.extraEnvVarsCM }} + - name: APP_EXTRA_ENV_VARS + valueFrom: + configMapKeyRef: + name: {{ .Values.app.extraEnvVarsCM }} + key: app-extra-env-vars + {{- end }} + {{- if or .Values.config.auth.password .Values.config.auth.existingSecret }} + - name: OPEN_NOTEBOOK_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "open-notebook.secretName" . }} + key: {{ .Values.config.auth.secretKey | default "open-notebook-password" }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.app.ports.http }} + protocol: TCP + - name: api + containerPort: {{ .Values.app.ports.api }} + protocol: TCP + {{- if .Values.app.livenessProbe.enabled }} + livenessProbe: + {{- if eq .Values.app.livenessProbe.probeType "httpGet" }} + httpGet: + path: {{ .Values.app.livenessProbe.path }} + port: {{ .Values.app.livenessProbe.port }} + {{- else if eq .Values.app.livenessProbe.probeType "tcpSocket" }} + tcpSocket: + port: {{ .Values.app.livenessProbe.port }} + {{- end }} + initialDelaySeconds: {{ .Values.app.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.app.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.app.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.app.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.app.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.app.readinessProbe.enabled }} + readinessProbe: + {{- if eq .Values.app.readinessProbe.probeType "httpGet" }} + httpGet: + path: {{ .Values.app.readinessProbe.path }} + port: {{ .Values.app.readinessProbe.port }} + {{- else if eq .Values.app.readinessProbe.probeType "tcpSocket" }} + tcpSocket: + port: {{ .Values.app.readinessProbe.port }} + {{- end }} + initialDelaySeconds: {{ .Values.app.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.app.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.app.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.app.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.app.readinessProbe.successThreshold }} + {{- end }} + {{- if .Values.app.startupProbe.enabled }} + startupProbe: + {{- if eq .Values.app.startupProbe.probeType "httpGet" }} + httpGet: + path: {{ .Values.app.startupProbe.path }} + port: {{ .Values.app.startupProbe.port }} + {{- else if eq .Values.app.startupProbe.probeType "tcpSocket" }} + tcpSocket: + port: {{ .Values.app.startupProbe.port }} + {{- end }} + initialDelaySeconds: {{ .Values.app.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.app.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.app.startupProbe.timeoutSeconds }} + failureThreshold: {{ .Values.app.startupProbe.failureThreshold }} + successThreshold: {{ .Values.app.startupProbe.successThreshold }} + {{- end }} + {{- if .Values.app.resources }} + resources: {{- include "common.tplvalues.render" (dict "value" .Values.app.resources "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.app.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.app.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.app.persistence.enabled }} + - name: app-data + mountPath: {{ .Values.app.persistence.mountPath }} + {{- end }} + {{- if .Values.app.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.app.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.app.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.app.affinity "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.app.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.app.tolerations "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if and .Values.app.persistence.enabled .Values.app.persistence.existingClaim }} + - name: app-data + persistentVolumeClaim: + claimName: {{ .Values.app.persistence.existingClaim }} + {{- else if .Values.app.persistence.enabled }} + - name: app-data + persistentVolumeClaim: + claimName: {{ include "common.names.fullname" . }} + {{- end }} diff --git a/helm/templates/ingress.yaml b/helm/templates/ingress.yaml new file mode 100644 index 00000000..af2bc624 --- /dev/null +++ b/helm/templates/ingress.yaml @@ -0,0 +1,66 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} +{{- if .Values.ingress.enabled -}} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.ingress.annotations "context" $ ) | nindent 4 }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + {{- if .service }} + service: + name: {{ include "common.names.fullname" $ }} + port: + name: {{ .service }} + {{- else }} + service: + name: {{ include "common.names.fullname" $ }} + port: + number: {{ $.Values.service.ports.http }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/templates/pvc.yaml b/helm/templates/pvc.yaml new file mode 100644 index 00000000..d7414221 --- /dev/null +++ b/helm/templates/pvc.yaml @@ -0,0 +1,42 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} +{{- if and .Values.app.persistence.enabled (not .Values.app.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.app.persistence.accessMode }} + {{- if .Values.app.persistence.storageClass }} + {{- if (eq "-" .Values.app.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.app.persistence.storageClass | quote }} + {{- end }} + {{- end }} + resources: + requests: + storage: {{ .Values.app.persistence.size }} +{{- end }} diff --git a/helm/templates/secret.yaml b/helm/templates/secret.yaml new file mode 100644 index 00000000..bc2ab615 --- /dev/null +++ b/helm/templates/secret.yaml @@ -0,0 +1,118 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} + +{{/* +Secret for AI provider API keys +*/}} +{{- $apiSecretName := printf "%s-secret" (include "common.names.fullname" .) }} +{{- if and .Values.secrets.create (not .Values.secrets.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $apiSecretName }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.secrets.annotations "context" $ ) | nindent 4 }} +type: Opaque +stringData: + {{- if .Values.config.aiProviders.openai.apiKey }} + {{ .Values.config.aiProviders.openai.secretKey | default "openai-api-key" }}: {{ .Values.config.aiProviders.openai.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.anthropic.apiKey }} + {{ .Values.config.aiProviders.anthropic.secretKey | default "anthropic-api-key" }}: {{ .Values.config.aiProviders.anthropic.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.google.apiKey }} + {{ .Values.config.aiProviders.google.secretKey | default "google-api-key" }}: {{ .Values.config.aiProviders.google.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.mistral.apiKey }} + {{ .Values.config.aiProviders.mistral.secretKey | default "mistral-api-key" }}: {{ .Values.config.aiProviders.mistral.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.deepseek.apiKey }} + {{ .Values.config.aiProviders.deepseek.secretKey | default "deepseek-api-key" }}: {{ .Values.config.aiProviders.deepseek.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.openrouter.apiKey }} + {{ .Values.config.aiProviders.openrouter.secretKey | default "openrouter-api-key" }}: {{ .Values.config.aiProviders.openrouter.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.groq.apiKey }} + {{ .Values.config.aiProviders.groq.secretKey | default "groq-api-key" }}: {{ .Values.config.aiProviders.groq.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.xai.apiKey }} + {{ .Values.config.aiProviders.xai.secretKey | default "xai-api-key" }}: {{ .Values.config.aiProviders.xai.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.elevenlabs.apiKey }} + {{ .Values.config.aiProviders.elevenlabs.secretKey | default "elevenlabs-api-key" }}: {{ .Values.config.aiProviders.elevenlabs.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.voyage.apiKey }} + {{ .Values.config.aiProviders.voyage.secretKey | default "voyage-api-key" }}: {{ .Values.config.aiProviders.voyage.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.firecrawl.apiKey }} + {{ .Values.config.aiProviders.firecrawl.secretKey | default "firecrawl-api-key" }}: {{ .Values.config.aiProviders.firecrawl.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.jina.apiKey }} + {{ .Values.config.aiProviders.jina.secretKey | default "jina-api-key" }}: {{ .Values.config.aiProviders.jina.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.langchain.apiKey }} + {{ .Values.config.aiProviders.langchain.secretKey | default "langchain-api-key" }}: {{ .Values.config.aiProviders.langchain.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.openaiCompatible.apiKey }} + OPENAI_COMPATIBLE_API_KEY: {{ .Values.config.aiProviders.openaiCompatible.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.azureOpenai.apiKey }} + {{ .Values.config.aiProviders.azureOpenai.secretKey | default "azure-openai-api-key" }}: {{ .Values.config.aiProviders.azureOpenai.apiKey | quote }} + {{- end }} + {{- if .Values.config.aiProviders.vertexai.credentials }} + GOOGLE_APPLICATION_CREDENTIALS_JSON: {{ .Values.config.aiProviders.vertexai.credentials | quote }} + {{- end }} +{{- end }} + +{{/* +Secret for application authentication password +*/}} +{{- if and .Values.config.auth.password (not .Values.config.auth.existingSecret) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "open-notebook.secretName" . }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} +type: Opaque +stringData: + {{ .Values.config.auth.secretKey | default "open-notebook-password" }}: {{ .Values.config.auth.password | quote }} +{{- end }} + +{{/* +Secret for SurrealDB authentication +*/}} +{{- if and .Values.surrealdb.enabled .Values.surrealdb.auth.password (not .Values.surrealdb.auth.existingSecret) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "open-notebook.surrealdb.secretName" . }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.surrealdb.labels" . | nindent 4 }} +type: Opaque +stringData: + {{ .Values.surrealdb.auth.secretKey.username | default "username" }}: {{ .Values.surrealdb.auth.user | quote }} + {{ .Values.surrealdb.auth.secretKey.password | default "password" }}: {{ .Values.surrealdb.auth.password | quote }} +{{- end }} diff --git a/helm/templates/service.yaml b/helm/templates/service.yaml new file mode 100644 index 00000000..0cb4a541 --- /dev/null +++ b/helm/templates/service.yaml @@ -0,0 +1,73 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.labels" . | nindent 4 }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $ ) | nindent 4 }} +spec: + type: {{ .Values.service.type }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + - name: http + port: {{ .Values.service.ports.http }} + targetPort: http + protocol: TCP + {{- if and (eq .Values.service.type "NodePort") .Values.service.ports.httpNodePort }} + nodePort: {{ .Values.service.ports.httpNodePort }} + {{- end }} + - name: api + port: {{ .Values.service.ports.api }} + targetPort: api + protocol: TCP + {{- if and (eq .Values.service.type "NodePort") .Values.service.ports.apiNodePort }} + nodePort: {{ .Values.service.ports.apiNodePort }} + {{- end }} + selector: {{- include "open-notebook.selectorLabels" . | nindent 4 }} + +--- +{{- if .Values.surrealdb.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "common.names.fullname" . }}-surrealdb + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.surrealdb.labels" . | nindent 4 }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.surrealdb.service.annotations "context" $ ) | nindent 4 }} +spec: + type: {{ .Values.surrealdb.service.type }} + ports: + - name: http + port: {{ .Values.surrealdb.service.port }} + targetPort: http + protocol: TCP + {{- if and (eq .Values.surrealdb.service.type "NodePort") .Values.surrealdb.service.nodePort }} + nodePort: {{ .Values.surrealdb.service.nodePort }} + {{- end }} + selector: {{- include "open-notebook.surrealdb.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/templates/statefulset.yaml b/helm/templates/statefulset.yaml new file mode 100644 index 00000000..65a0ee5b --- /dev/null +++ b/helm/templates/statefulset.yaml @@ -0,0 +1,140 @@ +{{- /* +Copyright © 2024 Luis Novo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ -}} +{{- if .Values.surrealdb.enabled }} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "common.names.fullname" . }}-surrealdb + namespace: {{ include "common.names.namespace" . }} + labels: {{- include "open-notebook.surrealdb.labels" . | nindent 4 }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.global.annotations "context" $ ) | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: {{- include "open-notebook.surrealdb.selectorLabels" . | nindent 6 }} + serviceName: {{ include "common.names.fullname" . }}-surrealdb + template: + metadata: + labels: {{- include "open-notebook.surrealdb.selectorLabels" . | nindent 8 }} + {{- if .Values.surrealdb.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.surrealdb.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if .Values.surrealdb.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.surrealdb.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "common.images.pullSecrets" . | nindent 6 }} + {{- if .Values.surrealdb.podSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.surrealdb.podSecurityContext "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.surrealdb.priorityClassName }} + priorityClassName: {{ .Values.surrealdb.priorityClassName | quote }} + {{- end }} + containers: + - name: surrealdb + image: {{ include "open-notebook.surrealdb.image" . }} + imagePullPolicy: {{ .Values.surrealdb.image.pullPolicy }} + command: + - /surreal + - start + - --log + - info + - --bind + - 0.0.0.0:{{ .Values.surrealdb.service.port }} + - -- + - rocksdb:{{ .Values.surrealdb.persistence.mountPath }}/mydatabase.db + env: + - name: SURREAL_USER + valueFrom: + secretKeyRef: + name: {{ include "open-notebook.surrealdb.secretName" . }} + key: {{ .Values.surrealdb.auth.secretKey.username | default "username" }} + - name: SURREAL_PASS + valueFrom: + secretKeyRef: + name: {{ include "open-notebook.surrealdb.secretName" . }} + key: {{ .Values.surrealdb.auth.secretKey.password | default "password" }} + {{- if .Values.surrealdb.experimental.graphql }} + - name: SURREAL_EXPERIMENTAL_GRAPHQL + value: "true" + {{- end }} + ports: + - name: http + containerPort: {{ .Values.surrealdb.service.port }} + protocol: TCP + {{- if .Values.surrealdb.livenessProbe.enabled }} + livenessProbe: + tcpSocket: + port: http + initialDelaySeconds: {{ .Values.surrealdb.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.surrealdb.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.surrealdb.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.surrealdb.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.surrealdb.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.surrealdb.readinessProbe.enabled }} + readinessProbe: + tcpSocket: + port: http + initialDelaySeconds: {{ .Values.surrealdb.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.surrealdb.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.surrealdb.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.surrealdb.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.surrealdb.readinessProbe.successThreshold }} + {{- end }} + {{- if .Values.surrealdb.resources }} + resources: {{- include "common.tplvalues.render" (dict "value" .Values.surrealdb.resources "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.surrealdb.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.surrealdb.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + - name: surrealdb-data + mountPath: {{ .Values.surrealdb.persistence.mountPath }} + {{- if .Values.surrealdb.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.surrealdb.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.surrealdb.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.surrealdb.affinity "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.surrealdb.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.surrealdb.tolerations "context" $) | nindent 8 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: surrealdb-data + labels: {{- include "open-notebook.surrealdb.labels" . | nindent 10 }} + spec: + accessModes: + - {{ .Values.surrealdb.persistence.accessMode }} + {{- if .Values.surrealdb.persistence.storageClass }} + {{- if (eq "-" .Values.surrealdb.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.surrealdb.persistence.storageClass }} + {{- end }} + {{- end }} + resources: + requests: + storage: {{ .Values.surrealdb.persistence.size }} +{{- end }} diff --git a/helm/values.schema.json b/helm/values.schema.json new file mode 100644 index 00000000..cdefd09d --- /dev/null +++ b/helm/values.schema.json @@ -0,0 +1,610 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Open Notebook Helm Chart Values", + "type": "object", + "properties": { + "global": { + "type": "object", + "properties": { + "commonChartEnabled": { "type": "boolean" }, + "imageRegistry": { "type": "string" }, + "imagePullSecrets": { "type": "array" }, + "storageClass": { "type": "string" }, + "labels": { "type": "object" }, + "annotations": { "type": "object" }, + "clusterDomain": { "type": "string" } + } + }, + "image": { + "type": "object", + "properties": { + "registry": { "type": "string" }, + "repository": { "type": "string" }, + "tag": { "type": "string" }, + "digest": { "type": "string" }, + "pullPolicy": { "enum": ["Always", "IfNotPresent", "Never"] } + } + }, + "surrealdb": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "external": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "host": { "type": "string" }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "secure": { "type": "boolean" } + } + }, + "image": { + "type": "object", + "properties": { + "registry": { "type": "string" }, + "repository": { "type": "string" }, + "tag": { "type": "string" }, + "digest": { "type": "string" }, + "pullPolicy": { "enum": ["Always", "IfNotPresent", "Never"] } + } + }, + "auth": { + "type": "object", + "properties": { + "user": { "type": "string" }, + "password": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { + "type": "object", + "properties": { + "username": { "type": "string" }, + "password": { "type": "string" } + } + } + } + }, + "database": { + "type": "object", + "properties": { + "namespace": { "type": "string" }, + "database": { "type": "string" } + } + }, + "experimental": { + "type": "object", + "properties": { + "graphql": { "type": "boolean" } + } + }, + "persistence": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "existingClaim": { "type": "string" }, + "storageClass": { "type": "string" }, + "accessMode": { "type": "string" }, + "size": { "type": "string" }, + "mountPath": { "type": "string" } + } + }, + "service": { + "type": "object", + "properties": { + "type": { "enum": ["ClusterIP", "LoadBalancer", "NodePort"] }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "nodePort": { "type": ["integer", "null"] }, + "annotations": { "type": "object" } + } + }, + "resources": { + "type": "object", + "properties": { + "limits": { "type": "object" }, + "requests": { "type": "object" } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "initialDelaySeconds": { "type": "integer", "minimum": 0 }, + "periodSeconds": { "type": "integer", "minimum": 1 }, + "timeoutSeconds": { "type": "integer", "minimum": 1 }, + "failureThreshold": { "type": "integer", "minimum": 1 }, + "successThreshold": { "type": "integer", "minimum": 1 } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "initialDelaySeconds": { "type": "integer", "minimum": 0 }, + "periodSeconds": { "type": "integer", "minimum": 1 }, + "timeoutSeconds": { "type": "integer", "minimum": 1 }, + "failureThreshold": { "type": "integer", "minimum": 1 }, + "successThreshold": { "type": "integer", "minimum": 1 } + } + }, + "podAnnotations": { "type": "object" }, + "podLabels": { "type": "object" }, + "podSecurityContext": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "fsGroup": { "type": "integer", "minimum": 0 }, + "runAsUser": { "type": "integer", "minimum": 0 } + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "runAsNonRoot": { "type": "boolean" }, + "allowPrivilegeEscalation": { "type": "boolean" }, + "capabilities": { + "type": "object", + "properties": { + "drop": { "type": "array" } + } + } + } + }, + "nodeSelector": { "type": "object" }, + "tolerations": { "type": "array" }, + "affinity": { "type": "object" }, + "priorityClassName": { "type": "string" } + } + }, + "app": { + "type": "object", + "properties": { + "nameOverride": { "type": "string" }, + "fullnameOverride": { "type": "string" }, + "replicaCount": { "type": "integer", "minimum": 0 }, + "updateStrategy": { + "type": "object", + "properties": { + "type": { "enum": ["RollingUpdate", "Recreate"] }, + "rollingUpdate": { + "type": "object", + "properties": { + "maxSurge": { "type": ["integer", "string"] }, + "maxUnavailable": { "type": ["integer", "string"] } + } + } + } + }, + "ports": { + "type": "object", + "properties": { + "http": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "api": { "type": "integer", "minimum": 1, "maximum": 65535 } + } + }, + "persistence": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "existingClaim": { "type": "string" }, + "storageClass": { "type": "string" }, + "accessMode": { "type": "string" }, + "size": { "type": "string" }, + "mountPath": { "type": "string" } + } + }, + "command": { "type": "array" }, + "args": { "type": "array" }, + "extraEnvVars": { "type": "array" }, + "extraEnvVarsCM": { "type": "string" }, + "extraEnvVarsSecret": { "type": "string" }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "probeType": { "enum": ["httpGet", "tcpSocket", "exec"] }, + "path": { "type": "string" }, + "port": { "type": ["string", "integer"] }, + "initialDelaySeconds": { "type": "integer", "minimum": 0 }, + "periodSeconds": { "type": "integer", "minimum": 1 }, + "timeoutSeconds": { "type": "integer", "minimum": 1 }, + "failureThreshold": { "type": "integer", "minimum": 1 }, + "successThreshold": { "type": "integer", "minimum": 1 } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "probeType": { "enum": ["httpGet", "tcpSocket", "exec"] }, + "path": { "type": "string" }, + "port": { "type": ["string", "integer"] }, + "initialDelaySeconds": { "type": "integer", "minimum": 0 }, + "periodSeconds": { "type": "integer", "minimum": 1 }, + "timeoutSeconds": { "type": "integer", "minimum": 1 }, + "failureThreshold": { "type": "integer", "minimum": 1 }, + "successThreshold": { "type": "integer", "minimum": 1 } + } + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "probeType": { "enum": ["httpGet", "tcpSocket", "exec"] }, + "path": { "type": "string" }, + "port": { "type": ["string", "integer"] }, + "initialDelaySeconds": { "type": "integer", "minimum": 0 }, + "periodSeconds": { "type": "integer", "minimum": 1 }, + "timeoutSeconds": { "type": "integer", "minimum": 1 }, + "failureThreshold": { "type": "integer", "minimum": 1 }, + "successThreshold": { "type": "integer", "minimum": 1 } + } + }, + "resources": { + "type": "object", + "properties": { + "limits": { "type": "object" }, + "requests": { "type": "object" } + } + }, + "podAnnotations": { "type": "object" }, + "podLabels": { "type": "object" }, + "podSecurityContext": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "fsGroup": { "type": "integer", "minimum": 0 } + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "runAsNonRoot": { "type": "boolean" }, + "allowPrivilegeEscalation": { "type": "boolean" }, + "capabilities": { + "type": "object", + "properties": { + "drop": { "type": "array" } + } + } + } + }, + "nodeSelector": { "type": "object" }, + "tolerations": { "type": "array" }, + "affinity": { "type": "object" }, + "priorityClassName": { "type": "string" }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { "type": "boolean" }, + "name": { "type": "string" }, + "automountServiceAccountToken": { "type": "boolean" }, + "annotations": { "type": "object" } + } + }, + "podDisruptionBudget": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "minAvailable": { "type": ["integer", "string"] }, + "maxUnavailable": { "type": ["integer", "string"] } + } + } + } + }, + "service": { + "type": "object", + "properties": { + "type": { "enum": ["ClusterIP", "LoadBalancer", "NodePort"] }, + "ports": { + "type": "object", + "properties": { + "http": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "api": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "httpNodePort": { "type": ["integer", "null"], "minimum": 1, "maximum": 65535 }, + "apiNodePort": { "type": ["integer", "null"], "minimum": 1, "maximum": 65535 } + } + }, + "annotations": { "type": "object" }, + "sessionAffinity": { "enum": ["ClientIP", "None"] }, + "sessionAffinityConfig": { "type": "object" } + } + }, + "ingress": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "className": { "type": "string" }, + "annotations": { "type": "object" }, + "hosts": { "type": "array" }, + "tls": { "type": "array" } + } + }, + "config": { + "type": "object", + "properties": { + "api": { + "type": "object", + "properties": { + "url": { "type": "string" }, + "internalUrl": { "type": "string" }, + "clientTimeout": { "type": "integer", "minimum": 1 }, + "esperantoTimeout": { "type": "integer", "minimum": 1 } + } + }, + "auth": { + "type": "object", + "properties": { + "password": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "ssl": { + "type": "object", + "properties": { + "caBundle": { "type": "string" }, + "verify": { "type": "boolean" } + } + }, + "worker": { + "type": "object", + "properties": { + "maxTasks": { "type": "integer", "minimum": 1 }, + "retry": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "maxAttempts": { "type": "integer", "minimum": 1 }, + "waitStrategy": { "type": "string" }, + "waitMin": { "type": "integer", "minimum": 0 }, + "waitMax": { "type": "integer", "minimum": 0 } + } + } + } + }, + "tts": { + "type": "object", + "properties": { + "batchSize": { "type": "integer", "minimum": 1 } + } + }, + "aiProviders": { + "type": "object", + "properties": { + "openai": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" }, + "baseUrl": { "type": "string" } + } + }, + "anthropic": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "google": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" }, + "geminiBaseUrl": { "type": "string" } + } + }, + "vertexai": { + "type": "object", + "properties": { + "project": { "type": "string" }, + "credentials": { "type": "string" }, + "location": { "type": "string" }, + "existingSecret": { "type": "string" } + } + }, + "mistral": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "deepseek": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "ollama": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" } + } + }, + "openrouter": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" }, + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "groq": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "xai": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "elevenlabs": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "voyage": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "openaiCompatible": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" }, + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "llm": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" }, + "apiKey": { "type": "string" } + } + }, + "embedding": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" }, + "apiKey": { "type": "string" } + } + }, + "stt": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" }, + "apiKey": { "type": "string" } + } + }, + "tts": { + "type": "object", + "properties": { + "baseUrl": { "type": "string" }, + "apiKey": { "type": "string" } + } + } + } + }, + "azureOpenai": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "endpoint": { "type": "string" }, + "apiVersion": { "type": "string" }, + "existingSecret": { "type": "string" }, + "llm": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "endpoint": { "type": "string" }, + "apiVersion": { "type": "string" } + } + }, + "embedding": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "endpoint": { "type": "string" }, + "apiVersion": { "type": "string" } + } + }, + "stt": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "endpoint": { "type": "string" }, + "apiVersion": { "type": "string" } + } + }, + "tts": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "endpoint": { "type": "string" }, + "apiVersion": { "type": "string" } + } + } + } + }, + "firecrawl": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "jina": { + "type": "object", + "properties": { + "apiKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + }, + "langchain": { + "type": "object", + "properties": { + "tracingV2": { "type": "boolean" }, + "endpoint": { "type": "string" }, + "apiKey": { "type": "string" }, + "project": { "type": "string" }, + "existingSecret": { "type": "string" }, + "secretKey": { "type": "string" } + } + } + } + } + } + }, + "secrets": { + "type": "object", + "properties": { + "create": { "type": "boolean" }, + "existingSecret": { "type": "string" }, + "annotations": { "type": "object" } + } + }, + "networkPolicy": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "policyTypes": { "type": "array" }, + "ingress": { "type": "array" }, + "egress": { "type": "array" } + } + }, + "serviceMonitor": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "namespace": { "type": "string" }, + "interval": { "type": "string" }, + "scrapeTimeout": { "type": "string" }, + "labels": { "type": "object" }, + "annotations": { "type": "object" } + } + } + } +} diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 00000000..573221b8 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,883 @@ +# Default values for open-notebook +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +## +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## +global: + ## @param global.commonChartEnabled Enable common chart library compatibility + ## + commonChartEnabled: true + ## @param global.imageRegistry Global Docker image registry + ## ref: https://kubernetes.io/docs/concepts/containers/images/#updating-a-global-image + ## + imageRegistry: "" + ## @param global.imagePullSecrets Global Docker registry secret names as an array + ## e.g.: + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + ## @param global.storageClass Global StorageClass for PVCs + ## ref: https://kubernetes.io/docs/concepts/storage/storage-classes/ + ## + storageClass: "" + ## @param global.labels Labels to add to all deployed objects + ## + labels: {} + ## @param global.annotations Annotations to add to all deployed objects + ## + annotations: {} + ## @param global.clusterDomain Kubernetes cluster domain + ## ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-config + ## + clusterDomain: cluster.local + +## +## Open Notebook image parameters +## +image: + ## @param image.registry Docker image registry + ## + registry: docker.io + ## @param image.repository Docker image repository + ## + repository: lfnovo/open_notebook + ## @param image.tag Docker image tag (immutable tags are recommended) + ## ref: https://kubernetes.io/docs/concepts/containers/images/#image-names + ## + tag: "" + ## @param image.digest Docker image digest (overrides tag when set) + ## ref: https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy + ## + digest: "" + ## @param image.pullPolicy Docker image pull policy + ## ref: https://kubernetes.io/docs/concepts/containers/images/#updating-images + ## + pullPolicy: IfNotPresent + +## +## SurrealDB database configuration +## +surrealdb: + ## @param surrealdb.enabled Deploy SurrealDB as part of the chart + ## If false, configure external SurrealDB connection + ## + enabled: true + + ## @param surrealdb.external.enabled Use external SurrealDB instead of deploying one + ## + external: + enabled: false + ## @param surrealdb.external.host External SurrealDB host + ## + host: "" + ## @param surrealdb.external.port External SurrealDB port + ## + port: 8000 + ## @param surrealdb.external.secure Use HTTPS/WSS for external connection + ## + secure: false + + ## SurrealDB image parameters + ## ref: https://hub.docker.com/r/surrealdb/surrealdb + ## + image: + registry: docker.io + repository: surrealdb/surrealdb + tag: v2 + digest: "" + pullPolicy: IfNotPresent + + ## SurrealDB authentication configuration + ## + auth: + ## @param surrealdb.auth.user SurrealDB username + ## + user: root + ## @param surrealdb.auth.password SurrealDB password + ## NOTE: Must be set if existingSecret is not provided + ## + password: "root" + ## @param surrealdb.auth.existingSecret Existing secret containing SurrealDB credentials + ## NOTE: Must contain keys specified in secretKey.username and secretKey.password + ## + existingSecret: "" + ## @param surrealdb.auth.secretKey Keys in the secret containing credentials + ## + secretKey: + ## @param surrealdb.auth.secretKey.username Key in secret containing username + ## + username: username + ## @param surrealdb.auth.secretKey.password Key in secret containing password + ## + password: password + + ## SurrealDB database configuration + ## + database: + ## @param surrealdb.database.namespace SurrealDB namespace + ## + namespace: open_notebook + ## @param surrealdb.database.database SurrealDB database name + ## + database: production + + ## SurrealDB experimental features + ## + experimental: + ## @param surrealdb.experimental.graphql Enable GraphQL experimental feature + ## + graphql: true + + ## SurrealDB persistence configuration + ## + persistence: + ## @param surrealdb.persistence.enabled Enable persistence using PVC + ## + enabled: true + ## @param surrealdb.persistence.existingClaim Name of an existing PVC to use + ## + existingClaim: "" + ## @param surrealdb.persistence.storageClass StorageClass for PVC + ## ref: https://kubernetes.io/docs/concepts/storage/storage-classes/ + ## + storageClass: "" + ## @param surrealdb.persistence.accessMode PVC access mode + ## + accessMode: ReadWriteOnce + ## @param surrealdb.persistence.size PVC storage request size + ## + size: 8Gi + ## @param surrealdb.persistence.mountPath Mount path for SurrealDB data + ## + mountPath: /mydata + + ## SurrealDB service configuration + ## + service: + ## @param surrealdb.service.type Kubernetes Service type + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + ## + type: ClusterIP + ## @param surrealdb.service.port SurrealDB service port + ## + port: 8000 + ## @param surrealdb.service.nodePort Specific node port when type is NodePort + ## + nodePort: null + ## @param surrealdb.service.annotations Service annotations + ## + annotations: {} + + ## SurrealDB resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + ## + resources: + limits: {} + requests: {} + + ## Configure liveness probe for SurrealDB + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + ## + livenessProbe: + ## @param surrealdb.livenessProbe.enabled Enable liveness probe + ## + enabled: true + ## @param surrealdb.livenessProbe.initialDelaySeconds Initial delay seconds for liveness probe + ## + initialDelaySeconds: 30 + ## @param surrealdb.livenessProbe.periodSeconds Period seconds for liveness probe + ## + periodSeconds: 10 + ## @param surrealdb.livenessProbe.timeoutSeconds Timeout seconds for liveness probe + ## + timeoutSeconds: 5 + ## @param surrealdb.livenessProbe.failureThreshold Failure threshold for liveness probe + ## + failureThreshold: 6 + ## @param surrealdb.livenessProbe.successThreshold Success threshold for liveness probe + ## + successThreshold: 1 + + ## Configure readiness probe for SurrealDB + ## + readinessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + ## @param surrealdb.podAnnotations Annotations for SurrealDB pods + ## + podAnnotations: {} + ## @param surrealdb.podLabels Labels for SurrealDB pods + ## + podLabels: {} + + ## SurrealDB pod security context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## + podSecurityContext: + ## @param surrealdb.podSecurityContext.enabled Enable pod security context + ## + enabled: true + ## @param surrealdb.podSecurityContext.fsGroup File system group ID + ## + fsGroup: 1001 + ## @param surrealdb.podSecurityContext.runAsUser User ID to run as + ## + runAsUser: 1001 + + ## SurrealDB container security context + ## + containerSecurityContext: + enabled: true + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + + ## @param surrealdb.nodeSelector Node selector for SurrealDB pods + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + ## @param surrealdb.tolerations Tolerations for SurrealDB pods + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + ## + tolerations: [] + ## @param surrealdb.affinity Affinity rules for SurrealDB pods + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity + ## + affinity: {} + ## @param surrealdb.priorityClassName Priority class name for SurrealDB pods + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/ + ## + priorityClassName: "" + +## +## Open Notebook application configuration +## +app: + ## @param app.nameOverride String to partially override common.names.fullname template + ## + nameOverride: "" + ## @param app.fullnameOverride String to fully override common.names.fullname template + ## + fullnameOverride: "" + + ## @param app.replicaCount Number of application replicas + ## + replicaCount: 1 + + ## Update strategy for the application deployment + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment + ## + updateStrategy: + ## @param app.updateStrategy.type Deployment update strategy type + ## + type: RollingUpdate + rollingUpdate: + ## @param app.updateStrategy.rollingUpdate.maxSurge Maximum number of pods that can be created above desired + ## + maxSurge: 1 + ## @param app.updateStrategy.rollingUpdate.maxUnavailable Maximum number of pods that can be unavailable + ## + maxUnavailable: 0 + + ## Application ports + ## + ports: + ## @param app.ports.http HTTP frontend port + ## + http: 8502 + ## @param app.ports.api API server port + ## + api: 5055 + + ## Application data persistence + ## + persistence: + enabled: true + existingClaim: "" + storageClass: "" + accessMode: ReadWriteOnce + size: 8Gi + mountPath: /app/data + + ## @param app.command Override default container command + ## + command: [] + ## @param app.args Override default container args + ## + args: [] + + ## Additional environment variables for application containers + ## ref: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/ + ## + extraEnvVars: [] + ## e.g.: + ## extraEnvVars: + ## - name: MY_VAR + ## value: "my-value" + + ## @param app.extraEnvVarsCM Name of existing ConfigMap with extra environment variables + ## + extraEnvVarsCM: "" + ## @param app.extraEnvVarsSecret Name of existing Secret with extra environment variables + ## + extraEnvVarsSecret: "" + + ## Configure liveness probe for the application + ## + livenessProbe: + enabled: true + ## @param app.livenessProbe.probeType Probe type (httpGet, tcpSocket, exec) + ## + probeType: httpGet + ## @param app.livenessProbe.path Path for HTTP probe + ## + path: /health + ## @param app.livenessProbe.port Port for probe + ## + port: api + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + ## Configure readiness probe for the application + ## + readinessProbe: + enabled: true + probeType: httpGet + path: /health + port: api + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + ## Configure startup probe for the application + ## + startupProbe: + enabled: false + probeType: httpGet + path: /health + port: api + initialDelaySeconds: 0 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 30 + successThreshold: 1 + + ## Application resource requests and limits + ## + resources: + limits: {} + requests: {} + ## e.g.: + ## resources: + ## limits: + ## cpu: 500m + ## memory: 512Mi + ## requests: + ## cpu: 100m + ## memory: 128Mi + + ## @param app.podAnnotations Annotations for application pods + ## + podAnnotations: {} + ## @param app.podLabels Labels for application pods + ## + podLabels: {} + + ## Application pod security context + ## + podSecurityContext: + enabled: false + fsGroup: 1001 + + containerSecurityContext: + enabled: false + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + + ## @param app.nodeSelector Node selector for application pods + ## + nodeSelector: {} + ## @param app.tolerations Tolerations for application pods + ## + tolerations: [] + ## @param app.affinity Affinity rules for application pods + ## + affinity: {} + ## @param app.priorityClassName Priority class name for application pods + ## + priorityClassName: "" + + ## Service account configuration + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## + serviceAccount: + ## @param app.serviceAccount.create Create a service account + ## + create: false + ## @param app.serviceAccount.name Name of the service account to use + ## + name: "" + ## @param app.serviceAccount.automountServiceAccountToken Automount service account token + ## + automountServiceAccountToken: false + ## @param app.serviceAccount.annotations Service account annotations + ## + annotations: {} + + ## Pod disruption budget configuration + ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ + ## + podDisruptionBudget: + ## @param app.podDisruptionBudget.enabled Enable pod disruption budget + ## + enabled: false + ## @param app.podDisruptionBudget.minAvailable Minimum number of pods that must be available + ## + minAvailable: 1 + ## @param app.podDisruptionBudget.maxUnavailable Maximum number of pods that can be unavailable + ## + maxUnavailable: "" + +## +## Service configuration +## +service: + ## @param service.type Kubernetes Service type + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + ## + type: ClusterIP + ports: + ## @param service.ports.http HTTP service port + ## + http: 8502 + ## @param service.ports.api API service port + ## + api: 5055 + ## @param service.ports.httpNodePort Node port for HTTP service (when type=NodePort) + ## + httpNodePort: null + ## @param service.ports.apiNodePort Node port for API service (when type=NodePort) + ## + apiNodePort: null + ## @param service.annotations Service annotations + ## + annotations: {} + ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin + ## Values: ClientIP or None + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Session affinity configuration + ## + sessionAffinityConfig: {} + +## +## Ingress configuration +## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/ +## +ingress: + ## @param ingress.enabled Enable ingress record generation + ## + enabled: false + ## @param ingress.className IngressClass that will be used to implement the Ingress + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + className: "" + ## @param ingress.annotations Additional annotations for the Ingress resource + ## e.g.: + ## annotations: + ## cert-manager.io/cluster-issuer: letsencrypt-prod + ## nginx.ingress.kubernetes.io/ssl-redirect: "true" + ## + annotations: {} + ## @param ingress.hosts List of host configurations + ## e.g.: + ## hosts: + ## - host: open-notebook.example.com + ## paths: + ## - path: / + ## pathType: Prefix + ## service: http + ## + hosts: + - host: open-notebook.example.com + paths: + - path: / + pathType: Prefix + service: http + + ## @param ingress.tls TLS configuration for Ingress + ## e.g.: + ## tls: + ## - secretName: open-notebook-tls + ## hosts: + ## - open-notebook.example.com + ## + tls: [] + +## +## Open Notebook application configuration +## +config: + ## API configuration + ## + api: + ## @param config.api.url External API URL for browser access + ## e.g. http://your-domain:30555 or https://api.example.com + ## + url: "" + ## @param config.api.internalUrl Internal API URL for Next.js server-side calls + ## e.g. http://open-notebook.open-notebook.svc.cluster.local:5055 + ## + internalUrl: "" + ## @param config.api.clientTimeout Client timeout in seconds + ## + clientTimeout: 300 + ## @param config.api.esperantoTimeout Esperanto LLM timeout in seconds + ## + esperantoTimeout: 60 + + ## Security configuration + ## + auth: + ## @param config.auth.password Application password (leave empty to disable auth) + ## + password: "" + ## @param config.auth.existingSecret Existing secret containing password + ## NOTE: Must contain key specified by secretKey + ## + existingSecret: "" + ## @param config.auth.secretKey Key in secret containing password + ## + secretKey: open-notebook-password + + ## SSL/TLS configuration + ## + ssl: + ## @param config.ssl.caBundle Path to CA bundle file + ## + caBundle: "" + ## @param config.ssl.verify Verify SSL certificates + ## + verify: true + + ## Worker configuration + ## + worker: + ## @param config.worker.maxTasks Maximum number of concurrent worker tasks + ## + maxTasks: 5 + ## Retry configuration + ## + retry: + ## @param config.worker.retry.enabled Enable retry logic + ## + enabled: true + ## @param config.worker.retry.maxAttempts Maximum number of retry attempts + ## + maxAttempts: 3 + ## @param config.worker.retry.waitStrategy Retry wait strategy + ## Values: exponential_jitter, fixed, linear, exponential + ## + waitStrategy: exponential_jitter + ## @param config.worker.retry.waitMin Minimum wait time in seconds + ## + waitMin: 1 + ## @param config.worker.retry.waitMax Maximum wait time in seconds + ## + waitMax: 30 + + ## TTS (Text-to-Speech) configuration + ## + tts: + ## @param config.tts.batchSize Batch size for TTS processing + ## + batchSize: 5 + + ## AI providers configuration + ## Configure one or more AI providers for LLM, embeddings, STT, and TTS + ## + aiProviders: + ## OpenAI configuration + ## + openai: + ## @param config.aiProviders.openai.apiKey OpenAI API key + ## NOTE: When set and secrets.create=true, will be automatically added to secret + ## + apiKey: "" + ## @param config.aiProviders.openai.existingSecret Existing secret containing API key + ## + existingSecret: "" + ## @param config.aiProviders.openai.secretKey Key in secret containing API key + ## + secretKey: openai-api-key + ## @param config.aiProviders.openai.baseUrl Custom OpenAI base URL + ## + baseUrl: "" + + ## Anthropic (Claude) configuration + ## + anthropic: + apiKey: "" + existingSecret: "" + secretKey: anthropic-api-key + + ## Google Gemini configuration + ## + google: + apiKey: "" + existingSecret: "" + secretKey: google-api-key + ## @param config.aiProviders.google.geminiBaseUrl Custom Gemini base URL + ## + geminiBaseUrl: "" + + ## Google Vertex AI configuration + ## + vertexai: + ## @param config.aiProviders.vertexai.project Google Cloud project ID + ## + project: "" + ## @param config.aiProviders.vertexai.credentials Service account credentials (JSON string or file path) + ## + credentials: "" + ## @param config.aiProviders.vertexai.location Vertex AI region/location + ## + location: us-east5 + existingSecret: "" + + ## Mistral AI configuration + ## + mistral: + apiKey: "" + existingSecret: "" + secretKey: mistral-api-key + + ## DeepSeek configuration + ## + deepseek: + apiKey: "" + existingSecret: "" + secretKey: deepseek-api-key + + ## Ollama configuration + ## + ollama: + ## @param config.aiProviders.ollama.baseUrl Ollama API base URL + ## e.g. http://localhost:11434 + ## + baseUrl: "" + + ## OpenRouter configuration + ## + openrouter: + baseUrl: https://openrouter.ai/api/v1 + apiKey: "" + existingSecret: "" + secretKey: openrouter-api-key + + ## Groq configuration + ## + groq: + apiKey: "" + existingSecret: "" + secretKey: groq-api-key + + ## XAI (Grok) configuration + ## + xai: + apiKey: "" + existingSecret: "" + secretKey: xai-api-key + + ## ElevenLabs configuration (for podcast generation) + ## + elevenlabs: + apiKey: "" + existingSecret: "" + secretKey: elevenlabs-api-key + + ## Voyage AI configuration (embeddings) + ## + voyage: + apiKey: "" + existingSecret: "" + secretKey: voyage-api-key + + ## OpenAI-compatible endpoints configuration + ## For Azure OpenAI, or other OpenAI-compatible APIs + ## + openaiCompatible: + ## @param config.aiProviders.openaiCompatible.baseUrl Base URL for OpenAI-compatible endpoint + ## e.g. https://api.openai.com/v1 for OpenAI + ## + baseUrl: "" + ## @param config.aiProviders.openaiCompatible.apiKey API key for OpenAI-compatible endpoint + ## NOTE: When set and secrets.create=true, will be automatically added to secret + ## + apiKey: "" + ## @param config.aiProviders.openaiCompatible.existingSecret Existing secret containing API key + ## + existingSecret: "" + ## Mode-specific configuration + ## + llm: + ## @param config.aiProviders.openaiCompatible.llm.baseUrl Override base URL for LLM mode + ## + baseUrl: "" + ## @param config.aiProviders.openaiCompatible.llm.apiKey Override API key for LLM mode + ## + apiKey: "" + embedding: + baseUrl: "" + apiKey: "" + stt: + baseUrl: "" + apiKey: "" + tts: + baseUrl: "" + apiKey: "" + + ## Azure OpenAI configuration + ## + azureOpenai: + ## @param config.aiProviders.azureOpenai.apiKey Azure OpenAI API key + ## + apiKey: "" + ## @param config.aiProviders.azureOpenai.endpoint Azure OpenAI endpoint URL + ## e.g. https://your-resource.openai.azure.com + ## + endpoint: "" + ## @param config.aiProviders.azureOpenai.apiVersion Azure OpenAI API version + ## + apiVersion: "2024-12-01-preview" + existingSecret: "" + ## Mode-specific configuration + ## + llm: + apiKey: "" + endpoint: "" + apiVersion: "" + embedding: + apiKey: "" + endpoint: "" + apiVersion: "" + stt: + apiKey: "" + endpoint: "" + apiVersion: "" + tts: + apiKey: "" + endpoint: "" + apiVersion: "" + + ## Firecrawl configuration + ## + firecrawl: + apiKey: "" + existingSecret: "" + secretKey: firecrawl-api-key + + ## Jina configuration + ## + jina: + apiKey: "" + existingSecret: "" + secretKey: jina-api-key + + ## LangChain configuration + ## + langchain: + ## @param config.aiProviders.langchain.tracingV2 Enable LangSmith tracing + ## + tracingV2: false + ## @param config.aiProviders.langchain.endpoint LangSmith API endpoint + ## + endpoint: https://api.smith.langchain.com + apiKey: "" + ## @param config.aiProviders.langchain.project LangSmith project name + ## + project: "Open Notebook" + existingSecret: "" + secretKey: langchain-api-key + +## +## Secret management configuration +## +secrets: + ## @param secrets.create Create secrets for sensitive data + ## When enabled, secrets will be created for any apiKey/password fields that are set + ## + create: true + ## @param secrets.existingSecret Use existing secret for all configuration + ## NOTE: Must contain appropriate keys for each provider + ## + existingSecret: "" + ## @param secrets.annotations Annotations to add to created secrets + ## + annotations: {} + +## +## Network policy configuration +## ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/ +## +networkPolicy: + ## @param networkPolicy.enabled Enable network policies + ## + enabled: false + ## @param networkPolicy.policyTypes Policy types to enforce + ## + policyTypes: + - Ingress + - Egress + ## @param networkPolicy.ingress Ingress rules + ## + ingress: [] + ## @param networkPolicy.egress Egress rules + ## + egress: [] + +## +## ServiceMonitor configuration for Prometheus Operator +## ref: https://prometheus-operator.dev/docs/operator/api/#servicemonitor +## +serviceMonitor: + ## @param serviceMonitor.enabled Create ServiceMonitor resource + ## + enabled: false + ## @param serviceMonitor.namespace Namespace for ServiceMonitor + ## + namespace: "" + ## @param serviceMonitor.interval Scraping interval + ## + interval: 30s + ## @param serviceMonitor.scrapeTimeout Scrape timeout + ## + scrapeTimeout: 10s + ## @param serviceMonitor.labels Additional labels for ServiceMonitor + ## + labels: {} + ## @param serviceMonitor.annotations Additional annotations for ServiceMonitor + ## + annotations: {}