---
###
### Destination Controller Service
###
kind: Service
apiVersion: v1
metadata:
  name: linkerd-dst
  namespace: {{ .Release.Namespace }}
  labels:
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  annotations:
    {{ include "partials.annotations.created-by" . }}
spec:
  type: ClusterIP
  selector:
    linkerd.io/control-plane-component: destination
  ports:
  - name: grpc
    port: 8086
    targetPort: 8086
---
kind: Service
apiVersion: v1
metadata:
  name: linkerd-dst-headless
  namespace: {{ .Release.Namespace }}
  labels:
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  annotations:
    {{ include "partials.annotations.created-by" . }}
spec:
  clusterIP: None
  selector:
    linkerd.io/control-plane-component: destination
  ports:
  - name: grpc
    port: 8086
    targetPort: 8086
---
kind: Service
apiVersion: v1
metadata:
  name: linkerd-sp-validator
  namespace: {{ .Release.Namespace }}
  labels:
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  annotations:
    {{ include "partials.annotations.created-by" . }}
spec:
  type: ClusterIP
  selector:
    linkerd.io/control-plane-component: destination
  ports:
  - name: sp-validator
    port: 443
    targetPort: sp-validator
---
kind: Service
apiVersion: v1
metadata:
  name: linkerd-policy
  namespace: {{ .Release.Namespace }}
  labels:
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  annotations:
    {{ include "partials.annotations.created-by" . }}
spec:
  clusterIP: None
  selector:
    linkerd.io/control-plane-component: destination
  ports:
  - name: grpc
    port: 8090
    targetPort: 8090
---
kind: Service
apiVersion: v1
metadata:
  name: linkerd-policy-validator
  namespace: {{ .Release.Namespace }}
  labels:
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  annotations:
    {{ include "partials.annotations.created-by" . }}
spec:
  type: ClusterIP
  selector:
    linkerd.io/control-plane-component: destination
  ports:
  - name: policy-https
    port: 443
    targetPort: policy-https
{{- if .Values.enablePodDisruptionBudget }}
---
kind: PodDisruptionBudget
apiVersion: policy/v1
metadata:
  name: linkerd-dst
  namespace: {{ .Release.Namespace }}
  labels:
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  annotations:
    {{ include "partials.annotations.created-by" . }}
spec:
  maxUnavailable: {{ .Values.controller.podDisruptionBudget.maxUnavailable }}
  selector:
    matchLabels:
      linkerd.io/control-plane-component: destination
{{- end }}
---
{{- $tree := deepCopy . }}
{{ $_ := set $tree.Values.proxy "workloadKind" "deployment" -}}
{{ $_ := set $tree.Values.proxy "component" "linkerd-destination" -}}
{{ $_ := set $tree.Values.proxy "waitBeforeExitSeconds" 0 -}}
{{- if not (empty .Values.destinationProxyResources) }}
{{- $c := dig "cores" .Values.proxy.cores .Values.destinationProxyResources }}
{{- $_ := set $tree.Values.proxy "cores" $c }}
{{- $r := merge .Values.destinationProxyResources .Values.proxy.resources }}
{{- $_ := set $tree.Values.proxy "resources" $r }}
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    {{ include "partials.annotations.created-by" . }}
  labels:
    app.kubernetes.io/name: destination
    app.kubernetes.io/part-of: Linkerd
    app.kubernetes.io/version: {{.Values.linkerdVersion}}
    linkerd.io/control-plane-component: destination
    linkerd.io/control-plane-ns: {{.Release.Namespace}}
    {{- with .Values.commonLabels }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  name: linkerd-destination
  namespace: {{ .Release.Namespace }}
spec:
  replicas: {{.Values.controllerReplicas}}
  revisionHistoryLimit: {{.Values.revisionHistoryLimit}}
  selector:
    matchLabels:
      linkerd.io/control-plane-component: destination
      linkerd.io/control-plane-ns: {{.Release.Namespace}}
      {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 6}}
  {{- if .Values.deploymentStrategy }}
  strategy:
    {{- with .Values.deploymentStrategy }}{{ toYaml . | trim | nindent 4 }}{{- end }}
  {{- end }}
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/destination-rbac.yaml") . | sha256sum }}
        {{ include "partials.annotations.created-by" . }}
        {{- include "partials.proxy.annotations" . | nindent 8}}
        {{- with (mergeOverwrite (deepCopy .Values.podAnnotations) .Values.destinationController.podAnnotations) }}{{ toYaml . | trim | nindent 8 }}{{- end }}
        config.linkerd.io/default-inbound-policy: "all-unauthenticated"
      labels:
        linkerd.io/control-plane-component: destination
        linkerd.io/control-plane-ns: {{.Release.Namespace}}
        linkerd.io/workload-ns: {{.Release.Namespace}}
        {{- include "partials.proxy.labels" $tree.Values.proxy | nindent 8}}
        {{- with .Values.podLabels }}{{ toYaml . | trim | nindent 8 }}{{- end }}
    spec:
      {{- with .Values.runtimeClassName }}
      runtimeClassName: {{ . | quote }}
      {{- end }}
      {{- if .Values.tolerations -}}
      {{- include "linkerd.tolerations" . | nindent 6 }}
      {{- end -}}
      {{- include "linkerd.node-selector" . | nindent 6 }}
      {{- $_ := set $tree "component" "destination" -}}
      {{- with include "linkerd.affinity" $tree }}
      {{- . | nindent 6 }}
      {{- end }}
      automountServiceAccountToken: false
      containers:
      {{- $_ := set $tree.Values.proxy "await" $tree.Values.proxy.await }}
      {{- $_ := set $tree.Values.proxy "loadTrustBundleFromConfigMap" true }}
      {{- $_ := set $tree.Values.proxy "podInboundPorts" "8086,8090,8443,9443,9990,9996,9997" }}
      {{- $_ := set $tree.Values.proxy "outboundDiscoveryCacheUnusedTimeout" "5s" }}
      {{- $_ := set $tree.Values.proxy "inboundDiscoveryCacheUnusedTimeout" "90s" }}
      {{- /*
        The pod needs to accept webhook traffic, and we can't rely on that originating in the
        cluster network.
      */}}
      {{- $_ := set $tree.Values.proxy "defaultInboundPolicy" "all-unauthenticated" }}
      {{- $_ := set $tree.Values.proxy "capabilities" (dict "drop" (list "ALL")) }}
      {{- if not $tree.Values.proxy.nativeSidecar }}
      - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }}
      {{- end }}
      - args:
        - destination
        - -addr=:8086
        - -controller-namespace={{.Release.Namespace}}
        - -outbound-transport-mode={{.Values.proxy.outboundTransportMode}}
        - -enable-h2-upgrade={{.Values.enableH2Upgrade}}
        - -log-level={{.Values.controllerLogLevel}}
        - -log-format={{.Values.controllerLogFormat}}
        - -enable-endpoint-slices={{.Values.enableEndpointSlices}}
        - -cluster-domain={{.Values.clusterDomain}}
        - -identity-trust-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}}
        - -default-opaque-ports={{.Values.proxy.opaquePorts}}
        - -enable-ipv6={{not .Values.disableIPv6}}
        - -enable-pprof={{.Values.enablePprof | default false}}
        {{- if (.Values.destinationController).meshedHttp2ClientProtobuf }}
        - --meshed-http2-client-params={{ toJson .Values.destinationController.meshedHttp2ClientProtobuf }}
        {{- end }}
        {{- range (.Values.destinationController).additionalArgs }}
        - {{ . }}
        {{- end }}
        {{- range (.Values.destinationController).experimentalArgs }}
        - {{ . }}
        {{- end }}
        {{- if or (.Values.destinationController).additionalEnv (.Values.destinationController).experimentalEnv }}
        env:
        {{- with (.Values.destinationController).additionalEnv }}
        {{- toYaml . | nindent 8 -}}
        {{- end }}
        {{- with (.Values.destinationController).experimentalEnv }}
        {{- toYaml . | nindent 8 -}}
        {{- end }}
        {{- end }}
        {{- include "partials.linkerd.trace" . | nindent 8 -}}
        image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}}
        imagePullPolicy: {{.Values.imagePullPolicy}}
        livenessProbe:
          httpGet:
            path: /ping
            port: 9996
          initialDelaySeconds: 10
          {{- with (.Values.destinationController.livenessProbe).timeoutSeconds }}
          timeoutSeconds: {{ . }}
          {{- end }}
        name: destination
        ports:
        - containerPort: 8086
          name: dest-grpc
        - containerPort: 9996
          name: dest-admin
        readinessProbe:
          failureThreshold: 7
          httpGet:
            path: /ready
            port: 9996
          {{- with (.Values.destinationController.readinessProbe).timeoutSeconds }}
          timeoutSeconds: {{ . }}
          {{- end }}
        {{- if .Values.destinationResources -}}
        {{- include "partials.resources" .Values.destinationResources | nindent 8 }}
        {{- end }}
        securityContext:
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: {{.Values.controllerUID}}
          {{- if ge (int .Values.controllerGID) 0 }}
          runAsGroup: {{.Values.controllerGID}}
          {{- end }}
          allowPrivilegeEscalation: false
          seccompProfile:
            type: RuntimeDefault
        volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access
          readOnly: true
      - args:
        - sp-validator
        - -log-level={{.Values.controllerLogLevel}}
        - -log-format={{.Values.controllerLogFormat}}
        - -enable-pprof={{.Values.enablePprof | default false}}
        {{- if or (.Values.spValidator).additionalEnv (.Values.spValidator).experimentalEnv }}
        env:
        {{- with (.Values.spValidator).additionalEnv }}
        {{- toYaml . | nindent 8 -}}
        {{- end }}
        {{- with (.Values.spValidator).experimentalEnv }}
        {{- toYaml . | nindent 8 -}}
        {{- end }}
        {{- end }}
        image: {{.Values.controllerImage}}:{{.Values.controllerImageVersion | default .Values.linkerdVersion}}
        imagePullPolicy: {{.Values.imagePullPolicy}}
        livenessProbe:
          httpGet:
            path: /ping
            port: 9997
          initialDelaySeconds: 10
          {{- with ((.Values.spValidator).livenessProbe).timeoutSeconds }}
          timeoutSeconds: {{ . }}
          {{- end }}
        name: sp-validator
        ports:
        - containerPort: 8443
          name: sp-validator
        - containerPort: 9997
          name: spval-admin
        readinessProbe:
          failureThreshold: 7
          httpGet:
            path: /ready
            port: 9997
          {{- with ((.Values.spValidator).readinessProbe).timeoutSeconds }}
          timeoutSeconds: {{ . }}
          {{- end }}
        {{- if .Values.spValidatorResources -}}
        {{- include "partials.resources" .Values.spValidatorResources | nindent 8 }}
        {{- end }}
        securityContext:
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: {{.Values.controllerUID}}
          {{- if ge (int .Values.controllerGID) 0 }}
          runAsGroup: {{.Values.controllerGID}}
          {{- end }}
          allowPrivilegeEscalation: false
          seccompProfile:
            type: RuntimeDefault
        volumeMounts:
        - mountPath: /var/run/linkerd/tls
          name: sp-tls
          readOnly: true
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access
          readOnly: true
      - args:
        - --admin-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:9990
        - --control-plane-namespace={{.Release.Namespace}}
        - --grpc-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:8090
        - --server-addr={{ if .Values.disableIPv6 }}0.0.0.0{{ else }}[::]{{ end }}:9443
        - --server-tls-key=/var/run/linkerd/tls/tls.key
        - --server-tls-certs=/var/run/linkerd/tls/tls.crt
        - --cluster-networks={{.Values.clusterNetworks}}
        - --identity-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}}
        - --cluster-domain={{.Values.clusterDomain}}
        - --default-policy={{.Values.proxy.defaultInboundPolicy}}
        - --log-level={{.Values.policyController.logLevel | default "linkerd=info,warn"}}
        - --log-format={{.Values.controllerLogFormat}}
        - --default-opaque-ports={{.Values.proxy.opaquePorts}}
        - --global-egress-network-namespace={{.Values.egress.globalEgressNetworkNamespace}}
        {{- if .Values.policyController.probeNetworks }}
        - --probe-networks={{.Values.policyController.probeNetworks | join ","}}
        {{- end}}
        {{- range .Values.policyController.additionalArgs }}
        - {{ . }}
        {{- end }}
        {{- range .Values.policyController.experimentalArgs }}
        - {{ . }}
        {{- end }}
        image: {{.Values.policyController.image.name}}:{{.Values.policyController.image.version | default .Values.linkerdVersion}}
        imagePullPolicy: {{.Values.policyController.image.pullPolicy | default .Values.imagePullPolicy}}
        livenessProbe:
          httpGet:
            path: /live
            port: policy-admin
          initialDelaySeconds: 10
          {{- with (.Values.policyController.livenessProbe).timeoutSeconds }}
          timeoutSeconds: {{ . }}
          {{- end }}
        name: policy
        ports:
        - containerPort: 8090
          name: policy-grpc
        - containerPort: 9990
          name: policy-admin
        - containerPort: 9443
          name: policy-https
        readinessProbe:
          failureThreshold: 7
          httpGet:
            path: /ready
            port: policy-admin
          {{- with (.Values.policyController.readinessProbe).timeoutSeconds }}
          timeoutSeconds: {{ . }}
          {{- end }}
        {{- if .Values.policyController.resources }}
        {{- include "partials.resources" .Values.policyController.resources | nindent 8 }}
        {{- end }}
        securityContext:
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: {{.Values.controllerUID}}
          {{- if ge (int .Values.controllerGID) 0 }}
          runAsGroup: {{.Values.controllerGID}}
          {{- end }}
          allowPrivilegeEscalation: false
          seccompProfile:
            type: RuntimeDefault
        volumeMounts:
        - mountPath: /var/run/linkerd/tls
          name: policy-tls
          readOnly: true
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access
          readOnly: true
      initContainers:
      {{ if .Values.cniEnabled -}}
      - {{- include "partials.network-validator" $tree | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ else -}}
      {{- /*
        The destination controller needs to connect to the Kubernetes API before the proxy is able
        to proxy requests, so we always skip these connections.
      */}}
      {{- $_ := set $tree.Values.proxyInit "ignoreOutboundPorts" .Values.proxyInit.kubeAPIServerPorts -}}
      - {{- include "partials.proxy-init" $tree | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ end -}}
      {{- if $tree.Values.proxy.nativeSidecar }}
        {{- $_ := set $tree.Values.proxy "startupProbeInitialDelaySeconds" 35 }}
        {{- $_ := set $tree.Values.proxy "startupProbePeriodSeconds" 5 }}
        {{- $_ := set $tree.Values.proxy "startupProbeFailureThreshold" 20 }}
      - {{- include "partials.proxy" $tree | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ end -}}
      {{- if .Values.priorityClassName -}}
      priorityClassName: {{ .Values.priorityClassName }}
      {{ end -}}
      securityContext:
        seccompProfile:
          type: RuntimeDefault
      serviceAccountName: linkerd-destination
      volumes:
      - name: sp-tls
        secret:
          secretName: linkerd-sp-validator-k8s-tls
      - name: policy-tls
        secret:
          secretName: linkerd-policy-validator-k8s-tls
      - {{- include "partials.volumes.manual-mount-service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ if not .Values.cniEnabled -}}
      - {{- include "partials.proxyInit.volumes.xtables" . | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ end -}}
      {{if .Values.identity.serviceAccountTokenProjection -}}
      - {{- include "partials.proxy.volumes.service-account-token" . | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ end -}}
      - {{- include "partials.proxy.volumes.identity" . | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ if .Values.proxy.tracing.enable -}}
      - {{- include "partials.proxy.volumes.podinfo" . | indent 8 | trimPrefix (repeat 7 " ") }}
      {{ end }}
