Programatically apply a single resource from a multi resource Kubernetes YAML file - kubernetes

I have a file with three configmaps in it, like the one below.
apiVersion: v1
data:
TEST: "one"
kind: ConfigMap
metadata:
name: test-config-one
---
apiVersion: v1
data:
TEST: "two"
kind: ConfigMap
metadata:
name: test-config-two
---
apiVersion: v1
data:
TEST: "three"
kind: ConfigMap
metadata:
name: test-config-three
I'm trying to apply only test-config-three to the cluster. I know I can break that out into its own file and run kubectl apply -f test-config-three.yaml, but is there a way to do that without having to create a new file?
I was hoping to be able to do something like:
cat file.yml | yq <get only test-config-three> | kubectl apply -f -
But yq doesn't seem to support finding a single resource in a file. I also looked at tools like kubesplit but they tend to output all resources to separate files.
Is there a way to isolate and output a single resource from a yaml file containing multiple resources without creating a new file?
Update
Thanks to #Inian's answer below, I was able to get this full command working.
cat file.yml | yq e 'select(.data.TEST == "three")' - | kubectl apply -f -

There are two versions of yq implemented, one in Python and one in Go as I've highlighted in my answer at How can I parse a YAML file from a Linux shell script?
Using the Python version - kislyuk/yq
yq -y 'select(.data.TEST == "three")' yaml
Go version - mikefarah/yq
yq e 'select(.data.TEST == "three")' yaml

If you have python you can try following
export MYFILE=file.yml
export DOC_NUMBER=2
python3 -c "import yaml; print(yaml.dump(list(yaml.safe_load_all(open('"$MYFILE"')))["$DOC_NUMBER"]))" | kubectl apply -f -
yaml.safe_load_all(open('"$MYFILE"')) loads all the documents in the yaml file into a list. Then you are selecting "$DOC_NUMBER" document with ["$DOC_NUMBER"].
yaml.dump would dump the loaded object back into yaml format which is then printed.

Related

Set environment variable in kubernetes secret

When using Kubernetes .yml files, I can do the following:
$ cat configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
data:
foo: ${FOO}
bar: ${BAR}
static: doesNotChange
$ export FOO=myFooVal
$ export BAR=myBarVal
$ cat configmap.yml | envsubst | kubectl apply -f -
This would replace ${FOO} and ${BAR} in the configmap.yml file before actually applying the file to the cluster.
How could I achieve the very same behavior with a Kubernetes secret which has it's data values base64 encoded?
I would need to read all the keys in the data: field, decode the values, apply the environment variables and encode it again.
A tool to decode and encode the data: values inplace would be much appreciated.
It is actually possible, to store the secret.yml with stringData instead of data which allows to keep the files in plain text (SOPS encryption is still possible and encouraged)
$ cat secret.yml
apiVersion: v1
kind: Secret
metadata:
name: test-secret
namespace: default
type: Opaque
stringData:
dotenv: |
DATABASE_URL="postgresql://test:test#localhost:5432/test?schema=public"
API_PORT=${PORT}
FOO=${FOO}
BAR=${BAR}
$ export PORT=80
$ export FOO=myFooValue
$ export BAR=myBarValue
$ cat secret.yml | envsubst | kubectl apply -f -
A plus is for sure, that this not only allows for creation of the secret, but updating is also possible.
Just for documentation, here would be the full call with SOPS:
$ sops --decrypt secret.enc.yml | envsubst | kubectl apply -f -

kubectl create configmap - options to remove file names from output configmap when generateing using --file-name option

I am using kubectl create configmap command as follows:
kubectl create configmap config-multi-yaml-files --from-file=templates/1template.yaml --from-file=apps/app1.yaml --from-file=app2/app2.yaml --dry-run=true -o yaml > output.yaml
The resultant configmap do have file names (app1.yaml, app2.yaml) like this:
apiVersion: v1
data:
app1.yaml: |-
groups:
- name: sample
rules:
- alert: alert
How can I use this command so that I do have a configmap from multiple yamls, but do not have the respective file names in the resultant configmap.
Any pointers are appreciated.
Thanks.
Unfortunately, you will have to use keys in order to make it work. But here is an example of what you could do:
kubectl create configmap config-multi-yaml-files --from-file <(cat templates/1template.yaml) --from-file <(cat apps/app1.yaml) --dry-run=client -o yaml
That way the keys would be numbers: 11, 12, 13...
So you will not have to use file names in your ConfigMap but keys will have to be used.

How to edit configmap in kubernetes and override the values from a different yaml file?

I want to edit the configmap and replace the values. But it should be done using a different YAML in I ll specify overriding values as part of that file.
I was trying using kubectl edit cm -f replace.yaml but this didn't work so i want to know the structure in which the new file should be.
apiVersion: v1
kind: ConfigMap
metadata:
name: int-change-change-management-service-configurations
data:
should_retain_native_dn: "False"
NADC_IP: "10.11.12.13"
NADC_USER: "omc"
NADC_PASSWORD: "hello"
NADC_PORT: "991"
plan_compare_wait_time: "1"
plan_prefix: ""
ingress_ip: "http://10.12.13.14"
Now lets us assume NADC_IP should be changed and So I would like to know how should be structure of the YAML file and using which command it can be served?
The override taking place should only be during helm test for example when i run
helm test <release-name>?
kubectl replace -f replace.yaml
If you have a configmap in place like this:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
data:
should_retain_native_dn: "False"
NADC_IP: "10.11.12.13"
and you want to change the value of NADC_IP create a manifest file like this:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap
data:
should_retain_native_dn: "False"
NADC_IP: "12.34.56.78" # the new IP
and run kubectl replace -f replace.yaml
To update variable in configmap you need to take two steps:
First, update the value of variable:
kubectl create configmap <name_of_configmap> --from-literal=<var_name>=<new_value> -o yaml --dry-run | kubectl replace -f -
So in your case it will looks like this:
kubectl create configmap int-change-change-management-service-configurations --from-literal=NADC_IP=<new_value> -o yaml --dry-run | kubectl replace -f -
Second step, restart the pod:
kubectl delete pod <pod_name>
App will use new value from now. Let me know, if it works for you.
kubectl get cm {configmap name} -o=yaml --export > filename.yaml
You can try this it will give you yaml format
kubectl get configmap
int-change-change-management-service-configurations -o yaml
You can copy the content and replace it inside new yaml file and apply the changes
EDIT : 1
If you want to edit over terminal you can run
kubectl edit configmap {configmap name}
It will use vim editor and you can replace value from terminal using edit command.
EDIT : 2
kubectl get cm {configmap name} -o=yaml --export > filename.yaml

Kubernetes ConfigMap: import from file as many values instead of as one?

Creating a new ConfigMap from a file:
kubernetes create configmap foo --from-file=foo
This is how the ConfigMap looks internally:
kubernetes get configmaps foo -o yaml
apiVersion: v1
data:
foo: |
VAR1=value1
VAR2=value2
Then, I use this ConfigMap to create a set of environment variables in the container:
apiVersion: v1
kind: Pod
metadata:
labels:
name: app
name: app
spec:
containers:
- name: app-server
image: app:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: foo
command: ["/bin/bash", "-c", "printenv"]
When the container command runs, I see the following output for printenv:
foo=VAR1=value1
VAR2=value2
So, an echo $foo command in the pod returns:
VAR1=value1 VAR2=value2
According to the documentation for ConfigMap with --from-file, this is expected behaviour.
What would be a creative way (and the proper place) to somehow get the values of this file available to the pod as individual env variables VAR1, VAR2, VAR3, etc. ?
This is not possible with the current version (1.6.x) of Kubernetes. As written in the offical documentation for kubectl create configmap:
--from-file: Key file can be specified using its file path, in which case file basename will be used as configmap key, or optionally with a key and file path, in which case the given key will be used. Specifying a directory will iterate each named file in the directory whose basename is a valid configmap key.
When you want to create a configmap which is used like this, as input for the envFrom container configuration you could create it with the --from-literal option like this:
kubectl create configmap foo --from-literal=var1=value1 --from-literal=var2=value2
To still keep the file, you could transform your file into somethings which then runs this command like this:
eval "kubectl create configmap foo $(cat foo.txt | sed -e 's/^/--from-literal=/' | tr "\n" ' ')"
Along with that maybe checking the outstanding proposals like the --flatten flag proposal on Github are worth your time.
Also keep an eye on the variable naming. iirc VAR1 and VAR2 are not valid property names - they have to be lower case which might cause some issues when passing them on.

How to set secret files to kubernetes secrets by yaml?

I want to store files in Kubernetes Secrets but I haven't found how to do it using a yaml file.
I've been able to make it using the cli with kubectl:
kubectl create secret generic some-secret --from-file=secret1.txt=secrets/secret1.txt
But when I try something similar in a yaml:
apiVersion: v1
kind: Secret
metadata:
name: some-secret
type: Opaque
data:
secret1.txt: secrets/secret1.txt
I´ve got this error:
[pos 73]: json: error decoding base64 binary 'assets/elasticsearch.yml': illegal base64 data at input byte 20
I'm following this guide http://kubernetes.io/docs/user-guide/secrets/. It explains how to create a secret using a yaml but not how to create a secret from a file using yaml.
Is it possible? If so, how can I do it?
As answered on previous post, we need to provide the certificate/key encoded as based64 to the file.
Here is generic example for a certiticate (in this case SSL):
The secret.yml.tmpl:
apiVersion: v1
kind: Secret
metadata:
name: test-secret
namespace: default
type: Opaque
data:
server.crt: SERVER_CRT
server.key: SERVER_KEY
Pre-process the file to include the certificate/key:
sed "s/SERVER_CRT/`cat server.crt|base64 -w0`/g" secret.yml.tmpl | \
sed "s/SERVER_KEY/`cat server.key|base64 -w0`/g" | \
kubectl apply -f -
Note that the certificate/key are encoded using base64 without whitespaces (-w0).
For the TLS can be simply:
kubectl create secret tls test-secret-tls --cert=server.crt --key=server.key
You can use --dry-run flag to prepare YAML that contains data from your files.
kubectl create secret generic jwt-certificates --from-file=jwt-public.cer --from-file=jwt-private.pfx --dry-run=true --output=yaml > jwt-secrets.yaml
Edit
Thanks to #Leopd for comment about API deprecation, new kubectl uses this command:
kubectl create secret generic jwt-certificates --from-file=jwt-public.cer --from-file=jwt-private.pfx --dry-run=client --output=yaml > jwt-secrets.yaml
On my machine I still have old kubectl version
When using the CLI format basically you're using a generator of the yaml before posting it to the server-side.
Since Kubernetes is client-server app with REST API in between, and the actions need to be atomic, the posted YAML needs to contain the content of the file, and best way to do that is by embedding it as a base64 format in-line. It would be nice if the file could be otherwise embedded (indentation maybe could be used to create the boundaries of the file), but I haven't seen any example of such until now.
That being said, putting a file reference on the yaml is not possible, there is no pre-flight rendering of the yaml to include the content.
So I just learned a super useful k8s fundamental I missed, and then discovered it has a security vulnerability associated with it, and came up with a resolution.
TLDR:
You can have cleartext multiline strings/textfiles as secret.yaml's in your secret repo !!! :)
(Note I recommend storing this in Hashicorp Vault, you can store versioned config files that have secrets, and easily view/edit them through the vault webpage, and unlike a git repo, you can have fine grain access control, pipelines can use the REST API to pull updated secrets which makes password rotation mad easy too.)
cleartext-appsettings-secret.yaml
appsettings.Dummy.json is the default file name (key of the secret)
(I use the word default file name as you could override it in the yaml mount)
and the clear text json code is the file contents (value of the secret)
apiVersion: v1
kind: Secret
metadata:
name: appsettings
namespace: api
type: Opaque
stringData:
appsettings.Dummy.json: |-
{
"Dummy": {
"Placeholder": {
"Password": "blank"
}
}
}
When I
kubectl apply -f cleartext-appsettings-secret.yaml
kubectl get secret appsettings -n=api -o yaml
The secret shows up cleartext in the annotation...
apiVersion: v1
data:
appsettings.Dummy.json: ewogICJEdW1teSI6IHsKICAgICJQbGFjZWhvbGRlciI6IHsKICAgICAgIlBhc3N3b3JkIjogImJsYW5rIgogICAgfQogIH0KfQ==
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{},"name":"appsettings","namespace":"api"},"stringData":{"appsettings.Dummy.json":"{\n \"Dummy\": {\n \"Placeholder\": {\n \"Password\": \"blank\"\n }\n }\n}"},"type":"Opaque"}
creationTimestamp: 2019-01-31T02:50:16Z
name: appsettings
namespace: api
resourceVersion: "4909"
selfLink: /api/v1/namespaces/api/secrets/appsettings
uid: f0629027-2502-11e9-9375-6eb4e0983acc
Apparently the yaml used to create the secret showing up in the annotation is expected behavior for kubectl apply -f secret.yaml since 2016/has been posted as a bug report, but issue closed without resolution/they're ignoring it vs fixing it.
If you're original secret.yaml is base64'd the annotation will at least be base64'd but in this scenario it's straight up non-base64'd human readable clear text.
Note1: it doesn't happen with imperative secret creation
kubectl create secret generic appsettings --from-file appsettings.Dummy.json --namespace=api
Note2: Another reason for favoring the declarative appsettings-secret.yaml, is that when it's time to edit kubectl apply -f will configure the secret, but if you run that create command it'll say error already exists and you'll have to delete it, before it'll let you run the create command again.
Note3: A reason for kubectl create secret generic name --from-file file --namespace / a reason against secret.yaml is that kubectl show secret won't show you the last time the secret got edited. Where as with the create command, because you have to delete it before you can recreate it, you'll know when it was last edited based on how long it's existed for, so that's good for audit trial. (But there's better ways of auditing)
kubectl apply -f cleartext-appsettings-secret.yaml
kubectl annotate secret appsettings -n=api kubectl.kubernetes.io/last-applied-configuration-
kubectl get secret appsettings -n=api -o yaml
Counteracts the leak
apiVersion: v1
data:
appsettings.Dummy.json: ewogICJEdW1teSI6IHsKICAgICJQbGFjZWhvbGRlciI6IHsKICAgICAgIlBhc3N3b3JkIjogImJsYW5rIgogICAgfQogIH0KfQ==
kind: Secret
metadata:
creationTimestamp: 2019-01-31T03:06:55Z
name: appsettings
namespace: api
resourceVersion: "6040"
selfLink: /api/v1/namespaces/api/secrets/appsettings
uid: 43f1b81c-2505-11e9-9375-6eb4e0983acc
type: Opaque
You can use secode to replace secret values with base64 encoded strings, by simply doing:
secode secrets.yaml > secrets_base64.yaml
It encodes all data fields and works with multiple secrets (kind:Secret) per yaml file, when defined in a list (kind: List).
Disclaimer: I'm the author
For the Windows users in the room, use this for each of the .cer and .key (example shows the .key being encoded for insertion in to the YAML file):
$Content = Get-Content -Raw -Path C:\ssl-cert-decrypted.key
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Content)) | Out-File -FilePath C:\ssl-cert-decrypted.key.b64
Open the new .b64 file and paste the (single line) output in to your YAML file - be aware that if checking in the YAML file to a source code repo with this information in it, the key would effectively be compromised since base64 isn't encryption.