How to replace JSON value in kubectl output using go-template? - kubernetes

I have a configMap and I want to create a backup configMap by using the last applied configuration from that.
I use the following command to get the last applied configuration:
kubectl get cm app-map -n app-space \
-o go-template \
--template='{{index .metadata "annotations" "kubectl.kubernetes.io/last-applied-configuration"}}' > backup.json
It returns something like this [the content of backup.json]:
{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"app-map","creationTimestamp":null},"data":{"app.yml":"xxxxxxxxx","config.yml":"yyyyyyyyy"}}
Now, I want my backup configMap to have a different name. So, I want to change the .metadata.name from app-map to app-map-backup.
Is there a way I can achieve that with kubectl and -o go-template? I want to have the name changed before I write it to the backup.json file.
I know I can do that using jq but I do not have permission to install jq on the server where I am using kubectl.

you could use kubectl bulk plugin. The below command will replicate your config map
# get resource(s) and create with field(name) change
kubectl bulk configmap app-map -n app-space create name app-mapp-backup
Kubectl bulk is very powerful to use, I suggest to check samples.

You cannot do this just using kubectl. But there are other ways.
You can download statically linked jq binary from official jq website:
wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x jq-linux64
and then you can use this binary like following:
kubectl -o go-template [...] | ./jq-linux64 ...
or you can use sed:
kubectl -o go-template [...] | sed 's/"name":"app-map"/"name":"app-map-backup"/'

Related

no matches for kind "Kustomization" in version "kustomize.config.k8s.io/v1beta1"

Getting the below error for the command kubectl apply -n prod -f kustomize/kustomization.yaml
error: unable to recognize "kustomize/kustomization.yaml": no matches for kind "Kustomization" in version "kustomize.config.k8s.io/v1beta1"
Please advise.
Firstly I recommend to read official doc: kubernetes-kustomization.
To solve the problem use -k flag instead of -f flag in command:
$ kubectl apply -k <kustomization_directory>
If in your kustomize directory will be only one manifest file (kustomization.yaml) then use $ kubectl apply -k kustomize/ from this directory. Otherwise create new empty directory and put your kustomization.yaml there then execute following command from parent directory $ kubectl apply -k new-directory/
Take a look: kustomize-no-matches, kustomize-kubernetes-no-matches.

How to get a list of docker images given some kubernetes template?

To simplify, I want to list all the docker images defined in a helm chart.
For eg, let's say I have the following set of templates:
$ helm template jenkins/jenkins
https://charts.jenkins.io/
Then, I want to somehow use kubectl to parse this result so I can apply a filter such as:
kubectl get pods -l k8s-app=kube-dns -o jsonpath={.items[*].spec.containers[*].name}
To return me the list. However, that command is to get pods. Any clue?
EDIT: I found a way:
❯ helm template jenkins/jenkins | kubectl apply -f - --dry-run=client -o jsonpath="{..image}" | tr -s '[[:space:]]' '\n' | sort | uniq
bats/bats:1.2.1
jenkins/jenkins:2.263.1
kiwigrid/k8s-sidecar:0.1.275
With one drawback: kubectl needs to be connected to a cluster. I would like to prevent that.
You can use a different jsonpath to get all images:
kubectl get pods -A -o jsonpath="{..image}"
If you just want unique images: kubectl get pods -A -o jsonpath="{..image}" | tr -s '[[:space:]]' '\n' | sort -u.
Substituting -A for the namespace of your chart or manifests.
If you have the manifests on your machine and not deployed, of course, you can just grep: grep 'image: ' *.yml
You can also use Go template syntax:
kubectl get pods -A -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"
If you have more than one chart in a given namespace, I think grepping would be the best way: helm template some-chart | grep 'image:'
EDIT:
Since this will be running in CI, it would be better to use a little bit of code to avoid potential false positives. This Python script does the trick:
#!/usr/bin/env python3
import sys
import yaml # pip install PyYAML
from nested_lookup import nested_lookup # pip install nested-lookup
template = ""
for line in sys.stdin:
template += line
parts = template.split('---')
for p in parts:
y = yaml.safe_load(p)
matches = nested_lookup("image", y)
if (len(matches)):
print("\n".join(matches))
Usage: helm template jenkins/jenkins | ./this-script.py. It prints out each occurrence of images, so if you only want unique images you'd need to throw all the matches in a list, then unique that before printing (or pipe it to sort -u).

Simple command or environment variable to print the current namespace in openshift/kubernetes

Is there some command for this? It irks me that Openshift takes pride in having "-o yaml" and "-o json" commands to avoid having to use cut/grep/awk, but for listing the current project this seems to be the only way to do it:
[root#bart-master ~]# oc project
Using project "default" on server "https://api.bart.mycluster.com:6443".
[root#bart-master ~]# oc project | cut -d '"' -f2
default
You can get the current project(namespace) using each oc and kubectl CLIs as follows
$ oc config view --minify -o 'jsonpath={..namespace}'
$ kubectl config view --minify -o 'jsonpath={..namespace}'
The oc project CLI command already has this built in. You can pass the -q or --short arguments to oc project in order to get the namespace name alone.
In general, oc has great help support that you can get by appending -h to the end of any command (including oc project) to get helpful arguments like this.

diff between whats active on cluster versus kustomize

kustomize's docs provides a nice one-liner that compares two different overlays...
diff \
<(kustomize build $OVERLAYS/staging) \
<(kustomize build $OVERLAYS/production)
is there a way to do the same but against what is running within a specific kubernetes namespace and that of a defined overlay on disk?
more specifically, knowing what an kubectl apply -k . would do without actually doing it? using --dry-run just says spits out a list of the objects rather than a real diff.
kustomize build ./ | kubectl diff -f -
In Kustomize version 4.x.x
If you're looking for a way to do this visually, I highly recommend trying the Compare & Sync feature from Monokle:
In the picture above you can see an example where I'm comparing the output of the cluster-install kustomization to the objects in my minikube cluster.
You can easily determine which resources are missing in your cluster and which ones are different.
On top of that, you're not limited to only comparing kustomizations to clusters. You can also compare two clusters, two kustomizations, helm charts, etc.
I'm not sure if this is what you are looking for, but in Kubernetes you have kubectl diff.
It's nicely explained on APIServer dry-run and kubectl diff.
You can use option -k, --kustomize which does:
Process the kustomization directory. This flag can't be used together with -f or -R.
Or maybe something similar to one-liner to set context for specific namespace:
$ kubectl config set-context staging --user=cluster-admin --namespace=staging
$ kubectl config set-context prod --user=cluster-admin --namespace=prod
Once you have context setup you could use them maybe in a following way:
kubectl config use-context staging; cat patched_k8s.yaml | kubectl config use-context prod; kubectl diff -f -
This is just an example which I did not tested.
Try this kustomize command, currently in alpha:
KUSTOMIZE_ENABLE_ALPHA_COMMANDS=true kustomize resources diff -k your/kustomize/overlay
via https://kubernetes.slack.com/archives/C9A5ALABG/p1582738327027200?thread_ts=1582695987.023600&cid=C9A5ALABG
I have a small function on my shell config to do this:
kdiff() {
overlay="${1}"
kustomize build ${overlay} \
| kubectl diff -f - ${#:2} \
| sed '/kubectl.kubernetes.io\/last-applied-configuration/,+1 d' \
| sed -r "s/(^\+[^\+].*|^\+$)/$(printf '\e[0;32m')\1$(printf '\e[0m')/g" \
| sed -r "s/(^\-[^\-].*|^\-$)/$(printf '\e[0;31m')\1$(printf '\e[0m')/g"
}
It drops the last-applied-configuration annotation and adds some color.

Kubernetes: modify a secret using kubectl?

How can I modify the values in a Kubernetes secret using kubectl?
I created the secret with kubernetes create secret generic, but there does not seem to be a way to modify a secret. For example, to add a new secret-value to it, or to change a secret-value in it.
I assume i can go 'low-level', and write the yaml-file and do a kubectl edit but I hope there is a simpler way.
(I'm using kubernetes 1.2.x)
The most direct (and interactive) way should be to execute kubectl edit secret <my secret>. Run kubectl get secrets if you'd like to see the list of secrets managed by Kubernetes.
In case you prefer a non-interactive update, this is one way of doing it:
kubectl get secret mysecret -o json | jq '.data["foo"]="YmFy"' | kubectl apply -f -
Note that YmFy is a base64-encoded bar string. If you want to pass the value as an argument, jq allows you to do that:
kubectl get secret mysecret -o json | jq --arg foo "$(echo bar | base64)" '.data["foo"]=$foo' | kubectl apply -f -
I'm more comfortable using jq but yq should also do the job if you prefer yaml format.
As I found myself in the need of modifying a secret, I landed up here.
Here is the most convenient way I found for editing a (one-line) secret.
This elaborates on kubectl edit secret <my secret> of Timo Reimann above.
kubectl edit secret <my secret> will (in my case) invoke vi.
Now I move the cursor to the space after the colon of the secret I want to edit.
Then I press r and [enter] which will put the base64 encoded value onto a line of its own.
Now I enter :. ! base64 -D which will decode the current line.
After making my changes to the value, I enter :. ! base64 which will encode the changed value.
Pressing k [shift]J will rejoin the secret name and its new value.
:wq will write the new secretfile and quit vi.
P.S. If the secret has a multi-line value, switch on line numbers (:set nu) and, after changing the decoded value, use A,B ! base64 where A and B are the line numbers of the first and last line of the value.
P.P.S I just learned the hard way that base64 will receive the text to encode with an appended newline :( If this is no issue for your values - fine. Otherwise my current solution is to filter this out with: .!perl -pe chomp | base64
Deriving from 'Skeeves' answer:
Base64 encode your value:
echo -n 'encode_My_Password' | base64
Open the secret in edit mode:
kubectl edit secret my-secret
The default editor will open, replace the value of an exiting key or add a new line and a new key with the encoded value.
Save and close the file. The updated value or new key-value pair has now been added to the secret.
The easiest way from the command line:
echo "This is my secret" | base64 | read output;kubectl patch secret my_secret_name -p="{\"data\":{\"secret_key\": \"$output\"}}" -v=1
It will encode value This is my secret and update your my_secret_name secret by adding secret_key key and encoded values as a last key-value pair in that secret.
I implemented a kubectl plugin just for this.
To install using krew
kubectl krew update
kubectl krew install modify-secret
To run it
kubectl modify-secret xyz -n kube-system
Demo
The Easy Way : Delete and recreate the secret
After looking at all these answers, for my needs the best solution was to delete and recreate :
kubectl delete secret generic
kubectl create secret generic # or whatever ..
If you want to do it the hard way :
Using edit to change a docker-registry secret
I came to this question looking to modify a "docker-registry" style secret.
Simply editing it using kubectl edit secret seemed fraught as I didn't know what the secret value looked like.
I had created it with a command like kubectl create secret docker-registry generic-registry-secret --docker-server=docker.server --docker-username='my-cloud-usernname' --docker-password='my-auth-token' --docker-email='my#email.com'
I could have edited it, I figured out after looking at the other various answers here how that could be done - I'm including my notes here in case they help others.
List secrets : kubectl get secrets
Details of specific secret : kubectl describe secrets/generic-registry-secret
Get value of secret : kubectl get secret generic-registry-secret -o jsonpath={.data}
Decode secret value : First get everything between "map[.dockerconfigjson:" and "]" and then do :
echo "x9ey_the_secret_encoded_value_here_X0b3=" | base64 --decode
I could then take from that the specific auth token value I was seeking, and replace it with a new one. And then run that new full entire string through a | base 64 to get the base 64 encoding, and now I could finally, confidently, change the value by using kubectl edit secret generic-registry-secret and put in the new correct value.
But a delete and recreate is the simpler option.
References :
https://kubernetes.io/docs/concepts/configuration/secret/
https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/
Add a new key to an existing secret.
kubectl patch secret $SECRET_NAME --type=json \
-p='[{
"op" : "add" ,
"path" : "/data/'$KEY'" ,
"value" : "'$(base64 <<< "$VALUE")'"
}]'
Update an existing key in a secret
kubectl patch secret $SECRET_NAME --type=json \
-p='[{
"op" : "replace" ,
"path" : "/data/'$KEY'" ,
"value" : "'$(base64 <<< "$VALUE")'"
}]'
I was only able to find the replace operation in documentation, with no mention of the add operation. However, it looked like it was RFC 6902 compliant, so I tested with add and it works fine. I would expect other operations defined by RFC 6902 to work as well, though I haven't tested them.
The fastest way I found:
# You need a version of micro that includes this commit https://github.com/zyedidia/micro/commit/9e8d76f2fa91463be660737d1de3bff61258c90d
kubectl get secrets my-secret -o json | jq -r .data.config | base64 -d | micro | base64 -w 0 | xclip -selection clipboard && kubectl edit secrets my-secret
And using a bash function that you can put in your profile:
function ks-edit { kubectl -n $1 get secrets $2 -o json | jq -r '.data."'$3'"' | base64 -d | micro | base64 -w 0 | xclip -selection clipboard && kubectl -n $1 edit secrets $2; }
You can call it like this:
ks-edit <namespace> <secret> <key>
Before editing secrets with kubectl...
I would highly recommend on using k9s (not only for this purpose, but also as a lightweight k8s CLI management tool).
As you can see below (ignore all white rectangles), when your cluster's context is set on terminal you just type k9s and you will hit a nice terminal where you can inspect all cluster resources.
Just type ":" and enter the resource name (secrets in this case) which will appear in the middle of screen.
Then you can choose a secret with the up and down arrows and type e to edit it (green arrow):
By far the easiest way to do this is to mantain a local .env file for each of your secrets.
e.g
MY_SECRET=something
PASSWORD=anotherthing
Just run
kubectl create secret generic <name> --from-env-file=.env
And when you need to change it - just delete it and run the above command again.
No messing with base64
Always get the copy of secrets before editing it -
kubectl get secrets <your-secret-name> -n <namespace> -o yaml > mysecret.yaml
Now you can edit run edit command to edit your secret -
kubectl edit secrets <your-secret-name> -n <namespace>
or you can make copy of your mysecret.yaml file & exit the secrets inside that & run -
kubectl apply -f mysecret.yaml
Make sure you are decoding & encoding with base64 for viewing & adding secrets respectively.
Here's my one liner:
$ kubectl get secrets/my-secret -o yaml | yq '.dataStrings = (.data | map_values(#base64d)) | del(.data)' | vipe | yq '.data = (.dataStrings | map_values(#base64)) | del(.dataStrings)' | kubectl apply -f -
In case you're wondering how to do this with k9s, I am adding here instructions on how to do this step by step:
Install krew from here https://krew.sigs.k8s.io/docs/user-guide/setup/install/ (skip this step in case you have already it)
Install modify-secret plugin:
kubectl krew install modify-secret
Run the following command or add it to ~/.zshrc or ~/.bashrc:
export XDG_CONFIG_HOME=~/
Add the following to ~/k9s/plugin.yml
plugin:
edit-secret:
shortCut: Ctrl-X
confirm: false
description: "Edit Decoded Secret"
scopes:
- secrets
command: kubectl
background: false
args:
- modify-secret
- --namespace
- $NAMESPACE
- --context
- $CONTEXT
- $NAME