简体   繁体   中英

How to patch array data of k8s yaml file on condition using yq?

I have a project that need to update cloneset yaml

YAML document something like:

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  generation: 1
  ...
spec:
  ...
  ...
  volumeClaimTemplates:
  - metadata:
      creationTimestamp: null
      name: data1
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 60G
  - metadata:
      creationTimestamp: null
      name: data2
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 60G
status:
  availableReplicas: 1

I want to patch volumeClaimTemplates based on metadata.name

If name is not specified, storages for all volumeClaimTemplates are updated, and if name is specified and is matched, storages for a specific name are updated, if name is specified, but name don't match, return error

matchVolumeTemplateName=${1-all}
storage="$2"
if [[ ${matchVolumeTemplateName} == "all" ]]; then
  ./bin/yq e -i ".spec.volumeClaimTemplates[].spec.resources.requests.storage = \"${storage}\"" "cloneset_modifystorage.yml"
else
  ./bin/yq e -i ".spec.volumeClaimTemplates[] | select(.metadata.name == "\"${matchVolumeTemplateName}\"").spec.resources.requests.storage |= \"${storage}\"" "cloneset_modifystorage.yml"
fi

However, with the above code, only part of the YAML data will be output if a match is found for the specified name, and the file will be empty if name don't match, for example matchVolumeTemplateName=data3

# cloneset_modifystorage.yml
# matchVolumeTemplateName = data1   storage = 90G

# Other data of K8S YAML is lost
metadata:
  creationTimestamp: null
  name: data1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 90G

As a result, other data information about K8S is missing , and an empty file is printed instead of an error when a name match fails.

I really appreciate any help with this.

You can simplify things by passing shell variables to the yq command directly instead of interpolating them with double-quotes

match="${1-all}"
storage="$2"

if [[ $match == "all" ]]; then
    s="$storage" yq e '.spec.volumeClaimTemplates[].spec.resources.requests.storage = env(s)' cloneset_modifystorage.yml
else
    m="$match" s="$storage" yq e '(.spec.volumeClaimTemplates[] | select(.metadata.name == env(m)).spec.resources.requests.storage) |= env(s)' cloneset_modifystorage.yml
fi

The values m and s can be looked up the env() function directly inside the yq filter expression. The key logic in the else part would be to add the parens( ( ) ) around the filter to produce the resulting output at the whole root node level and not at the sub element level

Verify the output in either cases and add the -i flag for in-place replacement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM