Home > Software engineering >  Passing shell variable to command executed via kubectl exec
Passing shell variable to command executed via kubectl exec

Time:07-28

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:

  1. 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

  2. Once in the pod I execute cqlsh:
    cqlsh $(hostname -i) -u myuser
    and then enter password interactively

  3. 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...

CodePudding user response:

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

  • Related