diff --git a/helm-toolkit/Chart.yaml b/helm-toolkit/Chart.yaml index 638dfe339..04f22bec1 100644 --- a/helm-toolkit/Chart.yaml +++ b/helm-toolkit/Chart.yaml @@ -15,7 +15,7 @@ apiVersion: v1 appVersion: v1.0.0 description: OpenStack-Helm Helm-Toolkit name: helm-toolkit -version: 0.2.74 +version: 0.2.75 home: https://docs.openstack.org/openstack-helm icon: https://www.openstack.org/themes/openstack/images/project-mascots/OpenStack-Helm/OpenStack_Project_OpenStackHelm_vertical.png sources: diff --git a/helm-toolkit/templates/utils/_daemonset_overrides_root.tpl b/helm-toolkit/templates/utils/_daemonset_overrides_root.tpl new file mode 100644 index 000000000..bdb28c331 --- /dev/null +++ b/helm-toolkit/templates/utils/_daemonset_overrides_root.tpl @@ -0,0 +1,279 @@ +{{/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/}} + +{{/* + +The helm-toolkit.utils.daemonset_overrides function have some limitations: + + * it allows to override only conf values specifid in configmap-etc + * it doesn't allow to override values for daemonsets passed via env variables + or via damoenset definition. As result it is impossible to have mixed + deployment when one compute is configured with dpdk while other not. + * it is impossible to override interface names/other information stored in + -bin configmap + * It allows to schedule on both hosts and labels, which adds some + uncertainty + +This implementation is intended to handle those limitations: + + * it allows to schedule only based on labels + * it creates -bin per daemonset override + * it allows to override values when rendering daemonsets + + It picks data from the following structure: + + .Values: + overrides: + mychart_mydaemonset: + labels: + label::value: + values: + override_root_option: override_root_value + conf: + ovs_dpdk: + enabled: true + neutron: + DEFAULT: + foo: bar + +*/}} + +{{- define "helm-toolkit.utils.daemonset_overrides_root" }} + {{- $daemonset := index . 0 }} + {{- $daemonSetTemplateName := index . 1 }} + {{ $serviceAccountName := index . 2 }} + {{- $configmap_include := index . 3 }} + {{- $configmap_name := index . 4 }} + {{- $configbin_include := index . 5 }} + {{- $configbin_name := index . 6 }} + {{- $context := index . 7 }} + + {{- $_ := unset $context ".Files" }} + {{- $daemonset_root_name := printf (print $context.Chart.Name "_" $daemonset) }} + {{- $_ := set $context.Values "__daemonset_list" list }} + {{- $_ := set $context.Values "__default" dict }} + + {{- $default_enabled := true }} + {{- if hasKey $context.Values "overrides" }} + {{- range $key, $val := $context.Values.overrides }} + + {{- if eq $key $daemonset_root_name }} + {{- range $type, $type_data := . }} + {{- if eq $type "overrides_default" }} + {{- $default_enabled = $type_data }} + {{- end }} + + {{- if eq $type "labels" }} + {{- $_ := set $context.Values "__label_dict" . }} + {{- range $lname, $ldata := . }} + {{ $label_name := (split "::" $lname)._0 }} + {{ $label_value := (split "::" $lname)._1 }} + {{/* dictionary that will contain all info needed to generate this + iteration of the daemonset. */}} + {{- $_ := set $context.Values "__current_label" dict }} + + {{/* set daemonset name */}} + {{- $_ := set $context.Values.__current_label "name" $label_name }} + + {{/* set daemonset metadata annotation */}} + {{- $_ := set $context.Values.__current_label "daemonset_override" $lname }} + + {{/* apply overrides */}} + + + {{- $override_root_copy := $ldata.values }} + {{/* Deep copy to prevent https://storyboard.openstack.org/#!/story/2005936 */}} + {{- $root_copy := omit ($context.Values | toYaml | fromYaml) "overrides" }} + {{- $merged_dict := mergeOverwrite $root_copy $override_root_copy }} + + {{- $root_conf_copy2 := dict "values" $merged_dict }} + {{- $context_values := omit (omit ($context.Values | toYaml | fromYaml) "values") "__daemonset_list" }} + {{- $root_conf_copy3 := mergeOverwrite $context_values $root_conf_copy2.values }} + {{- $root_conf_copy4 := dict "Values" $root_conf_copy3 }} + {{- $_ := set $context.Values.__current_label "nodeData" $root_conf_copy4 }} + + + {{/* Schedule to the provided label value(s) */}} + {{- $label_dict := dict "key" $label_name }} + {{- $_ := set $label_dict "values" (list $label_value) }} + {{- $_ := set $label_dict "operator" "In" }} + {{- $list_aggregate := list $label_dict }} + {{- $_ := set $context.Values.__current_label "matchExpressions" $list_aggregate }} + + {{/* Do not schedule to other specified labels, with higher + precedence as the list position increases. Last defined label + is highest priority. */}} + {{- $other_labels := omit $context.Values.__label_dict $lname }} + {{- range $lname2, $ldata2 := $other_labels }} + {{ $label_name2 := (split "::" $lname2)._0 }} + {{ $label_value2 := (split "::" $lname2)._1 }} + + {{- $label_dict := dict "key" $label_name2 }} + {{- $_ := set $label_dict "values" (list $label_value2) }} + {{- $_ := set $label_dict "operator" "NotIn" }} + + {{- $list_aggregate := append $context.Values.__current_label.matchExpressions $label_dict }} + {{- $_ := set $context.Values.__current_label "matchExpressions" $list_aggregate }} + {{- end }} + + {{/* store completed daemonset entry/info into global list */}} + {{- $list_aggregate := append $context.Values.__daemonset_list $context.Values.__current_label }} + {{- $_ := set $context.Values "__daemonset_list" $list_aggregate }} + {{- $_ := unset $context.Values "__current_label" }} + + {{- end }} + {{- end }} + {{- end }} + + {{/* scheduler exceptions for the default daemonset */}} + {{- $_ := set $context.Values.__default "matchExpressions" list }} + + {{- range $type, $type_data := . }} + {{/* Do not schedule to other specified labels */}} + {{- if eq $type "labels" }} + {{- range $lname, $ldata := . }} + {{ $label_name := (split "::" $lname)._0 }} + {{ $label_value := (split "::" $lname)._1 }} + + {{- $default_dict := dict "key" $label_name }} + {{- $_ := set $default_dict "values" (list $label_value) }} + {{- $_ := set $default_dict "operator" "NotIn" }} + + {{- $list_aggregate := append $context.Values.__default.matchExpressions $default_dict }} + {{- $_ := set $context.Values.__default "matchExpressions" $list_aggregate }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + + {{/* generate the default daemonset */}} + + {{/* set name */}} + {{- $_ := set $context.Values.__default "name" "default" }} + + {{/* no overrides apply, so copy as-is */}} + {{- $root_conf_copy1 := omit $context.Values.conf "overrides" }} + {{- $root_conf_copy2 := dict "conf" $root_conf_copy1 }} + {{- $context_values := omit $context.Values "conf" }} + {{- $root_conf_copy3 := mergeOverwrite $context_values $root_conf_copy2 }} + {{- $root_conf_copy4 := dict "Values" $root_conf_copy3 }} + {{- $_ := set $context.Values.__default "nodeData" $root_conf_copy4 }} + + {{/* add to global list */}} + {{- if $default_enabled }} + {{- $list_aggregate := append $context.Values.__daemonset_list $context.Values.__default }} + {{- $_ := set $context.Values "__daemonset_list" $list_aggregate }} + {{- end }} + + {{- range $current_dict := $context.Values.__daemonset_list }} + + {{- $context_novalues := omit $context "Values" }} + {{- $merged_dict := mergeOverwrite $context_novalues $current_dict.nodeData }} + {{- $_ := set $current_dict "nodeData" $merged_dict }} + {{/* Deep copy original daemonset_yaml */}} + {{- $daemonset_yaml := list $daemonset $configmap_name $serviceAccountName $merged_dict | include $daemonSetTemplateName | toString | fromYaml }} + {{- $_ := set $context.Values "__daemonset_yaml" ($daemonset_yaml | toYaml | fromYaml) }} + + {{/* Use the following name format $daemonset_root_name + sha256summ($current_dict.matchExpressions) + as labels might be too long and contain wrong characters like / */}} + {{- $_ := set $current_dict "dns_1123_name" dict }} + {{- $name_format := "" }} + {{- if eq $current_dict.name "default" }} + {{- $name_format = (printf "%s-%s" $daemonset_root_name "default") | replace "_" "-" }} + {{- else }} + {{- $name_format = (printf "%s-%s" $daemonset_root_name ($current_dict.matchExpressions | quote | sha256sum | trunc 16)) | replace "_" "-" }} + {{- end }} + {{- $_ := set $current_dict "dns_1123_name" $name_format }} + + {{/* set daemonset metadata name */}} + {{- if not $context.Values.__daemonset_yaml.metadata }}{{- $_ := set $context.Values.__daemonset_yaml "metadata" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.metadata.name }}{{- $_ := set $context.Values.__daemonset_yaml.metadata "name" dict }}{{- end }} + {{- $_ := set $context.Values.__daemonset_yaml.metadata "name" $current_dict.dns_1123_name }} + + {{/* cross-reference configmap name to container volume definitions */}} + {{- $_ := set $context.Values "__volume_list" list }} + {{- range $current_volume := $context.Values.__daemonset_yaml.spec.template.spec.volumes }} + {{- $_ := set $context.Values "__volume" $current_volume }} + {{- if hasKey $context.Values.__volume "secret" }} + {{- if eq $context.Values.__volume.secret.secretName $configmap_name }} + {{- $_ := set $context.Values.__volume.secret "secretName" (printf "%s-etc" $current_dict.dns_1123_name) }} + {{- end }} + {{- end }} + {{- if hasKey $context.Values.__volume "configMap" }} + {{- if eq $context.Values.__volume.configMap.name $configbin_name }} + {{- $_ := set $context.Values.__volume.configMap "name" (printf "%s-bin" $current_dict.dns_1123_name) }} + {{- end }} + {{- end }} + {{- $updated_list := append $context.Values.__volume_list $context.Values.__volume }} + {{- $_ := set $context.Values "__volume_list" $updated_list }} + {{- end }} + {{- $_ := set $context.Values.__daemonset_yaml.spec.template.spec "volumes" $context.Values.__volume_list }} + + + {{/* populate scheduling restrictions */}} + {{- if hasKey $current_dict "matchExpressions" }} + {{- $length := len $current_dict.matchExpressions }} + {{- if gt $length 0 }} + {{- if not $context.Values.__daemonset_yaml.spec.template.spec }}{{- $_ := set $context.Values.__daemonset_yaml.spec.template "spec" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.spec.template.spec.affinity }}{{- $_ := set $context.Values.__daemonset_yaml.spec.template.spec "affinity" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.spec.template.spec.affinity.nodeAffinity }}{{- $_ := set $context.Values.__daemonset_yaml.spec.template.spec.affinity "nodeAffinity" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution }}{{- $_ := set $context.Values.__daemonset_yaml.spec.template.spec.affinity.nodeAffinity "requiredDuringSchedulingIgnoredDuringExecution" dict }}{{- end }} + + {{- $expressions_modified := list }} + {{- if hasKey $context.Values.__daemonset_yaml.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution "nodeSelectorTerms" }} + {{- range $orig_expression := $context.Values.__daemonset_yaml.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms }} + {{- $match_expressions_modified := list }} + {{- $match_expressions_modified = concat $match_expressions_modified $current_dict.matchExpressions }} + {{- if hasKey $orig_expression "matchExpressions" }} + {{- $match_expressions_modified = concat $match_expressions_modified $orig_expression.matchExpressions }} + {{- $expressions_modified = append $expressions_modified (dict "matchExpressions" $match_expressions_modified) }} + {{- end }} + {{- end }} + {{- else }} + {{- $expressions_modified = (list (dict "matchExpressions" $current_dict.matchExpressions)) }} + {{- end }} + {{- $_ := set $context.Values.__daemonset_yaml.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution "nodeSelectorTerms" $expressions_modified }} + {{- end }} + {{- end }} + + {{/* input value hash for current set of values overrides */}} + {{- if not $context.Values.__daemonset_yaml.spec }}{{- $_ := set $context.Values.__daemonset_yaml "spec" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.spec.template }}{{- $_ := set $context.Values.__daemonset_yaml.spec "template" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.spec.template.metadata }}{{- $_ := set $context.Values.__daemonset_yaml.spec.template "metadata" dict }}{{- end }} + {{- if not $context.Values.__daemonset_yaml.spec.template.metadata.annotations }}{{- $_ := set $context.Values.__daemonset_yaml.spec.template.metadata "annotations" dict }}{{- end }} + {{- $cmap := list (printf "%s-etc" $current_dict.dns_1123_name) $current_dict.nodeData | include $configmap_include }} + {{- $cmap_bin := list (printf "%s-bin" $current_dict.dns_1123_name) $current_dict.nodeData | include $configbin_include }} + {{- $values_cmap_hash := $cmap | quote | sha256sum }} + {{- $values_cmap_bin_hash := $cmap_bin | quote | sha256sum }} + {{- $_ := set $context.Values.__daemonset_yaml.spec.template.metadata.annotations "configmap-etc-hash" $values_cmap_hash }} + {{- $_ := set $context.Values.__daemonset_yaml.spec.template.metadata.annotations "configmap-bin-hash" $values_cmap_bin_hash }} + + {{/* Do not set override for default daemonset */}} + {{- if $current_dict.daemonset_override }} + {{- $_ := set $context.Values.__daemonset_yaml.metadata.annotations "daemonset_override" $current_dict.daemonset_override }} + {{- end }} + +{{/* generate configmap */}} +--- +{{ $cmap }} + {{/* generate -bin yaml */}} +--- +{{ $cmap_bin }} + {{/* generate daemonset yaml */}} +--- +{{ $context.Values.__daemonset_yaml | toYaml }} + {{- end }} +{{- end }} diff --git a/releasenotes/notes/helm-toolkit.yaml b/releasenotes/notes/helm-toolkit.yaml index 95678e255..aff910652 100644 --- a/releasenotes/notes/helm-toolkit.yaml +++ b/releasenotes/notes/helm-toolkit.yaml @@ -81,4 +81,5 @@ helm-toolkit: - 0.2.72 Add snippet configmap_oslo_policy - 0.2.73 Add ability to get multiple hosts endpoint - 0.2.74 Remove trailing slash in endpoinds + - 0.2.75 Add daemonset_overrides_root util ...