Search code examples
pythonkubernetesyamlpyyamlconfigmap

How do I add a pipe the vertical bar (|) into a yaml file from Python


I have a task. I need to write python code to generate a yaml file for kubernetes. So far I have been using pyyaml and it works fine. Here is my generated yaml file:

apiVersion: v1
kind: ConfigMap
data:
  info: 
    name: hostname.com
    aio-max-nr: 262144
    cpu:
      cpuLogicalCores: 4
    memory:
      memTotal: 33567170560
    net.core.somaxconn: 1024
    ...

However, when I try to create this configMap the error is that info expects a string() but not a map. So I explored a bit and it seem the easiest way to resolve this is to add a pipe after info like this:

apiVersion: v1
kind: ConfigMap
data:
  info: | # this will translate everything in data into a string but still keep the format in yaml file for readability
    name: hostname.com
    aio-max-nr: 262144
    cpu:
      cpuLogicalCores: 4
    memory:
      memTotal: 33567170560
    net.core.somaxconn: 1024
    ...

This way, my configmap is created successfully. My struggling is I dont know how to add that pipe bar from python code. Here I manually added it, but I want to automate this whole process.

part of the python code I wrote is, pretend data is a dict():

content = dict()
content["apiVersion"] = "v1"
content["kind"] = "ConfigMap"
data = {...}
info = {"info": data}
content["data"] = info

# Get all contents ready. Now write into a yaml file
fileName = "out.yaml"
with open(fileName, 'w') as outfile:
    yaml.dump(content, outfile, default_flow_style=False)   

I searched online and found a lot of cases, but none of them fits my needs. Thanks in advance.


Solution

  • The pipe makes the contained values a string. That string is not processed by YAML, even if it contains data with YAML syntax. Consequently, you will need to give a string as value.

    Since the string contains data in YAML syntax, you can create the string by processing the contained data with YAML in a previous step. To make PyYAML dump the scalar in literal block style (i.e. with |), you need a custom representer:

    import yaml, sys
    from yaml.resolver import BaseResolver
    
    class AsLiteral(str):
      pass
    
    def represent_literal(dumper, data):
      return dumper.represent_scalar(BaseResolver.DEFAULT_SCALAR_TAG,
          data, style="|")
    
    yaml.add_representer(AsLiteral, represent_literal)
    
    info = {
      "name": "hostname.com",
      "aio-max-nr": 262144,
      "cpu": {
        "cpuLogicalCores": 4
      }
    }
    
    info_str = AsLiteral(yaml.dump(info))
    
    data = {
      "apiVersion": "v1",
      "kind": "ConfigMap",
      "data": {
        "info": info_str
      }
    }
    
    yaml.dump(data, sys.stdout)
    

    By putting the rendered YAML data into the type AsLiteral, the registered custom representer will be called which will set the desired style to |.