Critical fix: - deployment.yaml: Only reference OAuth credentials when clientId is set - Fixes pod failure when using existingSecret without static OAuth creds - Aligns deployment behavior with secret template logic Previously, the deployment referenced OAuth credentials when either clientId OR existingSecret was set. However, the secret template only includes OAuth credentials when clientId is explicitly provided. This caused pod failures when users provided an existingSecret for offline access without static OAuth credentials (intending to use DCR). The fix ensures OAuth env vars are only referenced when clientId is set, matching the OAuth mode pattern and allowing DCR to work correctly with existingSecret configurations. Minor improvements: - values.yaml: Clarify OAuth credentials are optional (uses DCR if not provided) Testing verified all scenarios: ✅ Pass-through only (no offline access): No secrets/PVCs/OAuth vars ✅ Offline + DCR (no clientId): Secret with encryption key only, no OAuth vars ✅ Offline + static OAuth: Secret with all keys, OAuth vars present ✅ existingSecret without clientId: No auto secret, no OAuth vars (FIXED) Resolves reviewer feedback from PR #447
333 lines
15 KiB
YAML
333 lines
15 KiB
YAML
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
|
labels:
|
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
spec:
|
|
strategy:
|
|
type: Recreate
|
|
{{- if not .Values.autoscaling.enabled }}
|
|
replicas: {{ .Values.replicaCount }}
|
|
{{- end }}
|
|
selector:
|
|
matchLabels:
|
|
{{- include "nextcloud-mcp-server.selectorLabels" . | nindent 6 }}
|
|
template:
|
|
metadata:
|
|
annotations:
|
|
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
|
{{- with .Values.podAnnotations }}
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
labels:
|
|
{{- include "nextcloud-mcp-server.labels" . | nindent 8 }}
|
|
{{- with .Values.podLabels }}
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
spec:
|
|
{{- with .Values.imagePullSecrets }}
|
|
imagePullSecrets:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
serviceAccountName: {{ include "nextcloud-mcp-server.serviceAccountName" . }}
|
|
securityContext:
|
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
|
{{- with .Values.initContainers }}
|
|
initContainers:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
containers:
|
|
- name: {{ .Chart.Name }}
|
|
securityContext:
|
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
|
image: "{{ .Values.image.repository }}:{{ include "nextcloud-mcp-server.imageTag" . }}"
|
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
args:
|
|
- "--transport"
|
|
- "{{ .Values.mcp.transport }}"
|
|
{{- if eq .Values.auth.mode "oauth" }}
|
|
- "--oauth"
|
|
- "--oauth-token-type"
|
|
- "{{ .Values.auth.oauth.tokenType }}"
|
|
{{- end }}
|
|
{{- with .Values.mcp.extraArgs }}
|
|
{{- toYaml . | nindent 12 }}
|
|
{{- end }}
|
|
ports:
|
|
- name: http
|
|
containerPort: {{ include "nextcloud-mcp-server.port" . }}
|
|
protocol: TCP
|
|
{{- if .Values.observability.metrics.enabled }}
|
|
- name: metrics
|
|
containerPort: {{ .Values.observability.metrics.port }}
|
|
protocol: TCP
|
|
{{- end }}
|
|
env:
|
|
# Nextcloud connection
|
|
- name: NEXTCLOUD_HOST
|
|
value: {{ .Values.nextcloud.host | quote }}
|
|
{{- if eq .Values.auth.mode "basic" }}
|
|
# Basic auth mode (single-user)
|
|
- name: NEXTCLOUD_USERNAME
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.basicAuthSecretName" . }}
|
|
key: {{ .Values.auth.basic.usernameKey }}
|
|
- name: NEXTCLOUD_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.basicAuthSecretName" . }}
|
|
key: {{ .Values.auth.basic.passwordKey }}
|
|
{{- else if eq .Values.auth.mode "multi-user-basic" }}
|
|
# Multi-user BasicAuth mode (pass-through)
|
|
- name: ENABLE_MULTI_USER_BASIC_AUTH
|
|
value: "true"
|
|
- name: NEXTCLOUD_MCP_SERVER_URL
|
|
value: {{ include "nextcloud-mcp-server.mcpServerUrl" . | quote }}
|
|
- name: NEXTCLOUD_PUBLIC_ISSUER_URL
|
|
value: {{ include "nextcloud-mcp-server.publicIssuerUrl" . | quote }}
|
|
{{- if .Values.auth.multiUserBasic.enableOfflineAccess }}
|
|
# Background operations with app passwords
|
|
- name: ENABLE_OFFLINE_ACCESS
|
|
value: "true"
|
|
- name: TOKEN_STORAGE_DB
|
|
value: {{ .Values.auth.multiUserBasic.tokenStorageDb | quote }}
|
|
- name: TOKEN_ENCRYPTION_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.multiUserBasicSecretName" . }}
|
|
key: {{ .Values.auth.multiUserBasic.tokenEncryptionKeyKey }}
|
|
- name: NEXTCLOUD_OIDC_SCOPES
|
|
value: {{ .Values.auth.multiUserBasic.scopes | quote }}
|
|
{{- if .Values.auth.multiUserBasic.clientId }}
|
|
# Static OAuth credentials (optional - uses DCR if not provided)
|
|
- name: NEXTCLOUD_OIDC_CLIENT_ID
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.multiUserBasicSecretName" . }}
|
|
key: {{ .Values.auth.multiUserBasic.clientIdKey }}
|
|
- name: NEXTCLOUD_OIDC_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.multiUserBasicSecretName" . }}
|
|
key: {{ .Values.auth.multiUserBasic.clientSecretKey }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- else if eq .Values.auth.mode "oauth" }}
|
|
# OAuth mode
|
|
- name: NEXTCLOUD_MCP_SERVER_URL
|
|
value: {{ include "nextcloud-mcp-server.mcpServerUrl" . | quote }}
|
|
- name: NEXTCLOUD_PUBLIC_ISSUER_URL
|
|
value: {{ include "nextcloud-mcp-server.publicIssuerUrl" . | quote }}
|
|
- name: NEXTCLOUD_OIDC_SCOPES
|
|
value: {{ .Values.auth.oauth.scopes | quote }}
|
|
{{- if .Values.auth.oauth.clientId }}
|
|
- name: NEXTCLOUD_OIDC_CLIENT_ID
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.oauthSecretName" . }}
|
|
key: {{ .Values.auth.oauth.clientIdKey }}
|
|
- name: NEXTCLOUD_OIDC_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ include "nextcloud-mcp-server.oauthSecretName" . }}
|
|
key: {{ .Values.auth.oauth.clientSecretKey }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- if .Values.documentProcessing.enabled }}
|
|
# Document processing
|
|
- name: ENABLE_DOCUMENT_PROCESSING
|
|
value: {{ .Values.documentProcessing.enabled | quote }}
|
|
- name: DOCUMENT_PROCESSOR
|
|
value: {{ .Values.documentProcessing.defaultProcessor | quote }}
|
|
- name: PROGRESS_INTERVAL
|
|
value: {{ .Values.documentProcessing.progressInterval | quote }}
|
|
{{- if .Values.documentProcessing.unstructured.enabled }}
|
|
- name: ENABLE_UNSTRUCTURED
|
|
value: "true"
|
|
- name: UNSTRUCTURED_API_URL
|
|
value: {{ .Values.documentProcessing.unstructured.apiUrl | quote }}
|
|
- name: UNSTRUCTURED_TIMEOUT
|
|
value: {{ .Values.documentProcessing.unstructured.timeout | quote }}
|
|
- name: UNSTRUCTURED_STRATEGY
|
|
value: {{ .Values.documentProcessing.unstructured.strategy | quote }}
|
|
- name: UNSTRUCTURED_LANGUAGES
|
|
value: {{ .Values.documentProcessing.unstructured.languages | quote }}
|
|
{{- end }}
|
|
{{- if .Values.documentProcessing.tesseract.enabled }}
|
|
- name: ENABLE_TESSERACT
|
|
value: "true"
|
|
{{- if .Values.documentProcessing.tesseract.cmd }}
|
|
- name: TESSERACT_CMD
|
|
value: {{ .Values.documentProcessing.tesseract.cmd | quote }}
|
|
{{- end }}
|
|
- name: TESSERACT_LANG
|
|
value: {{ .Values.documentProcessing.tesseract.lang | quote }}
|
|
{{- end }}
|
|
{{- if .Values.documentProcessing.custom.enabled }}
|
|
- name: ENABLE_CUSTOM_PROCESSOR
|
|
value: "true"
|
|
- name: CUSTOM_PROCESSOR_NAME
|
|
value: {{ .Values.documentProcessing.custom.name | quote }}
|
|
- name: CUSTOM_PROCESSOR_URL
|
|
value: {{ .Values.documentProcessing.custom.url | quote }}
|
|
{{- if .Values.documentProcessing.custom.apiKey }}
|
|
- name: CUSTOM_PROCESSOR_API_KEY
|
|
value: {{ .Values.documentProcessing.custom.apiKey | quote }}
|
|
{{- end }}
|
|
- name: CUSTOM_PROCESSOR_TIMEOUT
|
|
value: {{ .Values.documentProcessing.custom.timeout | quote }}
|
|
- name: CUSTOM_PROCESSOR_TYPES
|
|
value: {{ .Values.documentProcessing.custom.types | quote }}
|
|
{{- end }}
|
|
{{- end }}
|
|
# Vector Sync
|
|
- name: VECTOR_SYNC_ENABLED
|
|
value: {{ .Values.vectorSync.enabled | quote }}
|
|
{{- if .Values.vectorSync.enabled }}
|
|
- name: VECTOR_SYNC_SCAN_INTERVAL
|
|
value: {{ .Values.vectorSync.scanInterval | quote }}
|
|
- name: VECTOR_SYNC_PROCESSOR_WORKERS
|
|
value: {{ .Values.vectorSync.processorWorkers | quote }}
|
|
- name: VECTOR_SYNC_QUEUE_MAX_SIZE
|
|
value: {{ .Values.vectorSync.queueMaxSize | quote }}
|
|
{{- end }}
|
|
# Document Chunking (always set, used by vector sync processor)
|
|
- name: DOCUMENT_CHUNK_SIZE
|
|
value: {{ .Values.documentChunking.chunkSize | quote }}
|
|
- name: DOCUMENT_CHUNK_OVERLAP
|
|
value: {{ .Values.documentChunking.chunkOverlap | quote }}
|
|
# Qdrant Vector Database
|
|
{{- if eq .Values.qdrant.mode "network" }}
|
|
# Network mode: Use dedicated Qdrant service
|
|
{{- if .Values.qdrant.networkMode.deploySubchart }}
|
|
- name: QDRANT_URL
|
|
value: "http://{{ .Release.Name }}-qdrant:6333"
|
|
{{- else if .Values.qdrant.networkMode.externalUrl }}
|
|
- name: QDRANT_URL
|
|
value: {{ .Values.qdrant.networkMode.externalUrl | quote }}
|
|
{{- end }}
|
|
{{- if or .Values.qdrant.networkMode.apiKey .Values.qdrant.networkMode.existingSecret }}
|
|
- name: QDRANT_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Values.qdrant.networkMode.existingSecret | default (printf "%s-qdrant" .Release.Name) }}
|
|
key: {{ .Values.qdrant.networkMode.secretKey }}
|
|
{{- end }}
|
|
{{- else if eq .Values.qdrant.mode "persistent" }}
|
|
# Persistent local mode: File-based storage
|
|
- name: QDRANT_LOCATION
|
|
value: {{ .Values.qdrant.localPersistence.dataPath | quote }}
|
|
{{- else }}
|
|
# In-memory mode (default): Ephemeral storage
|
|
- name: QDRANT_LOCATION
|
|
value: ":memory:"
|
|
{{- end }}
|
|
- name: QDRANT_COLLECTION
|
|
value: {{ .Values.qdrant.collection | quote }}
|
|
# Ollama Embedding Service
|
|
{{- if or .Values.ollama.enabled .Values.ollama.url }}
|
|
- name: OLLAMA_BASE_URL
|
|
value: {{ .Values.ollama.url | default (printf "http://%s-ollama:11434" .Release.Name) | quote }}
|
|
- name: OLLAMA_EMBEDDING_MODEL
|
|
value: {{ .Values.ollama.embeddingModel | quote }}
|
|
- name: OLLAMA_VERIFY_SSL
|
|
value: {{ .Values.ollama.verifySsl | quote }}
|
|
{{- end }}
|
|
# OpenAI Embedding Provider (alternative to Ollama)
|
|
{{- if .Values.openai.enabled }}
|
|
- name: OPENAI_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Values.openai.existingSecret | default (printf "%s-openai" (include "nextcloud-mcp-server.fullname" .)) }}
|
|
key: {{ .Values.openai.secretKey }}
|
|
{{- if .Values.openai.baseUrl }}
|
|
- name: OPENAI_BASE_URL
|
|
value: {{ .Values.openai.baseUrl | quote }}
|
|
{{- end }}
|
|
{{- end }}
|
|
# Observability
|
|
- name: METRICS_ENABLED
|
|
value: {{ .Values.observability.metrics.enabled | quote }}
|
|
- name: METRICS_PORT
|
|
value: {{ .Values.observability.metrics.port | quote }}
|
|
{{- if .Values.observability.tracing.enabled }}
|
|
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
|
value: {{ .Values.observability.tracing.endpoint | quote }}
|
|
- name: OTEL_SERVICE_NAME
|
|
value: {{ .Values.observability.tracing.serviceName | quote }}
|
|
- name: OTEL_TRACES_SAMPLER_ARG
|
|
value: {{ .Values.observability.tracing.samplingRate | quote }}
|
|
{{- end }}
|
|
- name: LOG_FORMAT
|
|
value: {{ .Values.observability.logging.format | quote }}
|
|
- name: LOG_LEVEL
|
|
value: {{ .Values.observability.logging.level | quote }}
|
|
- name: LOG_INCLUDE_TRACE_CONTEXT
|
|
value: {{ .Values.observability.logging.includeTraceContext | quote }}
|
|
{{- with .Values.extraEnv }}
|
|
{{- toYaml . | nindent 12 }}
|
|
{{- end }}
|
|
{{- with .Values.extraEnvFrom }}
|
|
envFrom:
|
|
{{- toYaml . | nindent 12 }}
|
|
{{- end }}
|
|
livenessProbe:
|
|
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
|
readinessProbe:
|
|
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
|
resources:
|
|
{{- toYaml .Values.resources | nindent 12 }}
|
|
volumeMounts:
|
|
- name: tmp
|
|
mountPath: /tmp
|
|
{{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled }}
|
|
- name: oauth-storage
|
|
mountPath: /app/.oauth
|
|
{{- end }}
|
|
{{- if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled }}
|
|
- name: token-storage
|
|
mountPath: /app/data
|
|
{{- end }}
|
|
{{- if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled }}
|
|
- name: qdrant-data
|
|
mountPath: /app/data
|
|
{{- end }}
|
|
{{- with .Values.volumeMounts }}
|
|
{{- toYaml . | nindent 12 }}
|
|
{{- end }}
|
|
volumes:
|
|
- name: tmp
|
|
emptyDir: {}
|
|
{{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled }}
|
|
- name: oauth-storage
|
|
persistentVolumeClaim:
|
|
claimName: {{ include "nextcloud-mcp-server.oauthPvcName" . }}
|
|
{{- end }}
|
|
{{- if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled }}
|
|
- name: token-storage
|
|
persistentVolumeClaim:
|
|
claimName: {{ include "nextcloud-mcp-server.multiUserBasicPvcName" . }}
|
|
{{- end }}
|
|
{{- if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled }}
|
|
- name: qdrant-data
|
|
persistentVolumeClaim:
|
|
claimName: {{ include "nextcloud-mcp-server.qdrantPvcName" . }}
|
|
{{- end }}
|
|
{{- with .Values.volumes }}
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
{{- with .Values.nodeSelector }}
|
|
nodeSelector:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
{{- with .Values.affinity }}
|
|
affinity:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
{{- with .Values.tolerations }}
|
|
tolerations:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|