kind: DaemonSet
apiVersion: apps/v1
metadata:
  # Name is set at runtime
  namespace: openshift-cluster-dns
  labels:
    openshift-app: dns
spec:
  selector:
    matchLabels:
      openshift-app: dns
  template:
    metadata:
      labels:
        openshift-app: dns
    spec:
      serviceAccountName: dns
      containers:
      - name: dns
        # image is set at runtime
        imagePullPolicy: IfNotPresent
        command: [ "coredns" ]
        args: [ "-conf", "/etc/coredns/Corefile" ]
        volumeMounts:
        - name: config-volume
          mountPath: /etc/coredns
          readOnly: true
        ports:
        - containerPort: 5353
          name: dns
          protocol: UDP
        - containerPort: 5353
          name: dns-tcp
          protocol: TCP
        - containerPort: 9153
          name: metrics
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
      - name: dns-node-resolver
        # image is set at runtime
        imagePullPolicy: IfNotPresent
        securityContext:
          privileged: true
        volumeMounts:
        - name: hosts-file
          mountPath: /etc/hosts
        # env NAMESERVER and CLUSTER_DOMAIN are set at runtime
        env:
        - name: SERVICES
          # comma or space separated list of services
          value: "docker-registry.default.svc"
        command:
        - /bin/bash
        - -c
        - |
          #!/bin/bash
          set -uo pipefail

          OPENSHIFT_MARKER="openshift-generated-node-resolver"
          HOSTS_FILE="/etc/hosts"
          TEMP_FILE="/etc/hosts.tmp"

          IFS=', ' read -r -a services <<< "${SERVICES:-"docker-registry.default.svc"}"

          # Make a temporary file with the old hosts file's attributes.
          cp -f --attributes-only "${HOSTS_FILE}" "${TEMP_FILE}"

          while true; do
            declare -A svc_ips
            for svc in "${services[@]}"; do
              # Fetch service IP from cluster dns if present
              ips=($(dig @"${NAMESERVER}" +short "${svc}.${CLUSTER_DOMAIN}"))

              if [[ "$?" -eq 0 && "${#ips[@]}" -ne 0 ]]; then
                svc_ips["${svc}"]="${ips[@]}"
              fi
            done

            # Update /etc/hosts only if we get valid service IPs
            # We will not update /etc/hosts when there is coredns service outage or api unavailability
            # Stale entries could exist in /etc/hosts if the service is deleted
            if [[ "${#svc_ips[@]}" -ne 0 ]]; then
              # Build a new hosts file from /etc/hosts with our custom entries filtered out
              grep -v "# ${OPENSHIFT_MARKER}" "${HOSTS_FILE}" > "${TEMP_FILE}"

              # Append resolver entries for services
              for svc in "${!svc_ips[@]}"; do
                for ip in ${svc_ips[${svc}]}; do
                  echo "${ip} ${svc} # ${OPENSHIFT_MARKER}" >> "${TEMP_FILE}"
                done
              done

              # TODO: Update /etc/hosts atomically to avoid any inconsistent behavior
              # Replace /etc/hosts with our modified version if needed
              cmp "${TEMP_FILE}" "${HOSTS_FILE}" || cp -f "${TEMP_FILE}" "${HOSTS_FILE}"
              # TEMP_FILE is not removed to avoid file create/delete and attributes copy churn
            fi
            sleep 60
            unset svc_ips
          done
      dnsPolicy: Default
      volumes:
       - name: config-volume
         configMap:
         # Name is set at runtime
           items:
           - key: Corefile
             path: Corefile
       - name: hosts-file
         hostPath:
           path: /etc/hosts
           type: File
      tolerations:
      # tolerate all taints so that DNS is always present on all nodes
      - operator: Exists
