How to pass env variable to a kubectl exec script call? - shell

How do I pass a environment variable into a kubectl exec command, which is calling a script?
kubectl exec client -n namespace -- /mnt/script.sh
In my script.sh, I need the value of the passed variable.
I tried:
kubectl exec client -n namespace -- PASSWORD=pswd /mnt/script.sh
which errors with:
OCI runtime exec failed: exec failed: unable to start container process: exec: "PASSWORD=pswd": executable file not found in $PATH: unknown

You can use env(1)
kubectl exec client -n namespace -- \
env PASSWORD=pswd /mnt/script.sh
or explicitly wrap the command in an sh(1) invocation so a shell processes it
kubectl exec client -n namespace -- \
sh -c 'PASSWORD=pswd /mnt/script.sh'
This comes with the usual caveats around kubectl exec: a "distroless" image may not have these standard tools; you're only modifying one replica of a Deployment; your changes will be lost as soon as the Pod is deleted, which can sometimes happen outside of your control.

Related

Passing shell variable to command executed via kubectl exec

I have a repetitive task that I do while testing which entails connecting to a cassandra pod and running a couple of CQL queries.
Here's the "manual" approach:
On cluster controller node, I exec a shell on the pod using kubectl:
kubectl exec pod/my-app-cassandra-pod-name -it --namespace myns -- /bin/bash
Once in the pod I execute cqlsh:
cqlsh $(hostname -i) -u myuser
and then enter password interactively
I execute my cql queries interactively
Now, I'd like to have a bash script to automate this. My intent is to run cqlsh directly, via kubectl exec.
The problem I have is that apparently I cannot use a shell variable within the "command" section of kubectl exec. And I will need shell variables to store a) the pod's IP, b) an id which is the input to my first query, and c) intermediate query results (the two latter ones are not added to script yet).
Here's what I have so far, using a dummy CQL query for now:
#!/bin/bash
CASS_IP=$(kubectl exec pod/my-app-cassandra-pod-name -it --namespace myns -- /usr/bin/hostname -i)
echo $CASS_IP # This prints out the IP address just fine, say 192.168.79.208
# The below does not work, errors provided below
kubectl exec pod/my-app-cassandra-pod-name -it --namespace myns -- /opt/cassandra/bin/cqlsh $CASS_IP -u myuser -p 'mypass' -e 'SELECT now() FROM system.local;'
# The below works just fine and returns the CQL query output
kubectl exec pod/my-app-cassandra-pod-name -it --namespace myns -- /opt/cassandra/bin/cqlsh 192.168.79.208 -u myuser -p 'mypass' -e 'SELECT now() FROM system.local;'
The output from the above is as follows, where IP is echoed, first exec'd cqlsh breaks, and second succeeds:
192.168.79.208
Warning: Timezone defined and 'pytz' module for timezone conversion not installed. Timestamps will be displayed in UTC timezone.
Traceback (most recent call last):
File "/opt/cassandra/bin/cqlsh.py", line 2357, in <module>
main(*read_options(sys.argv[1:], os.environ))
File "/opt/cassandra/bin/cqlsh.py", line 2326, in main
encoding=options.encoding)
File "/opt/cassandra/bin/cqlsh.py", line 463, in __init__
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]),
File "/opt/cassandra/bin/../lib/cassandra-driver-internal-only-3.25.0.zip/cassandra-driver-3.25.0/cassandra/policies.py", line 425, in __init__
File "/opt/cassandra/bin/../lib/cassandra-driver-internal-only-3.25.0.zip/cassandra-driver-3.25.0/cassandra/policies.py", line 426, in <listcomp>
File "/usr/lib64/python3.6/socket.py", line 745, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known
command terminated with exit code 1
Warning: Timezone defined and 'pytz' module for timezone conversion not installed. Timestamps will be displayed in UTC timezone.
system.now()
--------------------------------------
e78e75c0-0d3e-11ed-8825-1de1a1b1c128
(1 rows)
Any ideas how to get around this? I've been researching this for quite a while now, but I'm stuck...
This is a very, very FAQ: the kubectl exec is, as its name says, using exec(3) versus system(3) -- which in your case wouldn't work anyway because the $ in your kubectl exec would be interpreted by your shell not the pod's shell
but thankfully the solution is the same to both problems: create your own system(3) by wrapping the command in a sh -c invocation (or bash -c if you have bash-isms and bash is available inside the pod):
kubectl exec pod/my-app-cassandra-pod-name -it --namespace myns -- sh -c '/opt/cassandra/bin/cqlsh $(hostname -i) -u myuser -p "mypass" -e "SELECT now() FROM system.local;"'
as always, be cognizant of the "outer" versus "inner" quoting, especially if your "mypass" or the -e statement contains shell meta-characters

Kubernetes can't get bash prompt when exec into pod

I am using Kubernetes to exec into a pod like this:
kubectl exec myPod bash -i
which works fine, except I don't get a prompt. So then I do:
export PS1="myPrompt "
Which I would expect to give me a prompt, but doesn't. Is there some workaround for this?
Trying to exec into pod in interactive way requires specifying -ti option.
Where -i passes stdin to the container and -t connects your terminal to this stdin.
Take a look at the following example:
kubectl exec -it myPod -- bash

Executing a kubernetes pod with the use of pod name

I am writing a shell script for executing a pod for which the syntax is:
winpty kubectl --kubeconfig="C:\kubeconfig" -n namespace exec -it podname bash
This works fine but since podname is not stable and changes for every deployment so is there any alternative for this?
Thanks.
You can use normally $ kubectl exec command but define value for changing pod name.
Assuming that you have deployment and labeled pods: app=example, simply execute:
$ kubectl exec -it $(kubectl get pods -l app=example -o custom-columns=:metadata.name) -- bash
EDIT:
You can also execute:
POD_NAME = $(kubectl get pods -l app=example -o custom-columns=":metadata.name")
or
POD_NAME = $(kubectl get pods -l app=example -o jsonpath = "{. Items [0] .metadata.name}")
finally
$ winpty kubectl exec -ti $POD_NAME --bash
Make sure that you execute command in proper namespace - you can also add -n flag and define it.
You can use the following command:
kubectl -n <namespace> exec -it deploy/<deployment-name> -- bash
Add a service to your application:
As you know, pods are ephemeral; They come in and out of existence dynamically to ensure your application stays in compliance with your configuration. This behavior implements the scaling and self-healing aspects of kubernetes.
You application will consist of one or more pods that are accessible through a service , The application's service name and address does not change and so acts as the stable interface to reach your application.
This method works both if your application has one pod or many pods.
Does that help?

Kubectl: get a shell to a running container under Windows

I'm trying to log into running container using Kubectl, according to instructions in https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/, but I'm failing miserably:
kubectl exec -it mycontainer -- /bin/bash
Unable to use a TTY - input is not a terminal or the right kind of
file rpc error: code = 2 desc = oci runtime error: exec failed:
container_linux.go:247: starting container process caused "exec:
\"D:/Applications/Git/usr/bin/bash\": stat
D:/Applications/Git/usr/bin/bash: no such file or directory"
command terminated with exit code 126
It looks like kubectl tries to exec bash on my machine, which is totally not what I want to achieve.
I can exec commands without spaces:
$ kubectl exec mycontainer 'ls'
lib
start.sh
But with not:
$ kubectl exec mycontainer 'ls .'
rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:247: starting container process caused "exec: \"ls .\": executable file not found in $PATH"
command terminated with exit code 126
What I'm doing wrong?
I've tried both in mingw git shell , as with plain windows console.
Seems it might be related to this github issue.
One of the workarounds might be to use winpty as specified here.
winpty kubectl.exe exec -it pod-name -- sh
You can also try /bin/sh instead of /bin/bash it worked for me, but I do not have a Windows machine to check it in the same environment as you.
Below command worked for me to launch windows command prompt
kubectl exec -it mycontainer -- cmd.exe

How can I use bash constructs like 'cd' or '&&' or '>' redirection with ddev exec?

I'm trying to do some complex things with bash in the container using ddev exec and can't seem to get it to work. For example, ddev exec cd /var/tmp results in a big error message
Failed to execute command [cd /var/tmp]: Failed to run docker-compose [-f /Users/rfay/workspace/d8git/.ddev/docker-compose.yaml exec -T web cd /var/tmp], err='exit status 126', stdout='OCI runtime exec failed: exec failed: container_linux.go:348: starting container process caused "exec: \"cd\": executable file not found in $PATH": unknown
And trying to use "||" and "&&" or shell redirection with ">" doesn't work either.
Edit 2019-05-14: As of today's ddev release, v1.8.0, the answer below is obsolete, as ddev exec and exec hooks are executed in bash context. So ddev exec "ls | grep php" now works, ddev exec "mysql db <somefile.sql" works, as does an exec hook like exec: mysql <somefile.sql
ddev exec (and the "exec" hook in config.yaml) both execute actual comamnds, and not in the context of the shell. "cd" is not a Linux command, but rather a shell built-in. And '&&', '||', and '>' or '>>' are also shell constructs. So we have to do a bit of workaround to make them work.
But we can use bash explicitly to get these things to work:
ddev exec bash -c "cd /var/tmp && ls > /tmp/junk.txt"
To do the same thing in a post-start hook in config.yaml:
hooks:
post-start:
- exec: bash -c "cd /var/tmp && ls > /tmp/junk.txt"
Note that environment variables will not persist between exec statements, because they're in different shells, so it's best if you need to keep context to do it in one-liners.
Note also that if you want to redirect stdout/stderr you can redirect either within the container (as above) or to the host (redirecting the ddev exec output) like this:
ddev exec bash -c "cd /var/tmp && ls" >/tmp/junk.txt
It's possible that ddev exec might in the future execute commands in the context of bash to make this more transparent.

Resources