package dns import ( "bytes" "fmt" "io" "os" "os/exec" "strings" "sync" "k8s.io/klog/v2" ) func Execute(shell string, arg ...string) (bool, error) { var outb, errb bytes.Buffer cmd := exec.Command(shell, arg...) cmd.Dir = AcmeDir cmd.Stdout = &outb cmd.Stderr = &errb klog.Infof("cmd: %s\n", cmd.String()) err := cmd.Run() outstr := strings.TrimSuffix(outb.String(), "\n") errstr := strings.TrimSuffix(errb.String(), "\n") klog.Infof("out:\n%s\n", outstr) if err != nil { klog.Errorf("Script returned error:\nerr:\n") klog.Errorf("%s\n============\n", err) return false, err } if errb.String() != "" { klog.Infof("stderr:\n") klog.Errorf("%s\n============\n", errstr) return false, fmt.Errorf("stderr:\n%q", errstr) } klog.Infof("Script returned success\n") return true, nil } // https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html func Execute2(shell string, arg ...string) (bool, error) { var stdoutBuf, stderrBuf bytes.Buffer cmd := exec.Command(shell, arg...) cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) err := cmd.Run() if err != nil { klog.Errorf("Script returned error:\nerr:\n") klog.Errorf("%s\n============\n", err) return false, err } outStr, errStr := string(stdoutBuf.String()), string(stderrBuf.String()) fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr) klog.Infof("Script returned success\n") return true, nil } // CapturingPassThroughWriter is a writer that remembers // data written to it and passes it to w type CapturingPassThroughWriter struct { buf bytes.Buffer w io.Writer } // NewCapturingPassThroughWriter creates new CapturingPassThroughWriter func NewCapturingPassThroughWriter(w io.Writer) *CapturingPassThroughWriter { return &CapturingPassThroughWriter{ w: w, } } func (w *CapturingPassThroughWriter) Write(d []byte) (int, error) { w.buf.Write(d) return w.w.Write(d) } // Bytes returns bytes written to the writer func (w *CapturingPassThroughWriter) Bytes() []byte { return w.buf.Bytes() } func Execute3(shell string, arg ...string) (bool, error) { var errStdout, errStderr error cmd := exec.Command(shell, arg...) stdoutIn, _ := cmd.StdoutPipe() stderrIn, _ := cmd.StderrPipe() stdout := NewCapturingPassThroughWriter(os.Stdout) stderr := NewCapturingPassThroughWriter(os.Stderr) err := cmd.Start() if err != nil { klog.Fatalf("cmd.Start() failed with '%s'\n", err) } var wg sync.WaitGroup wg.Add(1) go func() { _, errStdout = io.Copy(stdout, stdoutIn) wg.Done() }() _, errStderr = io.Copy(stderr, stderrIn) wg.Wait() err = cmd.Wait() if err != nil { klog.Fatalf("cmd.Run() failed with %s\n", err) } if errStdout != nil || errStderr != nil { klog.Fatalf("failed to capture stdout or stderr\n") } //outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes()) //fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr) errStr := string(stderr.Bytes()) if stderr != nil { klog.Infof("err:\n%s\n", errStr) } return true, nil }