# services/jenkins/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  namespace: jenkins
  labels:
    app: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: jenkins
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/agent-requests-cpu: "25m"
        vault.hashicorp.com/agent-requests-mem: "64Mi"
        vault.hashicorp.com/agent-limits-cpu: "250m"
        vault.hashicorp.com/agent-limits-mem: "128Mi"
        vault.hashicorp.com/role: "jenkins"
        vault.hashicorp.com/agent-inject-secret-jenkins-env: "kv/data/atlas/jenkins/jenkins-oidc"
        vault.hashicorp.com/agent-inject-template-jenkins-env: |
          {{ with secret "kv/data/atlas/jenkins/jenkins-oidc" }}
          OIDC_CLIENT_ID={{ .Data.data.clientId }}
          OIDC_CLIENT_SECRET={{ .Data.data.clientSecret }}
          OIDC_AUTH_URL={{ .Data.data.authorizationUrl }}
          OIDC_TOKEN_URL={{ .Data.data.tokenUrl }}
          OIDC_USERINFO_URL={{ .Data.data.userInfoUrl }}
          OIDC_LOGOUT_URL={{ .Data.data.logoutUrl }}
          {{ end }}
          {{ with secret "kv/data/atlas/jenkins/harbor-robot-creds" }}
          HARBOR_ROBOT_USERNAME={{ .Data.data.username }}
          HARBOR_ROBOT_PASSWORD={{ .Data.data.password }}
          HARBOR_STREAMING_ROBOT_USERNAME={{ .Data.data.username }}
          HARBOR_STREAMING_ROBOT_PASSWORD={{ .Data.data.password }}
          {{ end }}
          {{ with secret "kv/data/atlas/jenkins/harbor-streaming-robot-creds" }}
          HARBOR_STREAMING_ROBOT_USERNAME={{ .Data.data.username }}
          HARBOR_STREAMING_ROBOT_PASSWORD={{ .Data.data.password }}
          {{ end }}
          {{ with secret "kv/data/atlas/shared/harbor-pull" }}
          {{- if and .Data.data.username .Data.data.password }}
          HARBOR_PULL_USERNAME={{ .Data.data.username }}
          HARBOR_PULL_PASSWORD={{ .Data.data.password }}
          {{- end }}
          {{ end }}
          {{ with secret "kv/data/atlas/jenkins/gitea-pat" }}
          GITEA_PAT_USERNAME={{ .Data.data.username }}
          GITEA_PAT_TOKEN={{ .Data.data.token }}
          {{ end }}
          {{ with secret "kv/data/atlas/quality/sonarqube-oidc" }}
          SONARQUBE_TOKEN={{ .Data.data.sonarqube_exporter_token }}
          {{ end }}
          {{ with secret "kv/data/atlas/jenkins/webhook-tokens" }}
          TITAN_IAC_WEBHOOK_TOKEN={{ .Data.data.titan_iac_quality_gate }}
          GIT_NOTIFY_TOKEN_BSTEIN_DEV_HOME={{ .Data.data.git_notify_bstein_dev_home }}
          {{ end }}
          {{ with secret "kv/data/atlas/jenkins/ariadne-api" }}
          ARIADNE_JENKINS_API_USER={{ .Data.data.username }}
          ARIADNE_JENKINS_API_TOKEN={{ .Data.data.token }}
          {{ end }}
        bstein.dev/restarted-at: "2026-05-20T09:40:31Z"
    spec:
      serviceAccountName: jenkins
      nodeSelector:
        kubernetes.io/arch: arm64
        node-role.kubernetes.io/worker: "true"
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: kubernetes.io/hostname
                    operator: NotIn
                    values:
                      - titan-13
                      - titan-15
                      - titan-17
                      - titan-19
                  - key: hardware
                    operator: In
                    values: ["rpi5"]
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: atlas.bstein.dev/spillover
                    operator: DoesNotExist
            - weight: 95
              preference:
                matchExpressions:
                  - key: kubernetes.io/hostname
                    operator: NotIn
                    values:
                      - titan-13
                      - titan-15
                      - titan-17
                      - titan-19
            - weight: 90
              preference:
                matchExpressions:
                  - key: hardware
                    operator: In
                    values: ["rpi5"]
            - weight: 50
              preference:
                matchExpressions:
                  - key: hardware
                    operator: In
                    values: ["rpi4"]
      hostAliases:
        - ip: 38.28.125.112
          hostnames:
            - sso.bstein.dev
      securityContext:
        fsGroup: 1000
        fsGroupChangePolicy: OnRootMismatch
      initContainers:
        - name: install-plugins
          image: jenkins/jenkins:2.528.3-jdk21
          imagePullPolicy: IfNotPresent
          command:
            - sh
            - -c
            - |
              set -euo pipefail
              rm -rf /usr/share/jenkins/ref/plugins/*
              jenkins-plugin-cli --plugin-file /plugins/plugins.txt
          volumeMounts:
            - name: plugins
              mountPath: /plugins/plugins.txt
              subPath: plugins.txt
            - name: plugin-dir
              mountPath: /usr/share/jenkins/ref/plugins
      containers:
        - name: jenkins
          image: jenkins/jenkins:2.528.3-jdk21
          imagePullPolicy: IfNotPresent
          command:
            - /bin/sh
            - -c
            - |
              set -e
              exec env $(cat /vault/secrets/jenkins-env) /usr/bin/tini -- /usr/local/bin/jenkins.sh
          ports:
            - name: http
              containerPort: 8080
            - name: agent-listener
              containerPort: 50000
          env:
            - name: JAVA_OPTS
              value: "-Xms512m -Xmx2048m -Duser.timezone=America/Chicago -Dkubernetes.websocket.timeout=60000 -Dorg.csanchez.jenkins.plugins.kubernetes.pipeline.ContainerExecDecorator.websocketConnectionTimeout=120"
            - name: TZ
              value: "America/Chicago"
            - name: JENKINS_OPTS
              value: "--webroot=/var/jenkins_cache/war"
            - name: JENKINS_SLAVE_AGENT_PORT
              value: "50000"
            - name: CASC_JENKINS_CONFIG
              value: /config/jcasc
            - name: ENABLE_OIDC
              value: "true"
            - name: OIDC_ISSUER
              value: "https://sso.bstein.dev/realms/atlas"
          resources:
            requests:
              cpu: 100m
              memory: 1024Mi
            limits:
              cpu: 1500m
              memory: 3Gi
          livenessProbe:
            httpGet:
              path: /login
              port: http
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 5
          readinessProbe:
            httpGet:
              path: /login
              port: http
            initialDelaySeconds: 20
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          startupProbe:
            httpGet:
              path: /login
              port: http
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 60
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
            - name: jenkins-cache
              mountPath: /var/jenkins_cache
            - name: jcasc
              mountPath: /config/jcasc
            - name: init-scripts
              mountPath: /usr/share/jenkins/ref/init.groovy.d
            - name: init-scripts
              mountPath: /var/jenkins_home/init.groovy.d
            - name: plugin-dir
              mountPath: /usr/share/jenkins/ref/plugins
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: jenkins-home
          persistentVolumeClaim:
            claimName: jenkins
        - name: jenkins-cache
          persistentVolumeClaim:
            claimName: jenkins-cache-v2
        - name: plugin-dir
          persistentVolumeClaim:
            claimName: jenkins-plugins-v2
        - name: plugins
          configMap:
            name: jenkins-plugins
        - name: jcasc
          configMap:
            name: jenkins-jcasc
        - name: init-scripts
          configMap:
            name: jenkins-init-scripts
        - name: tmp
          emptyDir:
            medium: Memory
