27

I want to use k8s go client to exec command in a pod. However I cannot find any example about this. So I read kubectl exec source code, and write code as below. And err = exec.Stream(sopt) always get an error without any message. Can anyone tell me how to debug this problem, or give me a correct example.

config := &restclient.Config{
    Host:     "http://192.168.8.175:8080",
    Insecure: true,
}

config.ContentConfig.GroupVersion = &api.Unversioned
config.ContentConfig.NegotiatedSerializer = api.Codecs

restClient, err := restclient.RESTClientFor(config)
if err != nil {
    panic(err.Error())
}

req := restClient.Post().Resource("pods").Name("wordpress-mysql-213049546-29s7d").Namespace("default").SubResource("exec").Param("container", "mysql")
req.VersionedParams(&api.PodExecOptions{
    Container: "mysql",
    Command:   []string{"ls"},
    Stdin:     true,
    Stdout:    true,
}, api.ParameterCodec)

exec, err := remotecommand.NewExecutor(config, "POST", req.URL())
if err != nil {
    panic(err.Error())
}
sopt := remotecommand.StreamOptions{
    SupportedProtocols: remotecommandserver.SupportedStreamingProtocols,
    Stdin:              os.Stdin,
    Stdout:             os.Stdout,
    Stderr:             os.Stderr,
    Tty:                false,
}

err = exec.Stream(sopt)
if err != nil {
    panic(err.Error())
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129
JJBoooom
  • 293
  • 1
  • 3
  • 5

3 Answers3

14
package k8s

import (
    "io"

    v1 "k8s.io/api/core/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/kubernetes/scheme"
    restclient "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/remotecommand"
)

// ExecCmd exec command on specific pod and wait the command's output.
func ExecCmdExample(client kubernetes.Interface, config *restclient.Config, podName string,
    command string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
    cmd := []string{
        "sh",
        "-c",
        command,
    }
    req := client.CoreV1().RESTClient().Post().Resource("pods").Name(podName).
        Namespace("default").SubResource("exec")
    option := &v1.PodExecOptions{
        Command: cmd,
        Stdin:   true,
        Stdout:  true,
        Stderr:  true,
        TTY:     true,
    }
    if stdin == nil {
        option.Stdin = false
    }
    req.VersionedParams(
        option,
        scheme.ParameterCodec,
    )
    exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
    if err != nil {
        return err
    }
    err = exec.Stream(remotecommand.StreamOptions{
        Stdin:  stdin,
        Stdout: stdout,
        Stderr: stderr,
    })
    if err != nil {
        return err
    }

    return nil
}

It works to me.

noanswer
  • 171
  • 1
  • 7
2
Command: []string{"/bin/sh", "-c", "ls", "-ll", "."}

The array of strings should start with /bin/sh (the path to the shell executable).

Then -c flag can be added as the second element to indicate that the subsequent string will be interpreted as a command to be executed by the shell.

Finally, any additional arguments for the command can be added as subsequent elements in the array.

Yasha
  • 1,017
  • 11
  • 21
  • This example is incorrect: the `-c` argument accepts a single string; any additional arguments are passed as arguments to the included script. This would simply run the `ls` command and ignore the other arguments (try running, on your command line, `sh -c "ls" "-ll" ".") – larsks Aug 05 '23 at 17:34
  • @larsks have you tried running the above within k8s Go client, and it didn't work for you? I understand that this would normally not work in other contexts, for example when defining this as a k8s deployment command. However, when setting the command for `PodExecOptions` in k8s Go client, the above essentially gets converted to `kubectl exec -- /bin/sh -c ls -ll .`, which executes just fine. Even if you were to run `kubectl exec -- "/bin/sh" "-c" "ls" "-ll" "."`, or as you have it `kubectl exec -- /bin/sh -c "ls" "-ll" "."`, we still get the correct output – Yasha Aug 10 '23 at 21:03
1

you miss .CoreV1() when create request!

zhongfox
  • 19
  • 2
  • Yeap, had the same issue recently. Fixed by adding `.CoreV1()` when creating the request... `req := restClient..CoreV1().Post().Resource("pods")...` – Tony Feb 26 '20 at 22:42