-
Notifications
You must be signed in to change notification settings - Fork 12
/
shell.go
132 lines (115 loc) · 3.32 KB
/
shell.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
Copyright (C) 2021-2023, Kubefirst
This program is licensed under MIT.
See the LICENSE file for more details.
*/
package pkg
import (
"bufio"
"bytes"
"io"
"os"
"os/exec"
"strings"
"github.com/rs/zerolog/log"
)
// ExecShellReturnStrings Exec shell actions returning a string for use by the caller.
func ExecShellReturnStrings(command string, args ...string) (string, string, error) {
var outb, errb bytes.Buffer
k := exec.Command(command, args...)
// log.Info()().Msg()("Command:", k.String()) //Do not remove this line used for some debugging, will be wrapped by debug log some day.
k.Stdout = &outb
k.Stderr = &errb
err := k.Run()
if err != nil {
log.Error().Err(err).Msgf("error executing command")
}
if len(errb.String()) > 0 {
log.Error().Msgf("error executing command: %s", errb.String())
}
log.Info().Msgf("OUT: %s", outb.String())
log.Info().Msgf("Command: %s", command)
return outb.String(), errb.String(), err
}
// ExecShellReturnStringsV2 exec shell, discard stdout
func ExecShellReturnStringsV2(command string, args ...string) (string, error) {
var errb bytes.Buffer
k := exec.Command(command, args...)
// log.Info()().Msg()("Command:", k.String()) //Do not remove this line used for some debugging, will be wrapped by debug log some day.
k.Stdout = io.Discard
k.Stderr = &errb
err := k.Run()
if err != nil {
log.Error().Err(err).Msgf("error executing command")
}
if len(errb.String()) > 0 {
log.Error().Msgf("error executing command: %s", errb.String())
}
return errb.String(), err
}
// ExecShellWithVars Exec shell actions supporting:
// - On-the-fly logging of result
// - Map of Vars loaded
func ExecShellWithVars(osvars map[string]string, command string, args ...string) error {
log.Debug().Msgf("Debug: Running %s", command)
for k, v := range osvars {
os.Setenv(k, v)
suppressedValue := strings.Repeat("*", len(v))
log.Info().Msgf(" export %s = %s", k, suppressedValue)
}
cmd := exec.Command(command, args...)
cmdReaderOut, err := cmd.StdoutPipe()
if err != nil {
log.Error().Err(err).Msgf("failed creating out pipe for: %v", command)
return err
}
cmdReaderErr, err := cmd.StderrPipe()
if err != nil {
log.Error().Err(err).Msgf("failed creating out pipe for: %v", command)
return err
}
scannerOut := bufio.NewScanner(cmdReaderOut)
stdOut := make(chan string)
go reader(scannerOut, stdOut)
doneOut := make(chan bool)
scannerErr := bufio.NewScanner(cmdReaderErr)
stdErr := make(chan string)
go reader(scannerErr, stdErr)
doneErr := make(chan bool)
go func() {
for msg := range stdOut {
log.Info().Msgf("OUT: %s", msg)
}
doneOut <- true
}()
go func() {
// STD Err should not be supressed, as it prevents to troubleshoot issues in case something fails.
// On linux StdErr > StdOut by design in terms of priority.
for msg := range stdErr {
log.Warn().Msgf("ERR: %s", msg)
}
doneErr <- true
}()
err = cmd.Run()
if err != nil {
log.Error().Err(err).Msgf("command %q failed", command)
return err
} else {
close(stdOut)
close(stdErr)
}
<-doneOut
<-doneErr
return nil
}
// Not meant to be exported, for internal use only.
func reader(scanner *bufio.Scanner, out chan string) {
defer func() {
if r := recover(); r != nil {
log.Error().Msgf("Error processing logs from command. Error: %s", r)
}
}()
for scanner.Scan() {
out <- scanner.Text()
}
}