Chronograph3r
Chronograph3r

Reputation: 125

How to Find and Replace using YQ in Shell script / Yaml?

I have around 200-300 yaml files lying around and What I am trying to achieve is to change only the image: option in few of my yaml files, I have a shell script which can get the required yaml files, and I just want to achieve changing the image: key only.

Solution Tried:

Used a tool yq v4:

for i in ${deployment[@]}
 do
 yq eval '( select(.spec.template.spec.containers[0].image |= "gcr.io/myrepo/mynginx:1.2.3"'  deployment-$i.yaml
done

What this command does is it achieves the desired result of changing the image, but it also appends the changes on the all the parts of deployment file which is for example at the end of every spec in the file:

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: portal-1
        spec:
          selector:
            matchLabels:
              component: portal-1
              role: ui
          replicas: 1
          template:
            metadata:
              labels:
                component: reactportal
                role: ui
            spec:
              containers:
                - name: portal
                  image: gcr.io/myrepo/mynginx:4.52 <<< Desired CHange Happens Here >>>
        ---
        
        apiVersion: extensions/v1beta1
        kind: Ingress
        metadata:
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$1
        nginx.ingress.kubernetes.io/enable-cors: "false"
        nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
      name: portal-ingress
    spec:
      rules:
        - host: mydomain.com
          http:
            paths:
              - backend:
                  serviceName: portal-svc
                  servicePort: 80
                path: /(.*)
 template:                                                  <<<< THIS IS ALSO GETTING APPENDED >>>
        spec:
          containers:
            - image: gcr.io/myrepo/mynginx:1.2.3

How to achieve the desired result without appending the image on every part of the deployment file?

Upvotes: 1

Views: 3750

Answers (2)

Philippe
Philippe

Reputation: 26537

This should get you the expected result :

for i in ${deployment[@]}; do
  yq eval '.spec|=select(.selector.matchLabels.component=="portal-1")
                 .template.spec.containers[0].image =
                    "gcr.io/myrepo/mynginx:1.2.3"' deployment-$i.yaml
done

Upvotes: 3

mario
mario

Reputation: 11098

Does it have to be yq ?

I would do it with a fairly simple bash one-liner, using sed:

for i in {1..3}; do sed -i "s/image.*/image: mysql:latest/" deployment-$i.yaml; done

The above command searches for the string "image" followed by any other characters till the very end of the line (so it takes the whole string image: "my-fancy-image-name") and replaces it with completely new string - in the above example the new string is image: mysql:latest.

The option -i or --in-place is used for editing files in place i.e. the original file is edited and saved with a new content.

The only asssumption is that you have a fixed naming convention for your deployment files i.e. the correct names for the above example are: deployment-1.yaml, deployment-2.yaml etc.

If you want to put your image name in double quotes as in your example, you can do it this way:

for i in {1..3}; do sed -i "s/image.*/image: \"gcr.io\/myrepo\/mynginx:1.2.3\"/" deployment-$i.yaml; done

As you can see, certain characters that have a special meaning for sed command like " or / have to be escaped with \ character so they can be used in your replacement string.

Actually there is one more assumption when using the above script, namely that the "image" string occurs exactly once in your Deployment. If this is the case, you don't need to worry about adding g option for global replacement (every occurance of the searched string). The above examples will perform the replacement only on the first occurence of the "image" string, which should satisfy your requirements. If you have deployments with more than one container e.g. an init container or sidecar, most likely you wouldn't be interested in changing every image in such deployment to the same single value anyway.

Upvotes: 2

Related Questions