Files
claude-code/templates/deployment.yaml
T
claude c31dd5875c
Helm Chart Release / release-chart (push) Successful in 7s
Build Session Image / build-push (push) Successful in 2m5s
feat: git identity を Web UI から登録・削除できるようにする (v2.1.263-f)
helm --set-file ではなく UI から SSH 秘密鍵 + known_hosts / HTTPS トークンを登録可能に。

- git.guiIdentities.enabled(既定 true)。UI に「認証情報」カード(一覧/追加/削除)
- GET/POST/DELETE /api/identities。登録分はセッション NS の Secret
  (ラベル identity-source=gui / identity-type)として保存。Helm 管理分と同一覧に統合
- manager.py: resolve_identity / list_identities / create_gui_identity / delete_gui_identity。
  build_pod と create_session は Helm 管理 + GUI 登録の両方を解決。
  /api/config の identities を {name,type,source} 配列に変更
- rbac.yaml: guiIdentities.enabled 時のみ manager Role に同 NS secrets の CRUD を付与
- 秘密鍵はリクエストボディで manager へ届くため README に経路 TLS 必須を明記
- helm lint / template 検証済み
2026-09-09 12:47:50 +09:00

266 lines
11 KiB
YAML

{{- $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: ANTHROPIC_AUTH_MODE
value: {{ .Values.anthropic.authMode | quote }}
- name: ANTHROPIC_CREDENTIALS_KEY
value: {{ .Values.anthropic.existingSecretKeyCredentials | default ".credentials.json" | quote }}
- name: GIT_IDENTITIES_JSON
value: {{ $idents | toJson | quote }}
- name: GUI_IDENTITIES_ENABLED
value: {{ .Values.git.guiIdentities.enabled | quote }}
- name: GIT_FULLNAME_PREFIX
value: {{ include "claude-code.fullname" . | 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: TERMINATED_RETENTION_SECONDS
value: {{ .Values.session.reaper.terminatedRetentionSeconds | 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 }}