Creating YAML for Kubernetes with Python3

4/7/2019

I am trying to create my Role YAML file for Kubernetes and I am getting stuck with this specific section of the needed YAML:

rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

I have tried to add it as a dictionary and then a list with a dictionary inside that for the -apiGroups line, but that then causes issues with the rest of the arguments for rules. I also am having issues with the [] showing up just like that when I am using yaml.dump even though I specify default_flow_style=False

def create_role_yml(role_filename, team_name, group_user):
    """
    https://kubernetes.io/docs/reference/
    access-authn-authz/rbac/#role-and-clusterrole
    """

    yml_file_kubernetes_data = dict(

        apiVersion='rbac.authorization.k8s.io/v1',
        kind='Role',
        metadata=dict(
            namespace=team_name,
            name=group_user,
            ),
        rules={
            [{'apiGroups':""}],
            'resourses': '[pods]',
            'verbs':'[get, watch, list]'}

        )

    with open(role_filename, 'w') as outfile:
        yaml.dump(yml_file_kubernetes_data, outfile, 
                  default_flow_style=False)

I would like to open the YAML and it to look exactly like the Kubernetes reference YAML:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

but I am getting the [ ] seperated, and no - for apiGroup. This is my result:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: pod-reader
rules:
apiGroups:
- "" # "" indicates the core API group
resources: 
- "pods"
verbs: 
-"get"
-"watch"
-"list"
-- Brett K.
kubernetes
python-3.x
pyyaml
yaml

2 Answers

4/7/2019

The rules are actually an array of maps, so you'll want to change it to look like this:

#!/usr/bin/env python

import yaml

yml_file_kubernetes_data = dict(

    apiVersion='rbac.authorization.k8s.io/v1',
    kind='Role',
    metadata=dict(
        namespace='something',
        name='group',
        ),
    rules=[
        {
            'apiGroups': '',
            'resources': [ 'pods' ],
            'verbs': [ 'get', 'watch', 'list' ],
        }
    ],
    )

with open('rbac.yml', 'w') as outfile:
    yaml.dump(yml_file_kubernetes_data, outfile,
              default_flow_style=False)

If you want to explicitly use the [] array notation, you'll need to use something like ruamel.yml - honestly, it's really not worth it though. This is valid YAML

-- jaxxstorm
Source: StackOverflow

4/7/2019

What you try to do is not possible with the normal parameters you can hand to PyYAML's dump(), which gives you only very course control using default_flow_style

  • True: everything is flow style (JSON like)
  • False: everything is block style
  • None: leaf collections are flow style, and the rest block style

You reference YAML has both block style leaf collections: the value for the key metadata, as well as flow style leaf collections: the value for the key verbs. Without hacking the representer you cannot achieve this in PyYAML.

The way more easy way to generate YAML in your particular form is by read-modify-write your expected YAML with a parser that knows how to keep the formatting. You can do that with ruamel.yaml, which is specifically developed to preserve these kind of things (disclaimer: I am the author of that package).

If your input file is input.yaml:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace:
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", 'list']

(the single entry under metadata is on purpose, but you could specify both, or none if you assign instead of update)

And the following program:

import sys
from pathlib import Path
import ruamel.yaml

yaml_str = """\
"""

in_file = Path("input.yaml")
out_file = Path("output.yaml")

team_name = "default"
group_user = "pod-reader"


yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
data = yaml.load(in_file)
data["metadata"].update(dict(namespace=team_name, name=group_user))
yaml.dump(data, out_file)

gives output.yaml:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", 'list']

Please note that apart from the block/flow style, also the single/double quotes from the original and the comment are preserved. Your indentation already matches the default, so that is not explicity set (yaml.indent(mapping=2, sequence=2, offset=0)).

-- Anthon
Source: StackOverflow