@@ -3,6 +3,8 @@ package helm
33import (
44 "context"
55 "fmt"
6+ "helm.sh/helm/v3/pkg/chartutil"
7+ "k8s.io/apimachinery/pkg/util/yaml"
68 "sort"
79 "strings"
810
@@ -19,10 +21,11 @@ import (
1921)
2022
2123const (
22- helmRepositoryCache = "/tmp/.helmcache"
23- helmRepositoryConfig = "/tmp/.helmrepo"
24- helmRegistryConfigFile = "/tmp/.helmregistry/config.json"
25- ociSchemePrefix = string (config .EndpointSchemaOCI + "://" )
24+ helmRepositoryCache = "/tmp/.helmcache"
25+ helmRepositoryConfig = "/tmp/.helmrepo"
26+ helmRegistryConfigFile = "/tmp/.helmregistry/config.json"
27+ ociSchemePrefix = string (config .EndpointSchemaOCI + "://" )
28+ helmValuesMetadataFileName = "component-values-metadata.yaml"
2629)
2730
2831// HelmClient embeds the client.Client interface for usage in this package.
@@ -67,6 +70,10 @@ func NewClient(namespace string, helmRepoData *config.HelmRepositoryData, debug
6770
6871// InstallOrUpgrade takes a helmChart and applies it.
6972func (c * Client ) InstallOrUpgrade (ctx context.Context , chart * client.ChartSpec ) error {
73+ return c .InstallOrUpgradeWithMappedValues (ctx , chart , nil )
74+ }
75+
76+ func (c * Client ) InstallOrUpgradeWithMappedValues (ctx context.Context , chart * client.ChartSpec , mappedValues map [string ]string ) error {
7077 // This helm-client currently only works with OCI-Helm-Repositories.
7178 // Therefore, the chartName has to include the FQDN of the repository (e.g. "oci://my.repo/...")
7279 // If in the future non-oci-repositories need to be used, this should be done here...
@@ -76,6 +83,10 @@ func (c *Client) InstallOrUpgrade(ctx context.Context, chart *client.ChartSpec)
7683 return fmt .Errorf ("error patching chart-version for chart %s: %w" , chart .ChartName , err )
7784 }
7885
86+ if err := c .patchMappedValues (ctx , chart , mappedValues ); err != nil {
87+ return err
88+ }
89+
7990 _ , err := c .helmClient .InstallOrUpgradeChart (ctx , chart )
8091 if err != nil {
8192 return fmt .Errorf ("error while installOrUpgrade chart %s: %w" , chart .ChartName , err )
@@ -154,6 +165,107 @@ func (c *Client) GetChartSpecValues(spec *client.ChartSpec) (map[string]interfac
154165 return c .helmClient .GetChartSpecValues (spec )
155166}
156167
168+ type helmValuesMetadata struct {
169+ ApiVersion string `yaml:"apiVersion"`
170+ Metadata map [string ]helmValuesMetadataEntry `yaml:"metadata"`
171+ }
172+
173+ type helmValuesMetadataEntry struct {
174+ Name string `yaml:"name"`
175+ Description string `yaml:"description"`
176+ Keys []helmValuesMetadataKey `yaml:"keys"`
177+ }
178+
179+ type helmValuesMetadataKey struct {
180+ Path string `yaml:"path"`
181+ }
182+
183+ func (c * Client ) patchMappedValues (ctx context.Context , chartSpec * client.ChartSpec , mappedValues map [string ]string ) error {
184+ logger := log .FromContext (ctx )
185+ if mappedValues == nil {
186+ logger .Info ("return patching values because mapped values are nil" )
187+ return nil
188+ }
189+
190+ helmChart , err := c .getChart (ctx , chartSpec )
191+ if err != nil {
192+ return err
193+ }
194+
195+ files := helmChart .Files
196+ var metadataFile * helmValuesMetadata
197+ for _ , file := range files {
198+ logger .Info (file .Name )
199+ if file .Name == helmValuesMetadataFileName {
200+ err := yaml .Unmarshal (file .Data , & metadataFile )
201+ if err != nil {
202+ return fmt .Errorf ("failed to unmarshal %s: %w" , helmValuesMetadataFileName , err )
203+ }
204+ logger .Info (fmt .Sprintf ("found metadata file %q for chart %q" , helmValuesMetadataFileName , chartSpec .ChartName ))
205+ logger .Info (fmt .Sprintf ("%+v" , metadataFile ))
206+ break
207+ }
208+ }
209+
210+ if metadataFile == nil {
211+ logger .Info (fmt .Sprintf ("found no metadata file %q for chart %q" , helmValuesMetadataFileName , chartSpec .ChartName ))
212+ return nil
213+ }
214+
215+ values , err := chartutil .ReadValues ([]byte (chartSpec .ValuesYaml ))
216+ if err != nil {
217+ return fmt .Errorf ("failed to read current values: %w" , err )
218+ }
219+
220+ // TODO Exclude this check because it has to be done if the mappedValues are nil too
221+ for _ , metadataEntry := range metadataFile .Metadata {
222+ // Check if metadata values are already set in valuesYamlOverwrite
223+ for _ , key := range metadataEntry .Keys {
224+ // key.Path is somethings like controllerManager.env.logLevel (dot-separated)
225+ path := key .Path
226+ if isMetadataPathInValuesMap (path , values ) {
227+ return fmt .Errorf ("values contains path %s which should only be set in field mappedValues" , path )
228+ }
229+
230+ value , ok := mappedValues [metadataEntry .Name ]
231+ if ! ok {
232+ // Return nil because metadata is nil in mappedValue
233+ logger .Info (fmt .Sprintf ("found no value in mappedValues for metadata %q" , metadataEntry .Name ))
234+ return nil
235+ }
236+
237+ setValueInChartSpec (ctx , chartSpec , path , value )
238+ }
239+ }
240+
241+ return nil
242+ }
243+
244+ func setValueInChartSpec (ctx context.Context , chartSpec * client.ChartSpec , path string , value interface {}) {
245+ // TODO Mapping
246+ // This is easier than reading the current values as string and merging them.
247+ logger := log .FromContext (ctx )
248+ option := fmt .Sprintf ("%s=%s" , path , value )
249+ logger .Info (fmt .Sprintf ("set option %q for chart %q" , option , chartSpec .ChartName ))
250+ chartSpec .ValuesOptions .Values = append (chartSpec .ValuesOptions .Values , option )
251+ }
252+
253+ func isMetadataPathInValuesMap (path string , values chartutil.Values ) bool {
254+ before , after , _ := strings .Cut (path , "." )
255+
256+ i , ok := values [before ]
257+ if ! ok {
258+ return false
259+ }
260+
261+ ii , ok := i .(map [string ]interface {})
262+ if ! ok && after == "" {
263+ return true
264+ }
265+
266+ return isMetadataPathInValuesMap (after , ii )
267+ }
268+
157269func (c * Client ) patchOciEndpoint (chart * client.ChartSpec ) {
158270 if strings .HasPrefix (chart .ChartName , ociSchemePrefix ) {
159271 return
@@ -173,7 +285,7 @@ func (c *Client) patchChartVersion(chart *client.ChartSpec) error {
173285 return fmt .Errorf ("error resolving tags for chart %s: %w" , chart .ChartName , err )
174286 }
175287
176- //sort tags by version
288+ // sort tags by version
177289 sortedTags := sortByVersionDescending (tags )
178290
179291 if len (sortedTags ) <= 0 {
0 commit comments