# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: {{ include "snapshot.fullname" . }}-agent
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "snapshot.labels" . | nindent 4 }}
spec:
  selector:
    matchLabels:
      {{- include "snapshot.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "snapshot.selectorLabels" . | nindent 8 }}
        {{- with .Values.daemonset.podLabels }}
        {{- toYaml . | nindent 8 }}
        {{- end }}
      {{- with .Values.daemonset.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    spec:
      serviceAccountName: {{ include "snapshot.serviceAccountName" . }}
      hostPID: true
      hostIPC: true
      hostNetwork: true
      {{- with .Values.daemonset.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      tolerations:
        # Default: tolerate all taints (allow running on any node)
        - operator: Exists
        {{- with .Values.daemonset.tolerations }}
        {{- toYaml . | nindent 8 }}
        {{- end }}
      {{- if and .Values.daemonset.affinity (hasKey .Values.daemonset.affinity "nodeAffinity") }}
      {{- fail "daemonset.affinity.nodeAffinity is not supported because the chart already enforces kubernetes.io/arch=amd64; use daemonset.nodeSelector or daemonset.affinity.podAffinity/podAntiAffinity instead" }}
      {{- end }}
      affinity:
        # cuda-checkpoint only supports x86_64 — never schedule on arm64 nodes
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/arch
                    operator: In
                    values:
                      - amd64
        {{- with .Values.daemonset.affinity }}
        {{- toYaml . | nindent 8 }}
        {{- end }}
      # CUDA checkpoint/restore requires the nvidia container runtime
      runtimeClassName: nvidia
      {{- if .Values.seccomp.deploy }}
      initContainers:
        # Deploy seccomp profile to host before starting the agent
        # This profile blocks io_uring syscalls that CRIU doesn't support
        - name: deploy-seccomp
          image: busybox:latest
          command:
            - sh
            - -c
            - |
              mkdir -p /host-seccomp/profiles
              cp /seccomp-profiles/block-iouring.json /host-seccomp/profiles/block-iouring.json
              echo "Deployed seccomp profile to /var/lib/kubelet/seccomp/profiles/block-iouring.json"
          volumeMounts:
            - name: seccomp-profiles
              mountPath: /seccomp-profiles
              readOnly: true
            - name: host-seccomp
              mountPath: /host-seccomp
      {{- end }}
      containers:
        - name: agent
          image: "{{ .Values.daemonset.image.repository }}:{{ .Values.daemonset.image.tag }}"
          imagePullPolicy: {{ .Values.daemonset.image.pullPolicy }}
          securityContext:
            privileged: true
          env:
            # Dynamic values from Kubernetes downward API
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: SNAPSHOT_LOG_LEVEL
              value: {{ .Values.daemonset.snapshotLogLevel | quote }}
            {{- if .Values.rbac.namespaceRestricted }}
            # Restrict pod watching to this namespace (namespace-scoped RBAC)
            - name: RESTRICTED_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            {{- end }}
          volumeMounts:
            # Mount configuration ConfigMap
            - name: config
              mountPath: /etc/snapshot
              readOnly: true
            {{- if eq .Values.storage.type "pvc" }}
            # Mount the checkpoint PVC (only for PVC storage type)
            - name: checkpoints
              mountPath: {{ .Values.storage.pvc.basePath }}
            {{- end }}
            # Mount containerd runtime directory for checkpoint operations
            - name: containerd-run
              mountPath: /run/containerd
            # Mount kubelet pods directory for volume discovery
            - name: kubelet-pods
              mountPath: /var/lib/kubelet/pods
              readOnly: true
            # Mount containerd storage for filesystem info
            - name: containerd-storage
              mountPath: /var/lib/containerd
              readOnly: true
            # Mount host proc for CRIU and runtime PID signaling
            - name: host-proc
              mountPath: /host/proc
            # Mount host cgroup for CRIU (write access needed for cgroup freezer)
            - name: host-cgroup
              mountPath: /sys/fs/cgroup
            # Kubelet PodResources API socket (for GPU UUID discovery)
            - name: kubelet-pod-resources
              mountPath: /var/lib/kubelet/pod-resources
              readOnly: true
            {{- if and (eq .Values.storage.type "oci") .Values.storage.oci.credentialsSecretRef }}
            # Mount docker config for OCI registry auth
            - name: docker-config
              mountPath: /root/.docker
              readOnly: true
            {{- end }}
          {{- if and (eq .Values.storage.type "s3") .Values.storage.s3.credentialsSecretRef }}
          envFrom:
            - secretRef:
                name: {{ .Values.storage.s3.credentialsSecretRef }}
          {{- end }}
          resources:
            {{- toYaml .Values.daemonset.resources | nindent 12 }}
      volumes:
        # Configuration ConfigMap
        - name: config
          configMap:
            name: {{ include "snapshot.fullname" . }}-config
        {{- if .Values.seccomp.deploy }}
        # Seccomp profile ConfigMap (used by initContainer)
        - name: seccomp-profiles
          configMap:
            name: {{ include "snapshot.fullname" . }}-seccomp
        # Host seccomp directory (for deploying the profile)
        - name: host-seccomp
          hostPath:
            path: /var/lib/kubelet/seccomp
            type: DirectoryOrCreate
        {{- end }}
        {{- if eq .Values.storage.type "pvc" }}
        - name: checkpoints
          persistentVolumeClaim:
            claimName: {{ .Values.storage.pvc.name }}
        {{- end }}
        # Containerd runtime directory (read-write for checkpoint operations)
        - name: containerd-run
          hostPath:
            path: /run/containerd
            type: Directory
        # Kubelet pods directory (for volume discovery)
        - name: kubelet-pods
          hostPath:
            path: /var/lib/kubelet/pods
            type: Directory
        # Containerd storage directory (for filesystem info)
        - name: containerd-storage
          hostPath:
            path: /var/lib/containerd
            type: Directory
        # Host proc (for CRIU and runtime signaling - needs write access)
        - name: host-proc
          hostPath:
            path: /proc
            type: Directory
        # Host cgroup (for CRIU)
        - name: host-cgroup
          hostPath:
            path: /sys/fs/cgroup
            type: Directory
        # Kubelet PodResources API socket directory
        - name: kubelet-pod-resources
          hostPath:
            path: /var/lib/kubelet/pod-resources
            type: Directory
        {{- if and (eq .Values.storage.type "oci") .Values.storage.oci.credentialsSecretRef }}
        - name: docker-config
          secret:
            secretName: {{ .Values.storage.oci.credentialsSecretRef }}
        {{- end }}
      {{- with .Values.daemonset.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
