Cannot run a kubectl command via shell using os.exec from within a Alpine container

9/4/2020

I'm trying to run some kubectl commands from within my container.

package main

import (
    "fmt"
    "os/exec"
)

	op, err := GetPods("test-containers", "qa")
	if err != nil {
		fmt.Printf("\nError: %v", err)
	}
	fmt.Printf(op)

func GetPods(name, namespace string) (podName string, err error) {
	fmt.Println("Get pod names ....")
	cmd := "kubectl get pods -n " + namespace + " -o wide | grep " + name + " | awk '{print $1}' "
	cmnd, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		fmt.Println("Failed to find pod name." + string(cmnd))
	}
	podName = string(cmnd)
	return
}

My multistage Dockerfile is based on Alpine3.6 and has the kubectl binaries installed.

FROM alpine:3.6

RUN apk add --update curl wget ca-certificates unzip python py-pip openssl bash && \
    apk --update add openssh-client && \
    apk --update add --virtual build-dependencies python-dev libffi-dev openssl-dev build-base && \
    apk add --no-cache --upgrade bash && \
    pip install --upgrade pip cffi && \
    pip install awscli && \
    wget  https://storage.googleapis.com/kubernetes-release/release/v1.15.1/bin/linux/amd64/kubectl && \
    chmod u+x kubectl && mv kubectl /bin/kubectl && \
    apk del build-dependencies && \
    rm -rf /var/cache/apk/*


ENV HOME=/go/app/
WORKDIR /go/app
COPY --from=go-container-build /go/app/ .
ENTRYPOINT ["bash"]

When i run the container as a pod and try to execute the command ; i'm always presented with the following error:

cmd:  kubectl -n qa get pods -o wide | grep test-containers | awk '{print $1}'
Failed to find pod name.exit status 2
/bin/sh: illegal option -

I have tried to use the os.exec.Command() like exec.Command("/bin/sh", "-c", cmd) as well as exec.Command("/bin/bash", "-c", cmd) but i've been getting similar errors.

Failed to find pod name.exit status 2
/bin/sh: illegal option -

I also tried cmnd := exec.Command(cmd) directly ; but i get the error : Failed to find pod name.fork/exec kubectl -n qa get pods -o wide | grep test-containers | awk '{print $1}':executable file not found in $PATH

I have installed bash in the container and i always get the ouput when i run the command directly on the container shell.

/go/app # kubectl get pods -n kube-system
NAME                                  READY   STATUS    RESTARTS   AGE
coredns-5946c5d67c-9l6hn              1/1     Running   0          10d
coredns-5946c5d67c-xh47v              1/1     Running   0          10d
kube-proxy-44lbd                      1/1     Running   0          3d20h
kube-proxy-6stz8                      1/1     Running   0          10d
kube-proxy-hdkmv                      1/1     Running   0          3d20h
kube-proxy-zxxfr                      1/1     Running   0          10d

i see the following shell in my container

/go/app # cat /etc/shells
# valid login shells
/bin/sh
/bin/ash
/bin/bash

I also have kubectl in the path.

/go/app # echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
/go/app # ls -ltr /bin/ | grep kubectl
-rwxr--r--    1 root     root      42989600 Jul 18  2019 kubectl
/go/app # ls -ltr /bin/ | grep sh
-r-xr-xr-x    1 root     root          6866 Apr 20  2017 bashbug
-rwxr-xr-x    1 root     root        682128 Apr 20  2017 bash
lrwxrwxrwx    1 root     root            12 Mar  6  2019 sh -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Mar  6  2019 fdflush -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Mar  6  2019 ash -> /bin/busybox

Can someone point out what is missing here and how do we solve the sh: illegal option - issue here.

-- nevosial
docker
go
kubernetes
shell

1 Answer

9/4/2020

/bin/sh: illegal option -

is because you have a space after the -c, which the arg parser interprets as /bin/sh "- " -- ironically(?) your golang snippet is correct, but the parts of your code you pasted and the crux of your question shows the wrong syntax

One can trivially reproduce this without involving busybox or whatever

package main
import (
    "fmt"
    "os/exec"
)
func main() {
    // c, err := exec.Command("/bin/sh", "-c", "echo -n hello | grep lo").Output()
    c, err := exec.Command("/bin/sh", "-c ", "echo -n hello | grep lo").Output()
    if err != nil {
        ee := err.(*exec.ExitError)
        panic(fmt.Errorf("c is %q and err is %q and stderr is %q", string(c), err, string(ee.Stderr)))
    }
    fmt.Println("OK")
}

results in panic: c is "" and err is "exit status 2" and stderr is "/bin/sh: - : invalid option\n and if one swaps the commented lines, it prints OK

-- mdaniel
Source: StackOverflow