Search code examples
yamlpyyamlruamel.yaml

Updating the Yaml file, with new fields using ruamel


I am trying to update the yaml file using ruamel python.

proc=subprocess.Popen(['kubectl','get','pod','web3','-o','yaml','--export'], stdout=subprocess.PIPE)
rein=proc.stdout.read()
result, indent, block_seq_indent = ruamel.yaml.util.load_yaml_guess_indent(rein, preserve_quotes=True)

So far I have tried :

result['spec'].append('nodeSelector')

which gives ERROR :

result['spec'].append('nodeSelector')
AttributeError: 'CommentedMap' object has no attribute 'append'

Also tried like this :

result['spec']['nodeSelector']['kubernetes.io/hostname']='kubew1'

gives :

result['spec']['nodeSelector']['kubernetes.io/hostname']='kubew1'
File "/usr/local/lib/python3.6/dist-packages/ruamel/yaml/comments.py", line 752, in __getitem__
return ordereddict.__getitem__(self, key)
KeyError: 'nodeSelector'

My Intial Yaml File is :

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    app: demo
    name: web
  name: web3
  selfLink: /api/v1/namespaces/default/pods/web3
spec:
  containers:
  - image: aexlab/flask-sample-one
    imagePullPolicy: Always
    name: web
    ports:
    - containerPort: 5000
      name: http
      protocol: TCP
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-7bcc9
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true

And Expected fields I want to add inside 'spec' is :

  nodeSelector:
    kubernetes.io/hostname: kubew1

Any Ideas how to achieve this with ruamel library.


Solution

  • In your YAML file your root level collection is a mapping and the value for the key spec in that mapping is itself a mapping. Both of those mappings get loaded as dict-like objects using ruamel.yaml named CommentedMap.

    As with normal dicts you can add key-value pairs, deleted keys (and their values), and update values for a key, but there is no .append() method, as there is with a list (i.e. appending an extra item to a list).

    Your output is a bit terse, but of course you cannot just add nodeSelector to anything (list/sequence nor dict/mapping) and expect that to add kubernetes.io/hostname: kubew1 (a mapping in its own right) automatically.

    Your try of:

    result['spec']['nodeSelector']['kubernetes.io/hostname'] = 'kubew1'
    

    cannot work because there is no dict result['spec']['nodeSelector'] where you can add the key kubernetes.io/hostname.

    You would either first have to create a key with an emtpy dict as value:

    result['spec']['nodeSelector'] = {}
    result['spec']['nodeSelector']['kubernetes.io/hostname'] = 'kubew1'
    

    or do

    result['spec']['nodeSelector'] = {'kubernetes.io/hostname': 'kubew1'}
    

    Please note that the above has nothing much to do with ruamel.yaml, that is just basic Python data structure manipulation. Also note that there are over 100 libraries in the ruamel namespace, out of which ruamel.yaml is just one of several published as open source, so using ruamel is not very clear statement, although of course the context often provides enough information on which library you actually use.