# Go Client - exec-ing a shel command in pod

**URL:** <https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354>\
**Category:** General Discussions\
**Created:** [March 7, 2019, 5:52pm UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354 "2019-03-07T17:52:49Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![stanislavkozlovski](https://avatars.discourse-cdn.com/v4/letter/s/dbc845/32.png) [@stanislavkozlovski](https://discuss.kubernetes.io/u/stanislavkozlovski)\
**Post date:** [March 7, 2019, 5:52pm UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354/1 "2019-03-07T17:52:50Z")

</div>

Hello community,

I have the following code, leveraging the Kubernetes go client for executing a shell command in a pod programmatically:

```
// ExecuteRemoteCommand executes a remote shell command on the given pod
// returns the output from stdout and stderr
func ExecuteRemoteCommand(pod *v1.Pod, command string) (string, string, error) {
	kubeCfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		clientcmd.NewDefaultClientConfigLoadingRules(),
		&clientcmd.ConfigOverrides{},
	)
	restCfg, err := kubeCfg.ClientConfig()
	if err != nil {
		return "", "", err
	}
	coreClient, err := corev1client.NewForConfig(restCfg)
	if err != nil {
		return "", "", err
	}

	buf := &bytes.Buffer{}
	errBuf := &bytes.Buffer{}
	request := coreClient.RESTClient().
		Post().
		Namespace(pod.Namespace).
		Resource("pods").
		Name(pod.Name).
		SubResource("exec").
		VersionedParams(&k8s_corev1.PodExecOptions{
			Command: []string{"/bin/sh", "-c", command},
			Stdin: true,
			Stdout: true,
			Stderr: true,
			TTY: true,
		}, scheme.ParameterCodec)
	exec, err := remotecommand.NewSPDYExecutor(restCfg, "POST", request.URL())
	err = exec.Stream(remotecommand.StreamOptions{
		Stdout: buf,
		Stderr: errBuf,
	})
	if err != nil {
		return "", "", errors.Wrapf(err, "Failed executing command %s on %v/%v", command, pod.Namespace, pod.Name)
	}

	return buf.String(), errBuf.String(), nil
}

```

I find there is some flakiness in this code - I sometimes get output from the shell command and I sometimes don’t. The command I’m using it `ls`, so it should always return some output.

`exec.Stream()` is synchronous, right?

Has anybody had any experience with similar functionality?

---

<div class="post-metadata">

**Author:** ![ted\_green](https://avatars.discourse-cdn.com/v4/letter/t/bc8723/32.png) [@ted\_green](https://discuss.kubernetes.io/u/ted_green)\
**Post date:** [March 8, 2019, 3:49am UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354/2 "2019-03-08T03:49:23Z")

</div>

I’ve faced same question, the reason I found should be this: when you input the cmd to execute in container/pod, you may lost ‘LF’ or ‘CR’, cmd cannot be execute, so there is no output.

So I changed input methods, just like Kubernetes/dashboard, using sockJs to send cmd to pod/container. sockJs will do the job to add ‘LF’ or ‘CR’, every cmd input would be a whole executable cmd, so we can get the correct output.

---

<div class="post-metadata">

**Author:** ![stanislavkozlovski](https://avatars.discourse-cdn.com/v4/letter/s/dbc845/32.png) [@stanislavkozlovski](https://discuss.kubernetes.io/u/stanislavkozlovski)\
**Post date:** [March 8, 2019, 12:44pm UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354/3 "2019-03-08T12:44:10Z")

</div>

> [@stanislavkozlovski](#):
>
> Stdin: true,

I realized what the problem in that piece of code was - it’s the stdin argument. Setting it to `false` fixed my issue.

---

<div class="post-metadata">

**Author:** ![jayunit100](https://sea2.discourse-cdn.com/flex016/user_avatar/discuss.kubernetes.io/jayunit100/32/4596_2.png) [@jayunit100](https://discuss.kubernetes.io/u/jayunit100)\
**Post date:** [February 16, 2020, 11:45pm UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354/4 "2020-02-16T23:45:50Z")

</div>

For me it seems like this snippet fails indeed on the SPDY negotiation part. Even with STDIN=true;

Any other ideas for a robust kubectl exec in golang?

When i looked into the code, this seems to ultimately return a ‘nil’ value

```auto
func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) {
	connectionHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderConnection))
	upgradeHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderUpgrade))
	if (resp.StatusCode != http.StatusSwitchingProtocols) || !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
		defer resp.Body.Close()
		responseError := ""
		responseErrorBytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("whoops ")
			responseError = "unable to read error from server response"
			fmt.Println(err, responseError)
		} else {
			// TODO: I don't belong here, I should be abstracted from this class

			if obj, _, err := statusCodecs.UniversalDecoder().Decode(responseErrorBytes, nil, &metav1.Status{}); err == nil {
				if status, ok := obj.(*metav1.Status); ok {
					fmt.Println("why are we returning nil????", *status, ok, obj)
					return nil, &apierrors.StatusError{ErrStatus: *status}
				}

```

I suppose that returning a nil value isnt ‘good’ but is somehow valid, and then something breaks downstream from this. Note im running client-go on ubuntu 18, and talking to a Kind cluster. Not sure if that somehow might result in  
SPDY behaving badly.

_Update_

So, after digging more , i saw that the NewConnection function in rounttrippers.go, actually returns

```auto
func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) {

```

The following:

```auto
enter &{404 Not Found 404 HTTP/1.1 1 1 map[Content-Length:[487] Content-Type:[application/json] Date:[Mon, 17 Feb 2020 04:06:04 GMT]] 0xc000552200 487 [] false false map[] <nil> <nil>}

```

Hence somehow the upgrade request is getting 404’d.

---

<div class="post-metadata">

**Author:** ![MatthewTromp](https://sea2.discourse-cdn.com/flex016/user_avatar/discuss.kubernetes.io/matthewtromp/32/9434_2.png) [@MatthewTromp](https://discuss.kubernetes.io/u/MatthewTromp)\
**Post date:** [January 31, 2022, 6:55pm UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354/5 "2022-01-31T18:55:04Z")

</div>

For what it’s worth, I had to modify Stanislav’s code to get it to work. I think there’s been some updates to the api since his answer or something. Here’s a version that works for me.

```auto
import (
	"bytes"
	"fmt"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/remotecommand"
)

func ExecuteRemoteCommand(pod *v1.Pod, command string) (string, string, error) {
	kubeCfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		clientcmd.NewDefaultClientConfigLoadingRules(),
		&clientcmd.ConfigOverrides{},
	)
	restCfg, err := kubeCfg.ClientConfig()
	if err != nil {
		return "", "", err
	}
	coreClient, err := kubernetes.NewForConfig(restCfg)
	if err != nil {
		return "", "", err
	}

	buf := &bytes.Buffer{}
	errBuf := &bytes.Buffer{}
	request := coreClient.CoreV1().RESTClient().
		Post().
		Namespace(pod.Namespace).
		Resource("pods").
		Name(pod.Name).
		SubResource("exec").
		VersionedParams(&v1.PodExecOptions{
			Command: []string{"/bin/sh", "-c", command},
			Stdin: false,
			Stdout: true,
			Stderr: true,
			TTY: true,
		}, scheme.ParameterCodec)
	exec, err := remotecommand.NewSPDYExecutor(restCfg, "POST", request.URL())
	err = exec.Stream(remotecommand.StreamOptions{
		Stdout: buf,
		Stderr: errBuf,
	})
	if err != nil {
		return "", "", fmt.Errorf("%w Failed executing command %s on %v/%v", err, command, pod.Namespace, pod.Name)
	}

	return buf.String(), errBuf.String(), nil
}

```

---

<div class="post-metadata">

**Author:** ![Kif11](https://sea2.discourse-cdn.com/flex016/user_avatar/discuss.kubernetes.io/kif11/32/10748_2.png) [@Kif11](https://discuss.kubernetes.io/u/Kif11)\
**Post date:** [September 2, 2022, 5:45am UTC](https://discuss.kubernetes.io/t/go-client-exec-ing-a-shel-command-in-pod/5354/6 "2022-09-02T05:45:32Z")

</div>

@MatthewTromp thank you for your example. That worked for me as well!
