{{- 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 .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" bgpd_options=" -A 127.0.0.1 -p 0" 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" bfdd_options=" -A 127.0.0.1" 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 }}