{{- if .Values.speaker.frr.enabled }}
{{- if or .Values.frrk8s.enabled .Values.frrk8s.external }}
{{- fail "speaker.frr.enabled and frrk8s.enabled / external are mutually exclusive!" }}
{{- end }}
{{- end }}

{{- if and .Values.frrk8s.enabled .Values.frrk8s.external }}
{{- fail "frrk8s.enabled frrk8s.external are mutually exclusive!" }}
{{- end }}

{{- if and (not .Values.speaker.memberlist.enabled) .Values.speaker.nodeSelector }}
{{- fail "nodeSelector must be empty when memberlist is disabled" }}
{{- end }}

{{- if .Values.speaker.frr.enabled }}

# FRR expects to have these files owned by frr:frr on startup.
# Having them in a ConfigMap allows us to modify behaviors: for example enabling more daemons on startup.
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ template "metallb.fullname" . }}-frr-startup
  namespace: {{ .Release.Namespace | quote }}
  labels:
    {{- include "metallb.labels" . | nindent 4 }}
    app.kubernetes.io/component: speaker
data:
  daemons: |
    # This file tells the frr package which daemons to start.
    #
    # Sample configurations for these daemons can be found in
    # /usr/share/doc/frr/examples/.
    #
    # ATTENTION:
    #
    # When activating a daemon for the first time, a config file, even if it is
    # empty, has to be present *and* be owned by the user and group "frr", else
    # the daemon will not be started by /etc/init.d/frr. The permissions should
    # be u=rw,g=r,o=.
    # When using "vtysh" such a config file is also needed. It should be owned by
    # group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
    #
    # The watchfrr and zebra daemons are always started.
    #
    bgpd=yes
    ospfd=no
    ospf6d=no
    ripd=no
    ripngd=no
    isisd=no
    pimd=no
    ldpd=no
    nhrpd=no
    eigrpd=no
    babeld=no
    sharpd=no
    pbrd=no
    bfdd=yes
    fabricd=no
    vrrpd=no

    #
    # If this option is set the /etc/init.d/frr script automatically loads
    # the config via "vtysh -b" when the servers are started.
    # Check /etc/pam.d/frr if you intend to use "vtysh"!
    #
    vtysh_enable=yes
    zebra_options="  -A 127.0.0.1 -s 90000000 --limit-fds 100000"
    bgpd_options="   -A 127.0.0.1 -p 0 --limit-fds 100000"
    ospfd_options="  -A 127.0.0.1"
    ospf6d_options=" -A ::1"
    ripd_options="   -A 127.0.0.1"
    ripngd_options=" -A ::1"
    isisd_options="  -A 127.0.0.1"
    pimd_options="   -A 127.0.0.1"
    ldpd_options="   -A 127.0.0.1"
    nhrpd_options="  -A 127.0.0.1"
    eigrpd_options=" -A 127.0.0.1"
    babeld_options=" -A 127.0.0.1"
    sharpd_options=" -A 127.0.0.1"
    pbrd_options="   -A 127.0.0.1"
    staticd_options="-A 127.0.0.1 --limit-fds 100000"
    bfdd_options="   -A 127.0.0.1 --limit-fds 100000"
    fabricd_options="-A 127.0.0.1"
    vrrpd_options="  -A 127.0.0.1"

    # configuration profile
    #
    #frr_profile="traditional"
    #frr_profile="datacenter"

    #
    # This is the maximum number of FD's that will be available.
    # Upon startup this is read by the control files and ulimit
    # is called. Uncomment and use a reasonable value for your
    # setup if you are expecting a large number of peers in
    # say BGP.
    #MAX_FDS=1024

    # The list of daemons to watch is automatically generated by the init script.
    #watchfrr_options=""

    # for debugging purposes, you can specify a "wrap" command to start instead
    # of starting the daemon directly, e.g. to use valgrind on ospfd:
    #   ospfd_wrap="/usr/bin/valgrind"
    # or you can use "all_wrap" for all daemons, e.g. to use perf record:
    #   all_wrap="/usr/bin/perf record --call-graph -"
    # the normal daemon command is added to this at the end.
  vtysh.conf: |+
    service integrated-vtysh-config
  frr.conf: |+
    ! This file gets overriden the first time the speaker renders a config.
    ! So anything configured here is only temporary.
    frr version 8.0
    frr defaults traditional
    hostname Router
    line vty
    log file /etc/frr/frr.log informational
{{- end }}
---
{{- if .Values.speaker.enabled }}
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: {{ template "metallb.fullname" . }}-speaker
  namespace: {{ .Release.Namespace | quote }}
  labels:
    {{- include "metallb.labels" . | nindent 4 }}
    app.kubernetes.io/component: speaker
    {{- range $key, $value := .Values.speaker.labels }}
    {{ $key }}: {{ $value | quote }}
    {{- end }}
spec:
  {{- if .Values.speaker.updateStrategy }}
  updateStrategy: {{- toYaml .Values.speaker.updateStrategy | nindent 4 }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "metallb.selectorLabels" . | nindent 6 }}
      app.kubernetes.io/component: speaker
  template:
    metadata:
      {{- if or .Values.prometheus.scrapeAnnotations .Values.speaker.podAnnotations }}
      annotations:
        {{- if .Values.prometheus.scrapeAnnotations }}
        prometheus.io/scrape: "true"
        {{- if not .Values.speaker.frr.enabled }}
        prometheus.io/port: "{{ .Values.prometheus.metricsPort }}"
        {{- end }}
        {{- end }}
        {{- with .Values.speaker.podAnnotations }}
          {{- toYaml . | nindent 8 }}
        {{- end }}
      {{- end }}
      labels:
        {{- include "metallb.selectorLabels" . | nindent 8 }}
        app.kubernetes.io/component: speaker
        {{- range $key, $value := .Values.speaker.labels }}
        {{ $key }}: {{ $value | quote }}
        {{- end }}
    spec:
      {{- if .Values.speaker.runtimeClassName }}
      runtimeClassName: {{ .Values.speaker.runtimeClassName }}
      {{- end }}
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ template "metallb.speaker.serviceAccountName" . }}
      terminationGracePeriodSeconds: 0
      hostNetwork: true
      {{- if .Values.speaker.securityContext }}
      securityContext:
        {{- toYaml .Values.speaker.securityContext | nindent 8 }}
      {{- end }}
      volumes:
      {{- if .Values.prometheus.speakerMetricsTLSSecret }}
        - name: metrics-certs
          secret:
            secretName: {{ .Values.prometheus.speakerMetricsTLSSecret }}
      {{- end }}
      {{- if .Values.speaker.memberlist.enabled }}
        - name: memberlist
          secret:
            secretName: {{ include "metallb.secretName" . }}
            defaultMode: 420
      {{- end }}
      {{- if .Values.speaker.excludeInterfaces.enabled }}
        - name: metallb-excludel2
          configMap:
            defaultMode: 256
            name: metallb-excludel2
      {{- end }}
      {{- if .Values.speaker.frr.enabled }}
        - name: frr-sockets
          emptyDir: {}
        - name: frr-startup
          configMap:
            name: {{ template "metallb.fullname" . }}-frr-startup
        - name: frr-conf
          emptyDir: {}
        - name: reloader
          emptyDir: {}
        - name: metrics
          emptyDir: {}
      initContainers:
        # Copies the initial config files with the right permissions to the shared volume.
        - name: cp-frr-files
          image: {{ .Values.speaker.frr.image.repository }}:{{ .Values.speaker.frr.image.tag | default .Chart.AppVersion }}
          securityContext:
            runAsUser: 100
            runAsGroup: 101
          command: ["/bin/sh", "-c", "cp -rLf /tmp/frr/* /etc/frr/"]
          volumeMounts:
            - name: frr-startup
              mountPath: /tmp/frr
            - name: frr-conf
              mountPath: /etc/frr
        # Copies the reloader to the shared volume between the speaker and reloader.
        - name: cp-reloader
          image: {{ .Values.speaker.image.repository }}:{{ .Values.speaker.image.tag | default .Chart.AppVersion }}
          command: ["/cp-tool","/frr-reloader.sh","/etc/frr_reloader/frr-reloader.sh"]
          volumeMounts:
            - name: reloader
              mountPath: /etc/frr_reloader
        # Copies the metrics exporter
        - name: cp-metrics
          image: {{ .Values.speaker.image.repository }}:{{ .Values.speaker.image.tag | default .Chart.AppVersion }}
          command: ["/cp-tool","/frr-metrics","/etc/frr_metrics/frr-metrics"]
          volumeMounts:
            - name: metrics
              mountPath: /etc/frr_metrics
      shareProcessNamespace: true
      {{- end }}
      containers:
      - name: speaker
        image: {{ .Values.speaker.image.repository }}:{{ .Values.speaker.image.tag | default .Chart.AppVersion }}
        {{- if .Values.speaker.image.pullPolicy }}
        imagePullPolicy: {{ .Values.speaker.image.pullPolicy }}
        {{- end }}
        {{- if .Values.speaker.command }}
        command:
          - {{ .Values.speaker.command }}
        {{- end }}
        args:
        - --port={{ .Values.prometheus.metricsPort }}
        {{- with .Values.speaker.logLevel }}
        - --log-level={{ . }}
        {{- end }}
        {{- if .Values.loadBalancerClass }}
        - --lb-class={{ .Values.loadBalancerClass }}
        {{- end }}
        {{- if .Values.speaker.wanConfig }}
        - --ml-wan-config
        {{- end }}
        {{- if .Values.speaker.ignoreExcludeLB}}
        - --ignore-exclude-lb
        {{- end }}
        {{- if .Values.prometheus.secureMetricsPort }}
        - --host=localhost
        {{- end }}
        {{- if .Values.frrk8s.external }}
        - --frrk8s-namespace={{ required "namespace is required when frrk8s is external" .Values.frrk8s.namespace }}
        {{- end }}
        env:
        - name: METALLB_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: METALLB_HOST
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        {{- if .Values.speaker.memberlist.enabled }}
        {{- if .Values.speaker.memberlist.mlBindAddrOverride }}
        - name: METALLB_ML_BIND_ADDR
          value: "{{ .Values.speaker.memberlist.mlBindAddrOverride }}"
        {{ else }}
        - name: METALLB_ML_BIND_ADDR
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        {{ end }}
        - name: METALLB_ML_LABELS
          value: "app.kubernetes.io/name={{ include "metallb.name" . }},app.kubernetes.io/component=speaker"
        - name: METALLB_ML_BIND_PORT
          value: "{{ .Values.speaker.memberlist.mlBindPort }}"
        - name: METALLB_ML_SECRET_KEY_PATH
          value: "{{ .Values.speaker.memberlist.mlSecretKeyPath }}"
        {{- end }}
        {{- if .Values.speaker.frr.enabled }}
        - name: FRR_CONFIG_FILE
          value: /etc/frr_reloader/frr.conf
        - name: FRR_RELOADER_PID_FILE
          value: /etc/frr_reloader/reloader.pid
        - name: METALLB_BGP_TYPE
          value: frr
        {{- end }}
        {{- if or .Values.frrk8s.enabled .Values.frrk8s.external }}
        - name: METALLB_BGP_TYPE
          value: frr-k8s
        {{- end }}
        - name: METALLB_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        ports:
        - name: monitoring
          containerPort: {{ .Values.prometheus.metricsPort }}
        {{- if .Values.speaker.memberlist.enabled }}
        - name: memberlist-tcp
          containerPort: {{ .Values.speaker.memberlist.mlBindPort }}
          protocol: TCP
        - name: memberlist-udp
          containerPort: {{ .Values.speaker.memberlist.mlBindPort }}
          protocol: UDP
        {{- end }}
        {{- if .Values.speaker.livenessProbe.enabled }}
        livenessProbe:
          httpGet:
            {{- if .Values.prometheus.secureMetricsPort }}
            host: localhost
            {{- end }}
            path: /metrics
            port: monitoring
          initialDelaySeconds: {{ .Values.speaker.livenessProbe.initialDelaySeconds }}
          periodSeconds: {{ .Values.speaker.livenessProbe.periodSeconds }}
          timeoutSeconds: {{ .Values.speaker.livenessProbe.timeoutSeconds }}
          successThreshold: {{ .Values.speaker.livenessProbe.successThreshold }}
          failureThreshold: {{ .Values.speaker.livenessProbe.failureThreshold }}
        {{- end }}
        {{- if .Values.speaker.readinessProbe.enabled }}
        readinessProbe:
          httpGet:
            {{- if .Values.prometheus.secureMetricsPort }}
            host: localhost
            {{- end }}
            path: /metrics
            port: monitoring
          initialDelaySeconds: {{ .Values.speaker.readinessProbe.initialDelaySeconds }}
          periodSeconds: {{ .Values.speaker.readinessProbe.periodSeconds }}
          timeoutSeconds: {{ .Values.speaker.readinessProbe.timeoutSeconds }}
          successThreshold: {{ .Values.speaker.readinessProbe.successThreshold }}
          failureThreshold: {{ .Values.speaker.readinessProbe.failureThreshold }}
        {{- end }}
        {{- with .Values.speaker.resources }}
        resources:
          {{- toYaml . | nindent 10 }}
        {{- end }}
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
            add:
            - NET_RAW
        {{- if or .Values.speaker.frr.enabled .Values.speaker.memberlist.enabled .Values.speaker.excludeInterfaces.enabled }}
        volumeMounts:
          {{- if .Values.speaker.memberlist.enabled }}
          - name: memberlist
            mountPath: {{ .Values.speaker.memberlist.mlSecretKeyPath }}
          {{- end }}
          {{- if .Values.speaker.frr.enabled }}
          - name: reloader
            mountPath: /etc/frr_reloader
          {{- end }}
          {{- if .Values.speaker.excludeInterfaces.enabled }}
          - name: metallb-excludel2
            mountPath: /etc/metallb
          {{- end }}
        {{- end }}
      {{- if .Values.speaker.frr.enabled }}
      - name: frr
        securityContext:
          capabilities:
            add:
            - NET_ADMIN
            - NET_RAW
            - SYS_ADMIN
            - NET_BIND_SERVICE
        image: {{ .Values.speaker.frr.image.repository }}:{{ .Values.speaker.frr.image.tag | default .Chart.AppVersion }}
        {{- if .Values.speaker.frr.image.pullPolicy }}
        imagePullPolicy: {{ .Values.speaker.frr.image.pullPolicy }}
        {{- end }}
        env:
          - name: TINI_SUBREAPER
            value: "true"
        volumeMounts:
          - name: frr-sockets
            mountPath: /var/run/frr
          - name: frr-conf
            mountPath: /etc/frr
        # The command is FRR's default entrypoint & waiting for the log file to appear and tailing it.
        # If the log file isn't created in 60 seconds the tail fails and the container is restarted.
        # This workaround is needed to have the frr logs as part of kubectl logs -c frr < speaker_pod_name >.
        command:
          - /bin/sh
          - -c
          - |
            /sbin/tini -- /usr/lib/frr/docker-start &
            attempts=0
            until [[ -f /etc/frr/frr.log || $attempts -eq 60 ]]; do
              sleep 1
              attempts=$(( $attempts + 1 ))
            done
            tail -f /etc/frr/frr.log
        {{- with .Values.speaker.frr.resources }}
        resources:
          {{- toYaml . | nindent 12 }}
        {{- end }}
        {{- if .Values.speaker.livenessProbe.enabled }}
        livenessProbe:
          httpGet:
            {{- if .Values.prometheus.secureMetricsPort }}
            host: localhost
            {{- end }}
            path: livez
            port: {{ .Values.speaker.frr.metricsPort }}
          initialDelaySeconds: {{ .Values.speaker.livenessProbe.initialDelaySeconds }}
          periodSeconds: {{ .Values.speaker.livenessProbe.periodSeconds }}
          timeoutSeconds: {{ .Values.speaker.livenessProbe.timeoutSeconds }}
          successThreshold: {{ .Values.speaker.livenessProbe.successThreshold }}
          failureThreshold: {{ .Values.speaker.livenessProbe.failureThreshold }}
        {{- end }}
        {{- if .Values.speaker.startupProbe.enabled }}
        startupProbe:
          httpGet:
            {{- if .Values.prometheus.secureMetricsPort }}
            host: localhost
            {{- end }}
            path: /livez
            port: {{ .Values.speaker.frr.metricsPort }}
          failureThreshold: {{ .Values.speaker.startupProbe.failureThreshold }}
          periodSeconds: {{ .Values.speaker.startupProbe.periodSeconds }}
        {{- end }}
      - name: reloader
        image: {{ .Values.speaker.frr.image.repository }}:{{ .Values.speaker.frr.image.tag | default .Chart.AppVersion }}
        {{- if .Values.speaker.frr.image.pullPolicy }}
        imagePullPolicy: {{ .Values.speaker.frr.image.pullPolicy }}
        {{- end }}
        command: ["/etc/frr_reloader/frr-reloader.sh"]
        volumeMounts:
          - name: frr-sockets
            mountPath: /var/run/frr
          - name: frr-conf
            mountPath: /etc/frr
          - name: reloader
            mountPath: /etc/frr_reloader
        {{- with .Values.speaker.reloader.resources }}
        resources:
          {{- toYaml . | nindent 12 }}
        {{- end }}
      - name: frr-metrics
        image: {{ .Values.speaker.frr.image.repository }}:{{ .Values.speaker.frr.image.tag | default .Chart.AppVersion }}
        command: ["/etc/frr_metrics/frr-metrics"]
        args:
          - --metrics-port={{ .Values.speaker.frr.metricsPort }}
          {{- if .Values.prometheus.secureMetricsPort }}
          - --host=localhost
          {{- end }}
        env:
          - name: VTYSH_HISTFILE
            value: /dev/null
        ports:
          - containerPort: {{ .Values.speaker.frr.metricsPort }}
            name: monitoring
        volumeMounts:
          - name: frr-sockets
            mountPath: /var/run/frr
          - name: frr-conf
            mountPath: /etc/frr
          - name: metrics
            mountPath: /etc/frr_metrics
        {{- with .Values.speaker.frrMetrics.resources }}
        resources:
          {{- toYaml . | nindent 12 }}
        {{- end }}
      {{- end }}
      {{- if .Values.prometheus.secureMetricsPort }}
      - name: kube-rbac-proxy
        image: {{ .Values.prometheus.rbacProxy.repository }}:{{ .Values.prometheus.rbacProxy.tag }}
        imagePullPolicy: {{ .Values.prometheus.rbacProxy.pullPolicy }}
        args:
          - --logtostderr
          - --secure-listen-address=:{{ .Values.prometheus.secureMetricsPort }}
          - --upstream=http://localhost:{{ .Values.prometheus.metricsPort }}/
          - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
        {{- if .Values.prometheus.speakerMetricsTLSSecret }}
          - --tls-private-key-file=/etc/metrics/tls.key
          - --tls-cert-file=/etc/metrics/tls.crt
        {{- end }}
        ports:
          - containerPort: {{ .Values.prometheus.secureMetricsPort }}
            name: metricshttps
        resources:
          requests:
            cpu: 10m
            memory: 20Mi
        terminationMessagePolicy: FallbackToLogsOnError
        {{- if .Values.prometheus.speakerMetricsTLSSecret }}
        volumeMounts:
          - name: metrics-certs
            mountPath: /etc/metrics
            readOnly: true
        {{- end }}
      {{ end }}
      {{- if .Values.speaker.frr.enabled }}
      {{- if .Values.speaker.frr.secureMetricsPort }}
      - name: kube-rbac-proxy-frr
        image: {{ .Values.prometheus.rbacProxy.repository }}:{{ .Values.prometheus.rbacProxy.tag | default .Chart.AppVersion }}
        imagePullPolicy: {{ .Values.prometheus.rbacProxy.pullPolicy }}
        args:
          - --logtostderr
          - --secure-listen-address=:{{ .Values.speaker.frr.secureMetricsPort }}
          - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
          - --upstream=http://localhost:{{ .Values.speaker.frr.metricsPort }}/
        {{- if .Values.prometheus.speakerMetricsTLSSecret }}
          - --tls-private-key-file=/etc/metrics/tls.key
          - --tls-cert-file=/etc/metrics/tls.crt
        {{- end }}
        ports:
          - containerPort: {{ .Values.speaker.frr.secureMetricsPort }}
            name: frrmetricshttps
        env:
          - name: METALLB_HOST
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
        resources:
          requests:
            cpu: 10m
            memory: 20Mi
        terminationMessagePolicy: FallbackToLogsOnError
        {{- if .Values.prometheus.speakerMetricsTLSSecret }}
        volumeMounts:
          - name: metrics-certs
            mountPath: /etc/metrics
            readOnly: true
        {{- end }}
      {{ end }}
      {{- end }}
      {{- if .Values.speaker.extraContainers }}
      {{- toYaml .Values.speaker.extraContainers | nindent 6 }}
      {{- end }}
      nodeSelector:
        "kubernetes.io/os": linux
        {{- with .Values.speaker.nodeSelector }}
          {{- toYaml . | nindent 8 }}
        {{- end }}
      {{- with .Values.speaker.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- if or .Values.speaker.tolerateMaster .Values.speaker.tolerations }}
      tolerations:
      {{- if .Values.speaker.tolerateMaster }}
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
        operator: Exists
      - key: node-role.kubernetes.io/control-plane
        effect: NoSchedule
        operator: Exists
      {{- end }}
      {{- with .Values.speaker.tolerations }}
        {{- toYaml . | nindent 6 }}
      {{- end }}
      {{- end }}
      {{- with .Values.speaker.priorityClassName }}
      priorityClassName: {{ . | quote }}
      {{- end }}
{{- end }}
