# _helpers.tpl
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "envoy.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "envoy.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "envoy.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
 # configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ template "envoy.fullname" . }}
  labels:
    app: {{ template "envoy.name" . }}
    chart: {{ template "envoy.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
data:
{{- range $key, $value := .Values.files }}
  {{ $key }}: |-
{{ $value | default "" | indent 4 }}
{{- end -}}
{{- range $key, $value := .Values.templates }}
  {{ $key }}: |-
{{ $valueWithDefault := default "" $value -}}
{{ tpl $valueWithDefault $ | indent 4 }}
{{- end -}}
 # deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ template "envoy.fullname" . }}
  labels:
    app: {{ template "envoy.name" . }}
    chart: {{ template "envoy.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ template "envoy.name" . }}
      release: {{ .Release.Name }}
  strategy:
    {{ .Values.strategy | nindent 4 }}
  template:
    metadata:
      labels:
        app: {{ template "envoy.name" . }}
        release: {{ .Release.Name }}
        component: controller
        {{- if .Values.podLabels }}
        ## Custom pod labels
        {{- range $key, $value := .Values.podLabels }}
        {{ $key }}: {{ $value | quote }}
        {{- end }}
        {{- end }}
      annotations:
        checksum/config: {{ include (print .Template.BasePath "/configmap.yaml") . | sha256sum }}
        {{- if .Values.podAnnotations }}
        ## Custom pod annotations
        {{- range $key, $value := .Values.podAnnotations }}
        {{ $key }}: {{ $value | quote }}
        {{- end }}
        {{- end }}
    spec:
      securityContext:
        {{ toYaml .Values.securityContext | nindent 8 }}
      {{- if .Values.priorityClassName }}
      priorityClassName: "{{ .Values.priorityClassName }}"
      {{- end }}
      terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
      {{- if .Values.initContainersTemplate }}
      initContainers:
        {{ tpl .Values.initContainersTemplate $ | nindent 8 }}
      {{- end }}
      containers:

        - name: envoy
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          command:
            {{ toYaml .Values.command | nindent 12 }}
          args:
            {{- if $.Values.argsTemplate }}
            {{ tpl $.Values.argsTemplate $ | nindent 12}}
            {{- else }}
            {{ toYaml .Values.args | nindent 12 }}
            {{- end }}
          ports:
            {{- with .Values.ports }}
            {{- range $key, $port := . }}
            - name: {{ $key }}
              {{ toYaml $port | nindent 14 }}
            {{- end }}
            {{- end }}

          livenessProbe:
            {{ toYaml .Values.livenessProbe | nindent 12 }}
          readinessProbe:
            {{ toYaml .Values.readinessProbe | nindent 12 }}
          env:
          {{- range $key, $value := .Values.env }}
            - name: {{ $key | upper | replace "." "_" }}
              value: {{ $value | quote }}
          {{- end }}
          resources:
            {{ toYaml .Values.resources | nindent 12 }}
          volumeMounts:
            - name: config
              mountPath: /config
            {{- if .Values.volumeMounts }}
            {{ toYaml .Values.volumeMounts | nindent 12 }}
            {{- end }}
            {{- range $key, $value := .Values.secretMounts }}
            - name: {{ $key }}
              mountPath: {{ $value.mountPath }}
            {{- end }}
          lifecycle:
            {{ toYaml .Values.lifecycle | nindent 12 }}

        {{- if .Values.sidecarContainersTemplate }}
        {{ tpl .Values.sidecarContainersTemplate $ | nindent 8 }}
        {{- end }}

      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{ toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{ toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{ toYaml . | nindent 8 }}
      {{- end }}
      volumes:
        - name: config
          configMap:
            name: {{ template "envoy.fullname" . }}
        {{- if .Values.volumes }}
        {{ toYaml .Values.volumes | nindent 8 }}
        {{- end }}
        {{- range $key, $value := .Values.secretMounts }}
        - name: {{ $key }}
          secret:
            secretName: {{ $value.secretName }}
            defaultMode: {{ $value.defaultMode }}
        {{- end }}
 # poddisruptionbudget.yaml
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: {{ template "envoy.fullname" . }}
  labels:
    app: {{ template "envoy.name" . }}
    chart: {{ template "envoy.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  selector:
    matchLabels:
      app: {{ template "envoy.name" . }}
      release: {{ .Release.Name }}
{{ .Values.podDisruptionBudget | indent 2 }}
 # service.yaml
{{- if .Values.service.enabled }}
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.service.name }}
  labels:
    app: {{ template "envoy.name" . }}
    chart: {{ template "envoy.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
  annotations:
{{- with .Values.service.annotations }}
{{ toYaml . | indent 4 }}
{{- end }}
spec:
  type: {{ .Values.service.type }}
  {{- if ne .Values.service.loadBalancerIP "" }}
  loadBalancerIP: {{ .Values.service.loadBalancerIP }}
  {{- end }}
  ports:
  {{- range $key, $value := .Values.service.ports }}
    - name: {{ $key }}
{{ toYaml $value | indent 6 }}
  {{- end }}
  {{- if .Values.service.loadBalancerSourceRanges }}
  loadBalancerSourceRanges:
  {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 6 }}
  {{- end }}
  selector:
    app: {{ template "envoy.name" . }}
    release: {{ .Release.Name }}
{{- end }}
 # servicemonitor.yaml
{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ( .Values.serviceMonitor.enabled )  }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: {{ template "envoy.name" . }}
    chart: {{ template "envoy.chart" . }}
    heritage: {{ .Release.Service }}
    release: {{ .Release.Name }}
{{- if .Values.serviceMonitor.additionalLabels }}
{{ toYaml .Values.serviceMonitor.additionalLabels | indent 4}}
{{- end }}
  name: {{ template "envoy.fullname" . }}
{{- if .Values.serviceMonitor.namespace }}
  namespace: {{ .Values.serviceMonitor.namespace }}
{{- end }}
spec:
  endpoints:
  - targetPort: {{ .Values.ports.admin.containerPort }}
    interval: {{ .Values.serviceMonitor.interval }}
    path: "/stats/prometheus"
  jobLabel: {{ template "envoy.fullname" . }}
  namespaceSelector:
    matchNames:
    - {{ .Release.Namespace }}
  selector:
    matchLabels:
      app: {{ template "envoy.name" . }}
      release: {{ .Release.Name }}
  {{- with .Values.serviceMonitor.targetLabels }}
  targetLabels:
{{ toYaml . | trim | indent 4 -}}
  {{- end }}
  {{- with .Values.serviceMonitor.podTargetLabels }}
  podTargetLabels:
{{ toYaml . | trim | indent 4 -}}
  {{- end }}
{{- end }}
 # xds.configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ template "envoy.fullname" . }}-xds
  labels:
    app: {{ template "envoy.name" . }}
    chart: {{ template "envoy.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
data:
{{- range $filename, $content := .Values.xds }}
  {{ tpl $filename $ }}: |-
{{ $valueWithDefault := default "" $content -}}
{{ tpl $valueWithDefault $ | indent 4 }}
{{- end -}}

replicaCount: 2

podDisruptionBudget: |
  maxUnavailable: 1

## ref: https://pracucci.com/graceful-shutdown-of-kubernetes-pods.html
terminationGracePeriodSeconds: 30

strategy: |
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 2
    maxUnavailable: 1

image:
  repository: envoyproxy/envoy
  tag: v1.11.1
  pullPolicy: IfNotPresent

command:
  - /usr/local/bin/envoy
args:
  - -l
  - $loglevel
  - -c
  - /config/envoy.yaml

## Args template allows you to use Chart template expressions to dynamically generate args
# argsTemplate: |-
#   - -c
#   - /docker-entrypoint.sh envoy --service-node ${POD_NAME} --service-cluster {{ template "envoy.fullname" . }} -l debug -c /config/envoy.yaml

## Client service.
service:
  enabled: true
  ## Service name is user-configurable for maximum service discovery flexibility.
  name: envoy
  type: ClusterIP
  ## Ignored if the type is not LoadBalancer or if the IP is empty string
  loadBalancerIP: ""
  annotations: {}
    ## AWS example for use with LoadBalancer service type.
    # external-dns.alpha.kubernetes.io/hostname: envoy.cluster.local
    # service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    # service.beta.kubernetes.io/aws-load-balancer-internal: "true"
  ports:
    n0:
      port: 10000
      targetPort: n0
      protocol: TCP
  ## Used to whitelist certain source CIDRs
  # loadBalancerSourceRanges:
  # - 0.0.0.0/0

ports:
  admin:
    containerPort: 9901
    protocol: TCP
  n0:
    containerPort: 10000
    protocol: TCP

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

priorityClassName: ""

nodeSelector: {}

tolerations: []

affinity: {}
  # podAntiAffinity:
  #   preferredDuringSchedulingIgnoredDuringExecution:
  #     - weight: 50
  #       podAffinityTerm:
  #         topologyKey: failure-domain.beta.kubernetes.io/zone
  #         labelSelector:
  #           matchLabels:
  #             release: envoy
  #   requiredDuringSchedulingIgnoredDuringExecution:
  #     - weight: 40
  #       topologyKey: "kubernetes.io/hostname"
  #       labelSelector:
  #         matchLabels:
  #           release: envoy

## ref: https://github.com/envoyproxy/envoy/pull/2896
podAnnotations: {}
  # prometheus.io/scrape: "true"
  # prometheus.io/path: "/stats/prometheus"
  # prometheus.io/port: "9901"

podLabels: {}
  # team: "developers"
  # service: "envoy"

livenessProbe:
  tcpSocket:
    port: admin
  initialDelaySeconds: 30
  # periodSeconds: 10
  # timeoutSeconds: 5
  # failureThreshold: 3
  # successThreshold: 1

readinessProbe:
  tcpSocket:
    port: admin
  initialDelaySeconds: 30
  # periodSeconds: 10
  # timeoutSeconds: 5
  # failureThreshold: 3
  # successThreshold: 1

securityContext: {}

env: {}

## Create secrets out-of-band from Helm like this:
##
## $ kubectl create secret generic envoy --from-file=./some-secret.txt
##
secretMounts: {}
  # secret:
  #   secretName: envoy
  #   mountPath: /secret
  #   defaultMode: 256  # 256 in base10 == 0400 in octal

files:
  envoy.yaml: |-
    ## refs:
    ## - https://www.envoyproxy.io/docs/envoy/latest/start/start#quick-start-to-run-simple-example
    ## - https://raw.githubusercontent.com/envoyproxy/envoy/master/configs/google_com_proxy.v2.yaml
    admin:
      access_log_path: /dev/stdout
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 9901

    static_resources:
      listeners:
      - name: listener_0
        address:
          socket_address:
            address: 0.0.0.0
            port_value: 10000
        filter_chains:
        - filters:
          - name: envoy.http_connection_manager
            config:
              access_log:
              - name: envoy.file_access_log
                config:
                  path: /dev/stdout
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                - name: local_service
                  domains: ["*"]
                  routes:
                  - match:
                      prefix: "/"
                    route:
                      host_rewrite: www.google.com
                      cluster: service_google
              http_filters:
              - name: envoy.router
      clusters:
      - name: service_google
        connect_timeout: 0.25s
        type: LOGICAL_DNS
        dns_lookup_family: V4_ONLY
        lb_policy: ROUND_ROBIN
        hosts:
          - socket_address:
              address: google.com
              port_value: 443
        tls_context:
          sni: www.google.com

## Uncomment this section to use helm values to dynamically generate enovy.yaml
# templates:
#   envoy.yaml: |-
#     ## refs:
#     ## - https://www.envoyproxy.io/docs/envoy/latest/start/start#quick-start-to-run-simple-example
#     ## - https://raw.githubusercontent.com/envoyproxy/envoy/master/configs/google_com_proxy.v2.yaml
#     admin:
#       access_log_path: /dev/stdout
#       address:
#         socket_address:
#           address: 0.0.0.0
#           port_value: {{ .Values.ports.admin.containerPort }}

#     static_resources:
#       listeners:
#       - name: listener_0
#         address:
#           socket_address:
#             address: 0.0.0.0
#             port_value: {{ .Values.ports.n0.containerPort }}
#         filter_chains:
#         - filters:
#           - name: envoy.http_connection_manager
#             config:
#               access_log:
#               - name: envoy.file_access_log
#                 config:
#                   path: /dev/stdout
#               stat_prefix: ingress_http
#               route_config:
#                 name: local_route
#                 virtual_hosts:
#                 - name: local_service
#                   domains: ["*"]
#                   routes:
#                   - match:
#                       prefix: "/"
#                     route:
#                       host_rewrite: www.google.com
#                       cluster: service_google
#               http_filters:
#               - name: envoy.router
#       clusters:
#       - name: service_google
#         connect_timeout: 0.25s
#         type: LOGICAL_DNS
#         dns_lookup_family: V4_ONLY
#         lb_policy: ROUND_ROBIN
#         hosts:
#           - socket_address:
#               address: google.com
#               port_value: 443
#         tls_context:
#           sni: www.google.com

## Additional volumes to be added to Envoy pods
# volumes:
# - name: xds
#   emptyDir: {}

## Additional volume mounts to be added to Envoy containers(Primary containers of Envoy pods)
# volumeMounts:
# - name: xds
#   mountPath: /srv/runtime

## Init containers
# initContainersTemplate: |-
#   - name: xds-init
#     image: mumoshu/envoy-xds-configmap-loader:canary-6090275
#     command:
#     - envoy-xds-configmap-loader
#     args:
#     - --configmap={{ template "envoy.fullname" . }}-xds
#     - --onetime
#     - --insecure
#     env:
#     - name: POD_NAMESPACE
#       valueFrom:
#         fieldRef:
#           fieldPath: metadata.namespace
#     volumeMounts:
#     - name: xds
#       mountPath: /srv/runtime

## Sidecar containers
# sidecarContainersTemplate: |-
#   - name: xds-update
#     image: mumoshu/envoy-xds-configmap-loader:canary-6090275
#     command:
#     - envoy-xds-configmap-loader
#     args:
#     - --configmap={{ template "envoy.fullname" . }}-xds
#     - --sync-interval=5s
#     - --insecure
#     env:
#     - name: POD_NAMESPACE
#       valueFrom:
#         fieldRef:
#           fieldPath: metadata.namespace
#     volumeMounts:
#     - name: xds
#       mountPath: /srv/runtime

## ServiceMonitor consumed by prometheus-operator
serviceMonitor:
  ## If the operator is installed in your cluster, set to true to create a Service Monitor Entry
  enabled: false
  interval: "15s"
  targetLabels: []
  podTargetLabels: []
  ## Namespace in which the service monitor is created
  # namespace: monitoring
  # Added to the ServiceMonitor object so that prometheus-operator is able to discover it
  ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
  additionalLabels: {}

### Lifecycle Events
lifecycle: {}
#  preStop:
#    exec:
#      command:
#      - sh
#      - -c
#      - "sleep 60"

## PrometheusRule consumed by prometheus-operator
prometheusRule:
  enabled: false
  ## Namespace in which the prometheus rule is created
  # namespace: monitoring
  ## Define individual alerting rules as required
  ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#rulegroup
  ##      https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/
  groups:
    upstream-rules:
      enabled: true
      rules:
        high4xxRate:
          enabled: true
          alert: High4xxRate
          expr: sum(rate(envoy_cluster_upstream_rq_xx{response_code_class="4"}[1m])) / sum(rate(envoy_cluster_upstream_rq_xx[1m])) * 100 > 1
          for: 1m
          labels:
            severity: page
          annotations:
            summary: "4xx response rate above 1%"
            description: "The 4xx error response rate for envoy cluster {{ $labels.envoy_cluster_name }} reported a service replication success rate of {{ $value }}% for more than 1 minute."
  ## Added to the PrometheusRule object so that prometheus-operator is able to discover it
  ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
  additionalLabels: {}
