This question already has answers here:
example of exec in k8s's pod by using go client
(3 answers)
Closed 11 months ago.
I am new to go and client-go :).
I saw this function where I can use it to send a command or exec to a pod with an interactive terminal. In the below, I need to know what parameters should I provide to call it from func main() as I can see it requires "config *restclient.Config" which I do not understand. Any example or a starting point ?
PS. I know how to create the clientset to authenticate using kubeconfig. just need to know what parameters this will require and how to call it from the main function.
package main
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"
)
//ExecCmdExample 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
}
I was able to resolve this. the question can be closed.
Edit:
you can look at the code here:
https://github.com/mohatb/kubego
Related
This question already has answers here:
fork/exec . no such file or directory exit status 1
(3 answers)
call 'go build' command from golang os.exec
(1 answer)
Why is this curl command not working?
(2 answers)
calling command with some arguments works but not with others but works from console
(1 answer)
How to pass a flag to a command in go lang?
(1 answer)
Closed 2 days ago.
This post was edited and submitted for review 2 days ago.
I am trying to run this command using golang
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
var (
host = "rtsp://localhost:8554/test"
projectName = "test"
gcpRegion = "test"
vertexStreams = "test"
)
// Run the system terminal command.
validFormattedString := fmt.Sprintln("vaictl", "-p", projectName, "-l", gcpRegion, "-c", "application-cluster-0 --service-endpoint visionai.googleapis.com send rtsp to streams", vertexStreams, "--rtsp-uri", host)
runSystemTerminalCommand(validFormattedString)
}
// Run a command in the system terminal.
func runSystemTerminalCommand(content string) {
cmd := exec.Command(content)
err := cmd.Run()
if err != nil {
log.Fatalln(err)
}
}
Error:
2023/02/17 21:02:16 fork/exec vaictl -p github-code-snippets -l us-central1 -c application-cluster-0 --service-endpoint visionai.googleapis.com send rtsp to streams dji-stream-0 --rtsp-uri rtsp://Administrator:Password#localhost:8554/test : no such file or directory
The application is installed in the system but i am still getting the error that "no such file or directory".
FIX:
This is the fix for the issue.
Everything must be passed on its own for it to work.
package main
import (
"log"
"os/exec"
)
func main() {
var (
host = "rtsp://Administrator:Password#localhost:8554/test"
projectName = "github-code-snippets"
gcpRegion = "us-central1"
vertexStreams = "dji-stream-0"
)
// Run the system terminal command.
cmd := exec.Command("vaictl", "-p", projectName, "-l", gcpRegion, "-c", "application-cluster-0", "--service-endpoint", "visionai.googleapis.com", "send", "rtsp", "to", "streams", vertexStreams, "--rtsp-uri", host)
err := cmd.Run()
if err != nil {
log.Fatalln(err)
}
}
I'm trying to execute a command (e.g. curl) in a pod and evaluate the stdout of that command on my client. This works perfectly fine on my machine, however not when executed in a GitLab pipeline. The command itself is executed in both cases, however in GitLab, the stdout of that command is not returned. I could verify that logCapture.Write() is never called and thus logCapture.GetStdOut() returns an empty string. Oddly enough, when I execute the program in GitLab using the Interactive Web Terminal, the program succeeds, i.e. stdout is returned to the client.
Hope someone has a good guess what the problem might be.
Here is the corresponding code:
func execCommandInPod(c *Client, pod corev1.Pod, cmd []string) (string, error) {
req := c.Clientset.CoreV1().RESTClient().Post().Resource("pods").Name(pod.Name).Namespace(pod.Namespace).SubResource("exec")
option := &corev1.PodExecOptions{
Command: cmd,
Stdin: true,
Stdout: true,
Stderr: true,
TTY: true,
}
req.VersionedParams(
option,
scheme.ParameterCodec,
)
exec, err := remotecommand.NewSPDYExecutor(c.Config, "POST", req.URL())
if err != nil {
return "", err
}
capture := &logCapture{}
errorCapture := &logCapture{}
err = exec.Stream(remotecommand.StreamOptions{
Stdin: os.Stdin,
Stdout: capture,
Stderr: errorCapture,
Tty: false,
})
cmdOutput := capture.GetStdOut()
return cmdOutput, err
}
type Client struct {
Config *restclient.Config
Clientset *kubernetes.Clientset
}
type logCapture struct {
buffer bytes.Buffer
}
func (capture *logCapture) GetStdOut() string {
log.Infof("Buffer length: %d", capture.buffer.Len())
return capture.buffer.String()
}
func (capture *logCapture) Write(p []byte) (n int, err error) {
log.Infof("Writing bytes of length %d.", len(p))
a := strings.TrimSpace(string(p))
capture.buffer.WriteString(a)
return len(p), nil
}
I am translating my script from bash to go and unfortunately I am not able to make pass command work. When I run the bash one I receive a window to provide my password to the pass manager.
Here is part of my go code:
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
)
func run_command(command *exec.Cmd) string {
attach_json, err := command.Output()
if err != nil {
fmt.Println(err.Error())
// os.Exit(0)
}
fmt.Println(attach_json)
return string(attach_json)
}
email := "xyz#abc.com"
cmd_emamil := "GoJira/api-token:" + email
pass_cmd := exec.Command("pass", cmd_emamil, "> /dev/null")
pass_cmd := exec.Command("pass", cmd_emamil)
run_command(pass_cmd)
In the shell command pass >/dev/null, >/dev/null is not an argument to pass; instead, it's an instruction to the shell, telling it to replace file descriptor 1 (stdout) with a handle on /dev/null before pass is started.
When you use exec.Command() there is no shell, so you can't use shell syntax. Instead, assign the file you want stdout to be redirected to to the Stdout of your exec.Command.
devnull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0755)
if err != nil { panic(err) }
cmd_email := "GoJira/api-token:" + email
pass_cmd := exec.Command("pass", cmd_email)
pass_cmd.Stdout = devnull
I want to source shell scripts using Go. Ideally the following code
cmd := exec.Command("/bin/bash", "source", file.Name())
but, I know that "source" is a bash built-in function, not an executable.
However, I have found some ways to mimic this behavior in Python:
http://pythonwise.blogspot.fr/2010/04/sourcing-shell-script.html
Unfortunately, I don't know how to translate this in Go. Does anyone have an idea ?
Thanks !
You can set environmental variables when running a program using exec:
cmd := exec.Command("whatever")
cmd.Env = []string{"A=B"}
cmd.Run()
If you really need source then you can run your command through bash:
cmd := exec.Command("bash", "-c", "source " + file.Name() + " ; echo 'hi'")
cmd.Run()
Check out this library for a more full-featured workflow: https://github.com/progrium/go-basher.
Update: Here's an example that modifies the current environment:
package main
import (
"bufio"
"bytes"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
)
func main() {
err := ioutil.WriteFile("example_source", []byte("export FOO=bar; echo $FOO"), 0777)
if err != nil {
log.Fatal(err)
}
cmd := exec.Command("bash", "-c", "source example_source ; echo '<<<ENVIRONMENT>>>' ; env")
bs, err := cmd.CombinedOutput()
if err != nil {
log.Fatalln(err)
}
s := bufio.NewScanner(bytes.NewReader(bs))
start := false
for s.Scan() {
if s.Text() == "<<<ENVIRONMENT>>>" {
start = true
} else if start {
kv := strings.SplitN(s.Text(), "=", 2)
if len(kv) == 2 {
os.Setenv(kv[0], kv[1])
}
}
}
}
log.Println(os.Getenv("FOO"))
I have recently added such a utility function to my shell/bash Golang library:
https://godoc.org/mvdan.cc/sh/shell#SourceFile
For example, you could do:
vars, err := shell.SourceFile("foo.sh")
if err != nil { ... }
fmt.Println(vars["URL"].Value)
// http://the.url/value
It's decently safe, because it never actually calls bash nor any other program. It parses the shell script, then interprets it. But when interpreting, it has a whitelist of what files the script can open and what programs the script can execute.
The interpreter also has a context.Context, so you can set a timeout if you want to be protected against forever loops or other bad code.
I am trying to figure out a way to execute a script (.sh) file from Golang. I have found a couple of easy ways to execute commands (e.g. os/exec), but what I am looking to do is to execute an entire sh file (the file sets variables etc.).
Using the standard os/exec method for this does not seem to be straightforward: both trying to input "./script.sh" and loading the content of the script into a string do not work as arguments for the exec function.
for example, this is an sh file that I want to execute from Go:
OIFS=$IFS;
IFS=",";
# fill in your details here
dbname=testDB
host=localhost:27017
collection=testCollection
exportTo=../csv/
# get comma separated list of keys. do this by peeking into the first document in the collection and get his set of keys
keys=`mongo "$host/$dbname" --eval "rs.slaveOk();var keys = []; for(var key in db.$collection.find().sort({_id: -1}).limit(1)[0]) { keys.push(key); }; keys;" --quiet`;
# now use mongoexport with the set of keys to export the collection to csv
mongoexport --host $host -d $dbname -c $collection --fields "$keys" --csv --out $exportTo$dbname.$collection.csv;
IFS=$OIFS;
from the Go program:
out, err := exec.Command(mongoToCsvSH).Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("output is %s\n", out)
where mongoToCsvSH can be either the path to the sh or the actual content - both do not work.
Any ideas how to achieve this?
For your shell script to be directly runnable you have to:
Start it with #!/bin/sh (or #!/bin/bash, etc).
You have to make it executable, aka chmod +x script.
If you don't want to do that, then you will have to execute /bin/sh with the path to the script.
cmd := exec.Command("/bin/sh", mongoToCsvSH)
This worked for me
func Native() string {
cmd, err := exec.Command("/bin/sh", "/path/to/file.sh").Output()
if err != nil {
fmt.Printf("error %s", err)
}
output := string(cmd)
return output
}
You need to execute /bin/sh and pass the script itself as an argument.
This allows you to pass the arguments as well as get the output of the script in the Stdout or Stderr.
import (
"github.com/sirupsen/logrus"
"os"
"os/exec"
)
func Execute(script string, command []string) (bool, error) {
cmd := &exec.Cmd{
Path: script,
Args: command,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
c.logger.Info("Executing command ", cmd)
err := cmd.Start()
if err != nil {
return false, err
}
err = cmd.Wait()
if err != nil {
return false, err
}
return true, nil
}
Calling example:
command := []string{
"/<path>/yourscript.sh",
"arg1=val1",
"arg2=val2",
}
Execute("/<path>/yourscript.sh", command)