I am trying to parse a file from the helm bundle which contains lots of jinja variables in it. When I try to read the file using ruamel.yaml python library it throws following exception:
----- Python Traceback -----
File "/Users/bhupesh.gupta/Projects/node-python-poc/yaml-read-file-script.py", line 16, in <module>
data = list(yaml.load_all(input))
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/main.py", line 451, in load_all
for d in self.load_all(fp):
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/main.py", line 461, in load_all
yield constructor.get_data()
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/constructor.py", line 115, in get_data
return self.construct_document(self.composer.get_node())
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 66, in get_node
return self.compose_document()
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 99, in compose_document
node = self.compose_node(None, None)
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 143, in compose_node
node = self.compose_mapping_node(anchor)
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 223, in compose_mapping_node
item_value = self.compose_node(node, item_key)
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 143, in compose_node
node = self.compose_mapping_node(anchor)
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 223, in compose_mapping_node
item_value = self.compose_node(node, item_key)
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 143, in compose_node
node = self.compose_mapping_node(anchor)
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/composer.py", line 216, in compose_mapping_node
while not self.parser.check_event(MappingEndEvent):
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/parser.py", line 146, in check_event
self.current_event = self.state()
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/parser.py", line 597, in parse_block_mapping_key
if self.scanner.check_token(KeyToken):
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/scanner.py", line 1794, in check_token
while self.need_more_tokens():
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/scanner.py", line 211, in need_more_tokens
self.stale_possible_simple_keys()
File "/opt/homebrew/lib/python3.9/site-packages/ruamel/yaml/scanner.py", line 360, in stale_possible_simple_keys
raise ScannerError(
ruamel.yaml.scanner.ScannerError: while scanning a simple key
in "<unicode string>", line 20, column 1:
<<{ toYaml . | indent 4 }}
^ (line: 20)
could not find expected ':'
in "<unicode string>", line 21, column 2:
#<{- end }}
Following is the file which I am trying to parse:
{{- $profileFilePath := printf "%s/%s%s" "dep_configs" .Values.global.networkFunction.profile ".yaml" -}}
{{- $isFileAvail := .Files.Glob $profileFilePath }}
{{- $nameprefixStr := . }}
{{- if $isFileAvail }}
{{- $profileValues := $.Files.Get $profileFilePath | fromYaml }}
{{- if not (empty .Values.global.k8sPlatform.namePrefix) }}
{{- $nameprefixStr = printf "%s-enodeb-%s-%s-%s" (lower $.Values.global.k8sPlatform.namePrefix) (lower $.Values.global.env.VRAN_ENB_ID) (lower $profileValues.networkFunction.nfType) (lower $.Values.global.env.VRAN_NF_ID) -}}
{{- else }}
{{- $nameprefixStr = printf "enodeb-%s-%s-%s" (lower $.Values.global.env.VRAN_ENB_ID) (lower $profileValues.networkFunction.nfType) (lower $.Values.global.env.VRAN_NF_ID) -}}
{{- end }}
---
apiVersion: v1
kind: Pod
metadata:
name: {{ $nameprefixStr }}-pod
namespace: {{ $.Release.Namespace }}
labels:
name: {{ $nameprefixStr }}-pod
{{- with $.Values.global.k8sPlatform.labels }}
{{ toYaml . | indent 4 }}
{{- end }}
annotations:
{{- /* ##### fill the annotation for K8S looping through nw list */}}
{{- $k8slistString := "" }}
{{- $releaseName := $.Release.Name }}
{{- range $.Values.cnfNetworks.cnfcNwList }}
{{- if or ( eq .cniPluginType "sriov") (eq .cniPluginType "sriov-dpdk") }}
{{- if not (empty $k8slistString) }}
{{- $k8slistString = printf "%s," $k8slistString }}
{{- end }}
{{- $intfAnnotation := printf "%s-%s@%s" $releaseName .cnfcNwType .cnfcNwType }}
{{- $k8slistString = printf "%s%s" $k8slistString $intfAnnotation }}
{{- end }}
{{- end }}
{{- if not (empty $k8slistString) }}
k8s.v1.cni.cncf.io/networks: {{ $k8slistString }}
{{- end }}
{{- /* ##### START fill the annotation for ROBIN case looping through nw list */}}
{{- $robinvarlistglobal := list }}
{{- $varlisttemp := list }}
{{- $dictvar := dict }}
{{- range $.Values.cnfNetworks.cnfcNwList }}
{{- if eq .cniPluginType "robin" }}
{{- if not (eq .cnfcNwType "fec") }}
{{- if (ne .ippool "") }}
{{- $dictlocal := dict "interface_name" .cnfcNwType "ippool" .ippool }}
{{- if not (empty .ipam) }}
{{- if not (empty .ipam.ip) }}
{{- $_ := set $dictlocal "static_ips" .ipam.ip }}
{{- end }}
{{- if not (empty .ipam.mtu) }}
{{- $_ := set $dictlocal "mtu" .ipam.mtu }}
{{- end }}
{{- end }}
{{- $robinvarlistglobal = append $varlisttemp $dictlocal }}
{{- $varlisttemp = $robinvarlistglobal }}
{{- end }}
{{- else }}
{{- /* ##### ROBIN FEC case - fill the devices string */}}
{{- $devicestr := .devicePool }}
robin.io/devices: '{{- $devicestr}}'
{{- end }}
{{- end }}
{{- end }}
{{- /* ##### END fill the annotation for ROBIN case looping through nw list */}}
{{- if not (empty $robinvarlistglobal) }}
robin.io/networks: '{{- $robinvarlistglobal | toJson}}'
{{- end }}
{{- if not (empty $.Values.global.k8sPlatform.nodeSelector) }}
{{- if not (empty $.Values.global.k8sPlatform.nodeSelector.rpoolName) }}
robin.io/robinrpool: {{ $.Values.global.k8sPlatform.nodeSelector.rpoolName }}
{{- end }}
{{- end }}
{{- range $.Values.global.cnfCorrelationInput }}
cnfcName: {{ .cnfcName }}
cnfCorrelationIds: {{ .cnfcCorrelationIds | toJson | quote }}
{{- end }}
spec:
{{- /* ##### Check if AR ,FEC is enabled ############# */}}
{{- $ar := 0 }}
{{- $fec := 0 }}
{{- $sriov := 0 }}
{{- $sriovResourceMap := dict }}
{{- range $.Values.cnfNetworks.cnfcNwList }}
{{- if and (eq .cnfcNwType "ar") (eq .cniPluginType "sriov") }}
{{- $ar = (add1 $ar) }}
{{- end }}
{{- if and (eq .cnfcNwType "fec") (eq .cniPluginType "sriov") }}
{{- $fec = (add1 $fec) }}
{{- end }}
{{- if (eq .cnfcNwType "sriov") }}
{{- $sriov = (add1 $sriov) }}
{{- end }}
{{- end }}
{{- /* ### Loop cnfclist for counting SRIOV interface based on resource nameto get resourcemap### */}}
{{- range $.Values.cnfNetworks.cnfcNwList }}
{{- if or (eq .cniPluginType "sriov") (eq .cniPluginType "sriov-dpdk") (eq .cniPluginType "sriov-fec") }}
{{- if hasKey $sriovResourceMap .resourceName }}
{{- $_ := set $sriovResourceMap .resourceName (add1 (get $sriovResourceMap .resourceName)) }}
{{- /* ### End of hasKey ### */}}
{{- else }}
{{- $_ := set $sriovResourceMap .resourceName 1 }}
{{- end }}
{{- /* ### End of hasKey ### */}}
{{- end }}
{{- /* ### End of cnfclist for couting SRIOV interface ### */}}
{{- end }}
containers:
- name: {{ $profileValues.networkFunction.containername }}
image: {{ $.Values.global.image.repository }}/{{ $profileValues.image.imageName }}:{{ $profileValues.image.imageTag }}
imagePullPolicy: {{ $profileValues.image.pullPolicy }}
{{- /* ### introduce a sleep of 10 for initializing vpp before starting app -NEEDED FOR ROBIN ### */}}
command: ["bash", "-c", "/opt/ani/helm/entrypoint.sh"]
#command: ["bash","-c","while true; do sleep 1000; done"]
envFrom:
- configMapRef:
name: {{ $nameprefixStr }}-configmap-env
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MY_CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: {{ .containerName }}
resource: requests.cpu
- name: MY_CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: {{ .containerName }}
resource: limits.cpu
- name: MY_MEM_REQUEST
valueFrom:
resourceFieldRef:
containerName: {{ .containerName }}
resource: requests.memory
- name: MY_MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: {{ .containerName }}
resource: limits.memory
volumeMounts:
#
# Volumes (common for CU & DU) = entrypoint , memfs, dskfs, provconfig ,hugepages , inject files
# CU specific - mainconfig
# DU specific - devices, ar specific
#
- mountPath: /opt/ani/helm/entrypoint.sh
subPath: entrypoint
name: entrypoint
- mountPath: /opt/ani/helm/inject-files.sh
subPath: inject-files
name: entrypoint
- mountPath: /memfs
name: memfs
- mountPath: /dskfs/
name: dskfs-fm
- mountPath: /prov-config
name: prov-config
- mountPath: /ipaddr-config
name: ipaddr-config
{{- if $.Values.global.networkFunction.main_config }}
- mountPath: /main-config
name: main-config
{{- end }}
- mountPath: /mnt/huge
name: hugepage
readOnly: false
- name: devices
mountPath: /sys/devices
readOnly: false
{{- if (eq $ar 1) }}
- mountPath: /dev/kni
name: kni
{{- end }}
{{- if not (empty $.Values.host_volumes) }}
{{- if $.Values.host_volumes.inject_files.large }}
{{- if $.Values.host_volumes.inject_files.large.hostpath }}
- mountPath: /inject-files-large
name: inject-files-large
{{- end }}
{{- end }}
{{- if $.Values.host_volumes.inject_files.small }}
{{- if $.Values.host_volumes.inject_files.small.files }}
- mountPath: /inject-files-small
name: inject-files-small
{{- end }}
{{- end }}
{{- end }} # end inject_files
resources:
requests:
{{- toYaml $profileValues.networkFunction.runtime.resources.requests | nindent 8 }}
{{- range $sriovResourceName,$sriovResourceCount := $sriovResourceMap }}
{{- printf "%s: %d" $sriovResourceName $sriovResourceCount | nindent 8 }}
{{- end }}
limits:
{{- toYaml $profileValues.networkFunction.runtime.resources.limits | nindent 8 }}
{{- range $sriovResourceName,$sriovResourceCount := $sriovResourceMap }}
{{- printf "%s: %d" $sriovResourceName $sriovResourceCount | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml $profileValues.networkFunction.runtime.securityContext | nindent 6 }}
livenessProbe:
exec:
command:
- /bin/bash
- -c
- /opt/ani/scripts/{{ $profileValues.networkFunction.runtime.liveness.script }}
initialDelaySeconds: {{ $profileValues.networkFunction.runtime.liveness.initialDelaySeconds }}
periodSeconds: {{ $profileValues.networkFunction.runtime.liveness.periodSeconds }}
failureThreshold: {{ $profileValues.networkFunction.runtime.liveness.failureThreshold }}
lifecycle:
preStop:
exec:
command: [/bin/sh,-c,/opt/ani/scripts/prestop_exec.sh]
terminationGracePeriodSeconds: {{ $profileValues.networkFunction.runtime.terminationGracePeriodSeconds }}
volumes:
- name: memfs
emptyDir:
medium: Memory
sizeLimit: {{ $profileValues.networkFunction.config.host_volumes.memfs_storage }}
- name: dskfs-fm
# Writes by application on a limited partition
persistentVolumeClaim:
claimName: {{ $nameprefixStr }}-pvc-dskfs
- name: prov-config
configMap:
name: {{ $nameprefixStr }}-configmap-provini
items:
- key: prov.ini
path: prov.ini
- name: ipaddr-config
configMap:
name: enodeb-{{ $.Values.global.env.VRAN_ENB_ID }}-{{ lower $profileValues.networkFunction.nfType }}-{{ $.Values.global.env.VRAN_NF_ID }}-configmap-ipaddr
items:
- key: ipaddr.ini
path: ip_addr.ini
- name: entrypoint
configMap:
name: {{ $nameprefixStr }}-configmap-entrypoint
defaultMode: 0777
{{- if $.Values.global.networkFunction.main_config }}
- name: main-config
configMap:
name: {{ $nameprefixStr }}-configmap-mainconfig
items:
- key: mainconfig
path: config.xml
- key: license
path: license.xml
{{- end }}
- name: hugepage
emptyDir:
medium: HugePages
- name: devices
# Required by dpdk
hostPath:
path: /sys/devices
{{- if (eq $ar 1) }}
- name: kni
# Required by kni
hostPath:
path: /dev/kni
type: CharDevice
{{- end }}
{{- if not ( empty $.Values.host_volumes) }}
{{- if $.Values.host_volumes.inject_files.large }}
{{- if $.Values.host_volumes.inject_files.large.hostpath }}
- name: inject-files-large
# Used for file-injection. Lab Feature for single node clusters
hostPath:
path: {{ $.Values.host_volumes.inject_files.large.hostpath }}
{{- end }}
{{- end }}
{{- if $.Values.host_volumes.inject_files.small }}
- name: inject-files-small
configMap:
name: {{ $nameprefixStr }}-configmap-inject-small-files
items:
{{- range $index, $file:= $.Values.host_volumes.inject_files.small.files }}
- key: {{ $file.source }}
path: {{ $file.source }}
{{- end }}
{{- end }} # inject_files.small
{{- end }} # inject_files
{{- if not ( empty $.Values.global.env.SECGW_INTERNAL_DNS) }}
dnsPolicy: "None"
dnsConfig:
nameservers:
- {{ $.Values.global.env.SECGW_INTERNAL_DNS }}
{{- end }}
{{- else }}
{{- fail "Deployment file does not exist" }}
{{- end }}
It contains 2 YAML documents within single file. Even if I remove the first one it still cannot parse (just for testing) although I need to complete file to be parsed.
There are multiple problems here, partly due to limitations in
ruamel.yaml.jinja2
.The error you indicate is created by the
{{ ... }}
pattern, that occurs on a line of its own, in what seems to be a jinja2 construct starting with{{- with
(which correctly gets commented out before loading).I have not been able to find documentation for that. The with statement in jinja2 is different, and without proper documentation I hesitate to update the ruamel.yaml.jinja2 extension. It also doesn't 100% match the
{{ with ....}}
/{{ end }}
of the go-templates (no dash after the curly braces.You should pre-process the data to get the offending line out of the way, by commenting out all the lines between
with
andend
(the latter at the same indentation level), but it would nice if this were documented, to give more confidence that is the right approach in general.The "trick" with writing the original document is necessary because
ruamel.yaml
cannot attach comments toNone
(as it cannot be subclassed in a meaningful way).A minor thing, that is probably not a issue is that most block style sequences have indentation of 2 with no offset, but the sequences that are the values for the keys
items
andnameservers
close to the end of the file have an indentation of 4 with an offset of 2.Flow style sequences (
[/bin/sh,-c,/opt/ani/scripts/prestop_exec.sh]
) get space after the comma.Apart from these minor "standardisations" that should not affect loading of the converted template , the output file differs only in the programmatically updated "my_node_name"