{{- $sessNs := include "claude-code.sessionsNamespace" . -}} {{- $ccVersion := .Values.session.claudeCodeVersion | default .Chart.AppVersion -}} {{- /* --- git identities を manager が解釈できる JSON へ変換 --- */ -}} {{- $idents := dict -}} {{- range $name, $id := .Values.git.identities -}} {{- $entry := dict "type" $id.type "secret" (include "claude-code.gitIdentitySecretName" (dict "ctx" $ "name" $name)) -}} {{- if eq $id.type "ssh" -}} {{- $_ := set $entry "keyPrivateKey" ($id.existingSecretKeyPrivateKey | default "id") -}} {{- $_ := set $entry "keyKnownHosts" ($id.existingSecretKeyKnownHosts | default "known_hosts") -}} {{- else if eq $id.type "https-token" -}} {{- $_ := set $entry "keyToken" ($id.existingSecretKeyToken | default "token") -}} {{- $_ := set $entry "username" ($id.username | default "x-access-token") -}} {{- end -}} {{- $_ := set $idents $name $entry -}} {{- end -}} apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "claude-code.fullname" . }} namespace: {{ .Release.Namespace }} labels: {{- include "claude-code.labels" . | nindent 4 }} app.kubernetes.io/component: control-plane spec: replicas: {{ .Values.gateway.replicaCount }} strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 selector: matchLabels: {{- include "claude-code.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: control-plane template: metadata: labels: {{- include "claude-code.selectorLabels" . | nindent 8 }} app.kubernetes.io/component: control-plane annotations: checksum/manager: {{ .Files.Get "files/manager.py" | sha256sum }} checksum/gateway: {{ print (.Files.Get "files/gateway.conf.template") (.Files.Get "files/gateway-entrypoint.sh") | sha256sum }} checksum/session: {{ .Files.Get "files/session-entrypoint.sh" | sha256sum }} {{- with .Values.commonAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} spec: serviceAccountName: {{ include "claude-code.managerServiceAccountName" . }} automountServiceAccountToken: true {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} securityContext: {{- include "claude-code.podSecurityContext" . | nindent 8 }} containers: # ---- gateway: 受付・認証・WebSocket プロキシ ---- - name: gateway image: "{{ .Values.image.gateway.registry }}/{{ .Values.image.gateway.repository }}:{{ .Values.image.gateway.tag }}" imagePullPolicy: {{ .Values.image.gateway.pullPolicy }} command: ["/bin/sh", "/etc/gateway/entrypoint.sh"] env: - name: GATEWAY_PORT value: {{ .Values.gateway.port | quote }} - name: MANAGER_PORT value: "8081" - name: TTYD_PORT value: {{ .Values.session.ttydPort | quote }} - name: SESSIONS_NS value: {{ $sessNs | quote }} - name: AUTH_MODE value: {{ .Values.auth.mode | quote }} {{- if eq .Values.auth.mode "forward" }} - name: AUTH_FORWARD_URL value: {{ required "auth.mode=forward には auth.forward.url が必須です" .Values.auth.forward.url | quote }} {{- end }} - name: NGINX_ERROR_LEVEL value: {{ eq .Values.manager.logLevel "debug" | ternary "info" "warn" | quote }} ports: - name: http containerPort: {{ .Values.gateway.port }} securityContext: {{- include "claude-code.containerSecurityContext" . | nindent 12 }} readinessProbe: httpGet: { path: /healthz, port: http } periodSeconds: 10 livenessProbe: httpGet: { path: /healthz, port: http } initialDelaySeconds: 10 periodSeconds: 20 resources: {{- toYaml .Values.gateway.resources | nindent 12 }} volumeMounts: - name: gateway-config mountPath: /etc/gateway readOnly: true - name: nginx-tmp mountPath: /tmp/nginx {{- if eq .Values.auth.mode "basic" }} - name: gateway-secret mountPath: /etc/gateway-secret readOnly: true {{- end }} # ---- manager: ライフサイクル API / UI / 回収ループ ---- - name: manager image: "{{ .Values.image.manager.registry }}/{{ .Values.image.manager.repository }}:{{ .Values.image.manager.tag }}" imagePullPolicy: {{ .Values.image.manager.pullPolicy }} command: ["python3", "-u", "/app/manager.py"] env: - name: LISTEN_PORT value: "8081" - name: PYTHONDONTWRITEBYTECODE value: "1" - name: LOG_LEVEL value: {{ .Values.manager.logLevel | quote }} - name: FULLNAME value: {{ include "claude-code.fullname" . | quote }} - name: SESSIONS_NAMESPACE value: {{ $sessNs | quote }} - name: SESSION_SERVICE_ACCOUNT value: {{ include "claude-code.sessionServiceAccountName" . | quote }} - name: SESSION_IMAGE value: "{{ .Values.image.session.registry }}/{{ .Values.image.session.repository }}:{{ .Values.image.session.tag }}" - name: SESSION_IMAGE_PULL_POLICY value: {{ .Values.image.session.pullPolicy | quote }} {{- with .Values.imagePullSecrets }} - name: SESSION_IMAGE_PULL_SECRETS_JSON value: {{ toJson . | quote }} {{- end }} - name: SESSION_BOOTSTRAP_CONFIGMAP value: {{ include "claude-code.fullname" . }}-session - name: ANTHROPIC_SECRET value: {{ include "claude-code.anthropicSecretName" . | quote }} - name: GIT_IDENTITIES_JSON value: {{ $idents | toJson | quote }} - name: GIT_ALLOWED_HOSTS value: {{ join "," .Values.git.allowedHosts | quote }} - name: GIT_ALLOW_ARBITRARY value: {{ .Values.git.allowArbitraryRepos | quote }} - name: GIT_DEFAULT_AUTHOR_NAME value: {{ .Values.git.defaultAuthorName | quote }} - name: GIT_DEFAULT_AUTHOR_EMAIL value: {{ .Values.git.defaultAuthorEmail | quote }} - name: IDLE_TIMEOUT_SECONDS value: {{ .Values.session.idleTimeoutSeconds | quote }} - name: MAX_LIFETIME_SECONDS value: {{ .Values.session.maxLifetimeSeconds | quote }} - name: REAP_INTERVAL_SECONDS value: {{ .Values.session.reaper.intervalSeconds | quote }} - name: MAX_SESSIONS value: {{ .Values.session.maxSessions | quote }} - name: TTYD_PORT value: {{ .Values.session.ttydPort | quote }} - name: SESSION_RESOURCES_JSON value: {{ toJson .Values.session.resources | quote }} - name: WORKSPACE_DIR value: {{ .Values.session.workspaceDir | quote }} - name: AUTOSTART_CLAUDE value: {{ .Values.session.autoStartClaude | quote }} - name: VERIFY_COMMAND value: {{ .Values.session.verifyCommand | quote }} - name: AUTOPUSH_JSON value: {{ toJson .Values.session.autoPush | quote }} - name: EXTRA_APK_PACKAGES value: {{ join " " .Values.session.extraApkPackages | quote }} - name: CLAUDE_CODE_VERSION value: {{ $ccVersion | quote }} {{- with .Values.anthropic.extraEnv }} - name: ANTHROPIC_EXTRA_ENV_JSON value: {{ toJson . | quote }} {{- end }} {{- with .Values.session.podLabels }} - name: SESSION_POD_LABELS_JSON value: {{ toJson . | quote }} {{- end }} {{- with .Values.session.podAnnotations }} - name: SESSION_POD_ANNOTATIONS_JSON value: {{ toJson . | quote }} {{- end }} {{- with .Values.session.nodeSelector }} - name: SESSION_NODE_SELECTOR_JSON value: {{ toJson . | quote }} {{- end }} {{- with .Values.session.tolerations }} - name: SESSION_TOLERATIONS_JSON value: {{ toJson . | quote }} {{- end }} {{- with .Values.session.priorityClassName }} - name: SESSION_PRIORITY_CLASS value: {{ . | quote }} {{- end }} - name: AUTH_MODE value: {{ .Values.auth.mode | quote }} {{- if eq .Values.auth.mode "token" }} - name: AUTH_TOKEN_FILE value: /etc/manager-secret/token {{- end }} ports: - name: manager containerPort: 8081 securityContext: {{- include "claude-code.containerSecurityContext" . | nindent 12 }} readinessProbe: httpGet: { path: /healthz, port: manager } periodSeconds: 10 livenessProbe: httpGet: { path: /healthz, port: manager } initialDelaySeconds: 10 periodSeconds: 20 resources: {{- toYaml .Values.manager.resources | nindent 12 }} volumeMounts: - name: manager-app mountPath: /app readOnly: true - name: manager-tmp mountPath: /tmp {{- if eq .Values.auth.mode "token" }} - name: manager-secret mountPath: /etc/manager-secret readOnly: true {{- end }} volumes: - name: gateway-config configMap: name: {{ include "claude-code.fullname" . }}-gateway defaultMode: 0555 - name: nginx-tmp emptyDir: medium: Memory sizeLimit: 16Mi - name: manager-app configMap: name: {{ include "claude-code.fullname" . }}-manager defaultMode: 0555 - name: manager-tmp emptyDir: medium: Memory sizeLimit: 16Mi {{- if eq .Values.auth.mode "token" }} - name: manager-secret secret: secretName: {{ include "claude-code.authSecretName" . }} items: - key: {{ .Values.auth.token.existingSecretKey | default "token" }} path: token {{- end }} {{- if eq .Values.auth.mode "basic" }} - name: gateway-secret secret: secretName: {{ include "claude-code.authSecretName" . }} items: - key: {{ .Values.auth.basic.existingSecretKey | default "htpasswd" }} path: htpasswd {{- end }}