Search code examples
xmlkubernetesyamlazure-aksconfigmap

How to patch configmap in kubernetes which has xml within yaml?


we have requirement to patch this below yaml in azure kubernetes using kubectl cli

ConfigMap YAML:

kind: ConfigMap
apiVersion: v1
metadata:
  name: group-product-spec
  namespace: organization
  uid: 60694109-****-****-****-*****
  resourceVersion: '*******'
  creationTimestamp: '2024-02-14T10:35:56Z'
  managedFields:
    - manager: kubectl-client-side-apply
      operation: Update
      apiVersion: v1
      time: '2024-06-26T12:41:54Z'
      fieldsType: FieldsV1
      fieldsV1:
        f:data:
          .: {}
          f:config1.xml: {}
          f:config2.xml: {}
          f:config3.xml: {}
        f:metadata:
          f:annotations:
            .: {}
            f:kubectl.kubernetes.io/last-applied-configuration: {}
data:
  config1.xml: >-
    <Sheet>

    <Category name="Category1">

    <Item name="item1">Cat1Item1.value</Item>

    <Item name="item2">Cat1Item2.value</Item>

    </Category>

    <Category name="Category2">

    <Item name="item1">Cat2Item1.value</Item>

    <Item name="item2">Cat2Item2.value</Item>

    </Category>    
    
    <Category name="Category3">

    <Item name="item1">Cat3Item1.value</Item>

    <Item name="item2">Cat3Item2.value</Item>

    </Category>

    <Category name="Groups">

    <Item name="Group1" commarea="Y">G1.Value</Item>

    <Item name="Group2" commarea="Y">G2.Value</Item>

    <Item name="Group3" commarea="Y">G3.Value</Item>

    <Group1 name="G1.Value">

    <spec name="ISOS">

    <specdetails name="name1">spvalue1</specdetails>

    <specdetails name="name2">spvalue2</specdetails>

    <specdetails name="name3">spvalue3</specdetails>

    <specdetails name="name4">spvalue4</specdetails>

    </spec>
    
    </Group1>

    .

    .<Group2></Group2>
    
    .

    .<Group3></Group3>

    .


    <Category name="FinalProducts">

    <Item name="Product1">Product1FinalValue</Item>
    
    <Item name="Product2">Product2FinalValue</Item>
    
    <Item name="Product3">Product3FinalValue</Item>
    
    <Item name="Product4">Product4FinalValue</Item>

    
    </Category>

    </Sheet>
  config2.xml: >-
    <Sheet>

    <Category name="Category1">

    <Item name="item1">Cat1Item1.value</Item>

    <Item name="item2">Cat1Item2.value</Item>

    </Category>

    <Category name="Category2">

    <Item name="item1">Cat2Item1.value</Item>

    <Item name="item2">Cat2Item2.value</Item>

    </Category>    
    
    <Category name="Category3">

    <Item name="item1">Cat3Item1.value</Item>

    <Item name="item2">Cat3Item2.value</Item>

    </Category>

    <Category name="Groups">

    <Item name="Group1" commarea="Y">G1.Value</Item>

    <Item name="Group2" commarea="Y">G2.Value</Item>

    <Item name="Group3" commarea="Y">G3.Value</Item>

    <Group1 name="G1.Value">

    <spec name="ISOS">

    <specdetails name="name1">spvalue1</specdetails>

    <specdetails name="name2">spvalue2</specdetails>

    <specdetails name="name3">spvalue3</specdetails>

    <specdetails name="name4">spvalue4</specdetails>

    </spec>
    
    </Group1>

    .

    .<Group2></Group2>
    
    .

    .<Group3></Group3>

    .


    <Category name="FinalProducts">

    <Item name="Product1">Product1FinalValue</Item>
    
    <Item name="Product2">Product2FinalValue</Item>
    
    <Item name="Product3">Product3FinalValue</Item>
    
    <Item name="Product4">Product4FinalValue</Item>

    
    </Category>

    </Sheet>

Formatted XML within this YAML for better look

<Sheet>
    <Category name="Category1">
        <Item name="item1">Cat1Item1.value</Item>
        <Item name="item2">Cat1Item2.value</Item>
    </Category>
    <Category name="Category2">
        <Item name="item1">Cat2Item1.value</Item>
        <Item name="item2">Cat2Item2.value</Item>
    </Category>
    <Category name="Category3">
        <Item name="item1">Cat3Item1.value</Item>
        <Item name="item2">Cat3Item2.value</Item>
    </Category>
    <Category name="Groups">
        <Item name="Group1" commarea="Y">G1.Value</Item>
        <Item name="Group2" commarea="Y">G2.Value</Item>
        <Item name="Group3" commarea="Y">G3.Value</Item>
        <Group1 name="G1.Value">
            <spec name="ISOS">
                <specdetails name="name1">spvalue1</specdetails>
                <specdetails name="name2">spvalue2</specdetails>
                <specdetails name="name3">spvalue3</specdetails>
                <specdetails name="name4">spvalue4</specdetails>
            </spec>
        </Group1>

        .

        .
        <Group2 name="G2.Value"></Group2>

        .

        .
        <Group3 name="G3.Value"></Group3>

        .

        <Category name="FinalProducts">
            <Item name="Product1">Product1FinalValue</Item>
            <Item name="Product2">Product2FinalValue</Item>
            <Item name="Product3">Product3FinalValue</Item>
            <Item name="Product4">Product4FinalValue</Item>
        </Category>
    </Sheet>

I need to update one node item in this ConfigMap For example:

update the value from "Product3FinalValue" to "Not Available" in below X-path

data: config1.xml: Sheet1 > Category name="FinalProducts" > item name="Product3" > Product3FinalValue

This ConfigMap is saved as group-product-spec in AKS under the organization namespace

Commands used:

kubectl get cm **group-product-spec** -n **organization** -o yaml gives us this yaml

I think I need to use kubectl patch cm command but not sure how to use it.


Solution

  • From kubectl|Kubernetes' perspective, data contains a map of key:value pairs. In this case, the key is the filename and the value is the file's content. You know that the value content is XML but, for the tool, it is just string data.

    Your ConfigMap uses YAML block scalars (>-) to represent the content and I'm unsure how to enforce the encoding that this provides using JSON with kubectl patch (see below).

    1. kubectl edit

    It's not what you asked but (for the reason above), you could make these changes manually with:

    kubectl edit config/${NAME} \
    --namespace=${NAMESPACE}
    
    2. kubectl patch

    Here's a solution that use kubectl patch to PATCH the ConfigMap file content but -- to reiterate -- it loses escape characters (e.g. newlines \n):

    NAME="..." # ConfigMap
    NAMESPACE="..."
    
    FILE="config1.xml"
    
    # Export the XML from the ConfigMap
    kubectl get configmap/${NAME} \
    --namespace=${NAMESPACE} \
    --output=jsonpath="{.data.config1\.xml}" \
    > ${PWD}/${FILE}
    
    # Edit it
    
    VALUE="$(cat ${PWD}/${FILE})"
    
    PATCH="[
      {
        'op': 'replace',
        'path': '/data/${FILE}',
        'value': '${VALUE}'
      }
    ]"
    
    kubectl patch configmap/${NAME} \
    --namespace=${NAMESPACE} \
    --type=json \
    --patch="${PATCH}"
    
    3. kubectl create

    Another option which you don't include but may work for you is to simply delete and kubect create configmap ${NAME} --namespace=${NAMESPACE} --from-file=${PWD}/${FILE}.

    I'm interested to see what others propose.