Search code examples
python-3.xkubernetesyamlpyyaml

Creating YAML for Kubernetes with Python3


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"

Solution

  • 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)).