Skip to content

Commit

Permalink
Helm Gen Enhancements (#406)
Browse files Browse the repository at this point in the history
* Better merging of operator & custom values so we can allow nested operators

* different merge approach

* changelog

* codegen

* codegen

* change merge strategy to be compatible with yaml comments

* Was not handling nil values

* codegen
  • Loading branch information
josh-pritchard authored Feb 8, 2023
1 parent feb141e commit 49a2745
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 61 deletions.
5 changes: 5 additions & 0 deletions changelog/v0.27.1/helm-gen-enhancements.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
changelog:
- type: NON_USER_FACING
description: Helm gen enhancements to support new gloo-platform-chart.
issueLink: https://github.com/solo-io/skv2/issues/407

6 changes: 5 additions & 1 deletion codegen/model/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ type ValuesReferenceDocs struct {
type Operator struct {
Name string

// (Optional) For nesting operators in values API (e.g. a value of "agent" would place an operator at "agent.<operatorName>" )
ValuePath string

// deployment config
Deployment Deployment

Expand Down Expand Up @@ -150,7 +153,8 @@ func (c Chart) BuildChartValues() values.UserHelmValues {
}

helmValues.Operators = append(helmValues.Operators, values.UserOperatorValues{
Name: operator.Name,
Name: operator.Name,
ValuePath: operator.ValuePath,
Values: values.UserValues{
UserContainerValues: makeContainerDocs(operator.Deployment.Container),
Sidecars: sidecars,
Expand Down
1 change: 1 addition & 0 deletions codegen/model/values/helm_chart_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type UserHelmValues struct {

type UserOperatorValues struct {
Name string
ValuePath string
Values UserValues
CustomValues interface{}
}
Expand Down
12 changes: 12 additions & 0 deletions codegen/render/chart_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ var defaultChartInputs = inputTemplates{
},
}

var chartInputsNoOperators = inputTemplates{
"chart/values.yamltmpl": {
Path: "values.yaml",
},
"chart/Chart.yamltmpl": {
Path: "Chart.yaml",
},
}

func RenderChart(chart model.Chart) ([]OutFile, error) {
renderer := DefaultTemplateRenderer

Expand All @@ -42,6 +51,9 @@ func RenderChart(chart model.Chart) ([]OutFile, error) {

func (r ChartRenderer) Render(chart model.Chart) ([]OutFile, error) {
templatesToRender := defaultChartInputs
if len(chart.Operators) == 0 {
templatesToRender = chartInputsNoOperators
}

files, err := r.renderCoreTemplates(templatesToRender, chart)
if err != nil {
Expand Down
139 changes: 133 additions & 6 deletions codegen/render/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package render
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
"text/template"

"github.com/solo-io/skv2/codegen/model/values"
"github.com/solo-io/skv2/codegen/util/stringutils"

"github.com/BurntSushi/toml"
Expand All @@ -15,6 +17,7 @@ import (
"github.com/iancoleman/strcase"
"github.com/solo-io/skv2/codegen/model"
"github.com/solo-io/skv2/codegen/util"
goyaml "gopkg.in/yaml.v3"
"sigs.k8s.io/yaml"
)

Expand All @@ -25,11 +28,14 @@ func makeTemplateFuncs(customFuncs template.FuncMap) template.FuncMap {
skv2Funcs := template.FuncMap{
// string utils

"toToml": toTOML,
"toYaml": toYAML,
"fromYaml": fromYAML,
"toJson": toJSON,
"fromJson": fromJSON,
"toToml": toTOML,
"toYaml": toYAML,
"fromYaml": fromYAML,
"toJson": toJSON,
"fromJson": fromJSON,
"toNode": toNode,
"fromNode": fromNode,
"mergeNodes": mergeNodes,

"join": strings.Join,
"lower": strings.ToLower,
Expand Down Expand Up @@ -75,6 +81,40 @@ func makeTemplateFuncs(customFuncs template.FuncMap) template.FuncMap {
}
return t.Name
},

"get_operator_values": func(o values.UserOperatorValues) goyaml.Node {
opValues := map[string]interface{}{
strcase.ToLowerCamel(o.Name): o.Values,
}
if o.ValuePath != "" {
splitPath := strings.Split(o.ValuePath, ".")
for _, p := range splitPath {
opValues = map[string]interface{}{
strcase.ToLowerCamel(p): opValues,
}
}
}
return toNode(opValues)
},

"get_operator_custom_values": func(o values.UserOperatorValues) goyaml.Node {
opValues := map[string]interface{}{}
if o.CustomValues != nil {
opValues = map[string]interface{}{
strcase.ToLowerCamel(o.Name): o.CustomValues,
}
if o.ValuePath != "" {
splitPath := strings.Split(o.ValuePath, ".")
for _, p := range splitPath {
opValues = map[string]interface{}{
strcase.ToLowerCamel(p): opValues,
}
}
}
}
return toNode(opValues)
},

"containerConfigs": containerConfigs,
}

Expand All @@ -96,7 +136,11 @@ type containerConfig struct {
}

func containerConfigs(op model.Operator) []containerConfig {
opVar := "$.Values." + strcase.ToLowerCamel(op.Name)
opVar := fmt.Sprintf("$.Values.%s", strcase.ToLowerCamel(op.Name))
if op.ValuePath != "" {
opVar = fmt.Sprintf("$.Values.%s.%s", op.ValuePath, strcase.ToLowerCamel(op.Name))

}
configs := []containerConfig{{
Container: op.Deployment.Container,
Name: op.Name,
Expand Down Expand Up @@ -193,6 +237,89 @@ func fromYAML(str string) map[string]interface{} {
return m
}

func toNode(v interface{}) goyaml.Node {
var node goyaml.Node
if v != nil {
err := goyaml.Unmarshal([]byte(toYAML(v)), &node)
if err != nil {
panic(err)
}
}
return node
}

func fromNode(n goyaml.Node) string {
b, err := goyaml.Marshal(&n)
if err != nil {
panic(err)
}
return string(b)
}

func mergeNodes(nodes ...goyaml.Node) goyaml.Node {
if len(nodes) <= 1 {
panic("at least two nodes required for merge")
}
var mergedNode goyaml.Node
for _, n := range nodes {

if mergedNode.IsZero() {
if n.IsZero() {
continue
}
mergedNode = n
continue
}

if err := recursiveNodeMerge(&n, &mergedNode); err != nil {
panic(err)
}
}
if mergedNode.IsZero() {
panic("all nodes were found to be IsZero, cannot continue")

}
return mergedNode
}

func nodesEqual(l, r *goyaml.Node) bool {
if l.Kind == goyaml.ScalarNode && r.Kind == goyaml.ScalarNode {
return l.Value == r.Value
}
panic("equals on non-scalars not implemented!")
}

func recursiveNodeMerge(from, into *goyaml.Node) error {
if from.Kind != into.Kind {
return errors.New("cannot merge nodes of different kinds")
}
switch from.Kind {
case goyaml.MappingNode:
for i := 0; i < len(from.Content); i += 2 {
found := false
for j := 0; j < len(into.Content); j += 2 {
if nodesEqual(from.Content[i], into.Content[j]) {
found = true
if err := recursiveNodeMerge(from.Content[i+1], into.Content[j+1]); err != nil {
return errors.New("at key " + from.Content[i].Value + ": " + err.Error())
}
break
}
}
if !found {
into.Content = append(into.Content, from.Content[i:i+2]...)
}
}
case goyaml.SequenceNode:
into.Content = append(into.Content, from.Content...)
case goyaml.DocumentNode:
recursiveNodeMerge(from.Content[0], into.Content[0])
default:
return errors.New("can only merge mapping and sequence nodes")
}
return nil
}

// toTOML takes an interface, marshals it to toml, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
Expand Down
15 changes: 4 additions & 11 deletions codegen/templates/chart/values.yamltmpl
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
[[- $values := .BuildChartValues ]]

[[- if $values.CustomValues ]]
[[- toYaml $values.CustomValues ]]
[[- end ]]

[[ range $_, $operator := $values.Operators ]]
[[ lower_camel $operator.Name ]]:
[[ toYaml $operator.Values | indent 2]]
[[- if $operator.CustomValues ]]
[[ toYaml $operator.CustomValues | indent 2 ]]
[[- end ]]
[[- $customValues := toNode $values.CustomValues ]]
[[- range $_, $operator := $values.Operators ]]
[[- $customValues = mergeNodes $customValues (get_operator_values $operator) (get_operator_custom_values $operator) ]]
[[- end ]]
[[- fromNode $customValues ]]
40 changes: 19 additions & 21 deletions codegen/test/chart-no-desc/values.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
# Code generated by skv2. DO NOT EDIT.




painter:
enabled: true
env:
- name: FOO
value: BAR
floatingUserId: false
image:
pullPolicy: IfNotPresent
registry: quay.io/solo-io
repository: painter
tag: v0.0.0
ports: {}
runAsUser: 10101
serviceType: ""
sidecars:
palette:
env: null
image:
enabled: true
env:
- name: FOO
value: BAR
floatingUserId: false
image:
pullPolicy: IfNotPresent
registry: quay.io/solo-io
repository: palette
repository: painter
tag: v0.0.0
ports: {}
runAsUser: 10101
serviceType: ""
sidecars:
palette:
env: null
image:
pullPolicy: IfNotPresent
registry: quay.io/solo-io
repository: palette
tag: v0.0.0

40 changes: 19 additions & 21 deletions codegen/test/chart/values.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
# Code generated by skv2. DO NOT EDIT.




painter:
enabled: true
env:
- name: FOO
value: BAR
floatingUserId: false
image:
pullPolicy: IfNotPresent
registry: quay.io/solo-io
repository: painter
tag: v0.0.0
ports: {}
runAsUser: 10101
serviceType: ""
sidecars:
palette:
env: null
image:
enabled: true
env:
- name: FOO
value: BAR
floatingUserId: false
image:
pullPolicy: IfNotPresent
registry: quay.io/solo-io
repository: palette
repository: painter
tag: v0.0.0
ports: {}
runAsUser: 10101
serviceType: ""
sidecars:
palette:
env: null
image:
pullPolicy: IfNotPresent
registry: quay.io/solo-io
repository: palette
tag: v0.0.0

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ require (
golang.org/x/tools v0.1.12
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.25.4
k8s.io/apiextensions-apiserver v0.25.4
k8s.io/apimachinery v0.25.4
Expand Down Expand Up @@ -133,7 +134,6 @@ require (
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.25.4 // indirect
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
Expand Down

0 comments on commit 49a2745

Please sign in to comment.