helm 模板中的查询值

Query Values in helm template

我正在尝试为我的 Helm 图表中的所有入口资源生成 TLS 证书。我的 helm chart 包含一个具有多个后端的应用程序,因此我的 Values.yaml 的结构如下:

backend1:
  ingress:
    host: testing.app.com
    tls:
      - secretName: my-tls-cert
        hosts:
          - testing.app.com
backend2:
  ingress:
    host: testing.app.com
    tls:
      - secretName: idp-cts-cert
        hosts:
          - idp-cts
db
  creds: ""

serviceName: ""

请注意,其中混合了映射和字符串值。我的目标是使用我编写的实用程序模板来调用 genSignedCert 并生成一个 TLS 证书,其中的主机列为 CN 或备用名称:

{{/*
Return a self-signed TLS certificate
{{ include "common.certs.ingress-tls" .hosts  }}
*/}}
{{- define "common.certs.gen-cert" -}}
{{- $hostlist := toStrings . -}}
{{- $cn := (first $hostlist) -}}
{{- $altnames := uniq (rest $hostlist) -}}
{{- $ca := genCA "idp-ca" 365 -}}
{{- $cert := genSignedCert $cn nil $altnames 365 $ca -}}
tls.crt: {{ $cert.Cert | b64enc }}
tls.key: {{ $cert.Key | b64enc }}
{{- end -}} 

我已经尝试遍历这些值,但我无法想出可行的代码来执行此操作。

Edit1:我知道使用自签名证书的安全隐患。错误的 values.yaml 结构继承自以下事实:这是一个伞状图表,每个支持的图表也是它自己的图表。可能需要重构图表结构,但我想先用尽所有选项。

考虑在 Helm 外部生成 TLS 证书,并通过值注入它(或直接将其组件存储在 Secret 中)。这里避免了一些复杂的代码。但是,还有一个更严重的问题:每次调用 genCAgenSignedCert 时,它都会创建一个新证书,因此每次升级时,您都会获得不同的证书,就此而言,如果您调用这个模板每个 Ingress 对象一次,每个都有不同的证书。


稍微重组 values.yaml 会帮助解决这个问题。代码很难说 backend1 是后端规范,但 serviceName 不是。如果你只有 backends 的列表,这会更容易:

backends:
  - ingress:
      host: testing.app.com
      ...
  - ingress:
      host: testing.app.com
      ...

然后,您将遇到 Helm 模板作为全功能编程语言的一些限制。模板只有 return 个字符串,因此您不能编写一个 return 列表的模板。您不能将函数作为参数传递给模板,因此您不能编写通用的 map (在有限的情况下,您可以传递模板名称并 include 它)。

您可以编写一个递归函数,将部分列表传递给下一次迭代,然后在完成时调用最终生成器。在Python中,我们可以这样写:

def generateCertificate(backends, tls, hosts):
  # If `tls` is non-empty, take the first item from it and add its
  # hosts to the `hosts` list; then recurse with the same backend
  # list, the remaining `tls` items, and the updated `hosts`:
  if len(tls) > 0:
    return generateCertificate(backends, tls[1:], hosts + tls[0].hosts)
  # If `tls` is empty but `backends` is non-empty, take the first
  # backend, and recurse with the remaining `backends`, the `tls` items
  # from the selected backend, and the same `hosts`:
  else if len(backends) > 0:
    return generateCertificate(backends[1:], backends[0].tls, hosts)
  # If `tls` and `backends` are both empty, we're done
  else:
    return buildTheCertificate(hosts)

certificate = generateCertificate(values.backends, [], [])

我们可以将此逻辑转换为 Go 模板:

{{/* Emit a TLS certificate given the list of backends.  The
parameter is a dictionary with keys `backends`, `tls`, and `hosts`. */}}
{{- define "common.certs.gen-cert" -}}
{{- if .tls -}}
  {{- include "common.certs.gen-cert" (dict "backends" .backend "tls" (last .tls) "hosts" (concat .hosts (head .tls).hosts)) -}}
{{- else if .backends -}}
  {{- include "common.certs.gen-cert" (dict "backends" (tail .backends) "tls" (head .backends).tls "hosts" .hosts) -}}
{{- else -}}
  {{- include "common.certs.gen-cert-hosts" .hosts -}}
{{- end -}}
{{- end -}}

{{/* Actually generate a TLS certificate from a list of host names.
Note, the certificate will be regenerated on every call.  The
single parameter is a list of names. */}}
{{- define "common.certs.gen-cert-hosts" -}}
{{- $cn := first . -}}
{{- $altnames := rest . | uniq -}}
{{- $ca := genCA "idp-ca" 365 -}}
{{- $cert := genSignedCert $cn nil $altnames 365 $ca -}}
tls.crt: {{ $cert.Cert | b64enc }}
tls.key: {{ $cert.Key | b64enc }}
{{- end -}}

{{- include "common.certs.gen-cert" (dict "backends" .Values.backends) -}}

这段代码足够复杂,可能值得对其进行单元测试。设置它留作练习; Helm 在这里没有任何类型的本地支持。