helm / helm/community

feature: templated values for subcharts

Open
#437 0 comments 2 reactions 0 assignees View on GitHub
Dominant language
No language data
Stars
495
Forks
210
PR merge metrics
No merged PRs in 30d

Description

Presently, a Helm subchart's values are exposed directly by the parent chart as a values node named after the sub-chart. This makes the subchart's interface a direct part of the parent chart's interface. Any changes to the sub-chart that affect its values inputs will directly affect users of the parent chart.

Consequently it is not possible for a parent chart to wrap and abstract a sub-chart, using the sub-chart as implementation detail. This becomes a problem when the parent chart does not directly control all its dependency charts, e.g. when wrapping upstream vendor charts.

## Problem

Consider a chart that wishes to bundle Grafana Tempo and an OpenTelemetry Collector configured to enrich traces to forward to Tempo.

Ideally, this chart could consume the existing upstream community charts https://github.com/grafana-community/helm-charts/tree/main/charts/tempo and https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-collector as dependencies.

But if it does so, then the _full range of configuration_ for both charts is exposed to the user of the parent chart that bundles them, including all changes in their values input format between versions.

Consider the following parent chart:

```yaml
# parent Chart.yaml
apiVersion: v2
name: tracing-bundle
description: OpenTelemetry trace routing, enrichment and storage
type: application
version: 1.0.0
dependencies:
- name: tempo
repository: https://grafana-community.github.io/helm-charts
version: 2.0.0
- name: opentelemetry-collector
repository: https://open-telemetry.github.io/opentelemetry-helm-charts
version: 0.152.0
```

The parent `values.yaml` must define sub-trees for each sub-chart:

```yaml
# parent values.yaml
opentelemetry-collector:
# overrides for https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/values.yaml
mode: deployment
image:
repository: "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib"
tempo:
# overrides for https://github.com/grafana-community/helm-charts/blob/main/charts/tempo/values.yaml
# ...
```

These are directly exposed to the consumer of the parent chart. If e.g. the `opentelemetry-collector` chart renamed `mode` to `collectorMode`, then consumers of the `tracing-bundle` wrapper chart would have to change their values `opentelemetry-collector.mode` to `opentelemetry-collector.collectorMode`.

Additionally, the wrapper has no way to transform input to sub-charts or generate input to sub-charts based on its own input.

For example, it's not possible for the parent chart to support a `tempo.enabled` field to control whether the collector is installed at all (or not without the co-operation of the `tempo` chart itself adding such an option).

The parent chart cannot propagate some parent `additionalLabels` values in its parent chart to sub-charts, adapting to the key naming and format used by each sub-chart if and where they differ.

## Existing workarounds

There are no known existing workarounds. It is generally necessary to duplicate and rewrite the child charts where there's any requirement for co-operating charts or a stable values interface.

Helm does support [importing child values via dependencies `import-values`](https://helm.sh/docs/topics/charts/#importing-child-values-via-dependencies) but this mechanism:

* cannot copy a value from one sub-chart to another
* cannot explicitly copy or generate a sub-chart value from a parent chart value
* cannot transform the structure or content of values

In some cases it is possible to use a template to generate a yaml values for the sub-chart, copy individual resources from the sub-chart into the parent chart as templates, and explicitly inflate them with `include`. But this requires that every sub-chart resource must be copied into the parent chart and edited to be wrapped in a named template.

## Proposal

I propose that Helm should add a programmatic means of inflating a sub-chart with templated values input.

The dependencies entry should suppress auto-inflation with a new key, and allow giving the dependency an alias (in case of collisions between charts in two different repos); e.g.

```yaml
# parent Chart.yaml
# ...
dependencies:
- name: tempo
repository: https://grafana-community.github.io/helm-charts
version: 2.0.0
automatic: false # not auto-expanded
alias: tempo
- name: opentelemetry-collector
repository: https://open-telemetry.github.io/opentelemetry-helm-charts
version: 0.152.0
automatic: false # not auto-expanded
alias: opentelemetry-collector
```

These would be ignored for the purpose of `helm install`, `helm template` etc.

A new helm template function is then introduced to expand them on demand; say `inflate "chart-name-or-alias" $values`, e.g.:

```yaml
# parent chart templates/_otel_collector.tpl

{{- define "tracing-bundle"."otel-collector-values" }}
{{% generate the values.yaml for input into the opentelemetry-collector chart %}}
mode: deployment
image:
repository: {{ .Values.otel-collector.repository | default "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib" }}
{{- end }}

{{% expand the opentelemetry-collector dependency chart as if it was a sub-chart, but using the supplied values instead of .Values.opentelemetry-collector %}}
{{ inflate "opentelemetry-collector" {{ include "tracing-bundle"."otel-collector-values" .Values | fromYaml }} }}

```

Since it's a regular function, it can be inside conditionals (`{{ if .Values.otel-collector.enabled }}`), loops (deploying multiple instances of a sub-chart), etc.

The parent completely controls what is exposed to the sub-chart and the interface with which it is exposed.

Should a parent chart wish to emulate the existing behaviour, it can do so with

```
{{ inflate "opentelemetry-collector" .Values.opentelemetry-collector }}
```

(with the caveat that handling of .Values.global would need to be added explicitly, or provided by a helper function)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.