github.com/openebs/api@v1.12.0/pkg/util/exec-run.go (about) 1 // Copyright © 2020 The OpenEBS Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "io/ioutil" 19 "os" 20 "os/exec" 21 "time" 22 23 "github.com/pkg/errors" 24 25 "context" 26 27 "k8s.io/klog" 28 ) 29 30 // Runner interface implements various methods of running binaries which can be 31 // modified for unit testing. 32 type Runner interface { 33 RunCombinedOutput(string, ...string) ([]byte, error) 34 RunStdoutPipe(string, ...string) ([]byte, error) 35 RunCommandWithTimeoutContext(time.Duration, string, ...string) ([]byte, error) 36 RunCommandWithLog(string, ...string) ([]byte, error) 37 } 38 39 // RealRunner is the real runner for the program that actually execs the command. 40 type RealRunner struct{} 41 42 // RunCombinedOutput runs the command and returns its combined standard output 43 // and standard error. 44 func (r RealRunner) RunCombinedOutput(command string, args ...string) ([]byte, error) { 45 //execute pool creation command. 46 cmd := exec.Command(command, args...) 47 out, err := cmd.CombinedOutput() 48 return out, err 49 } 50 51 // RunStdoutPipe returns a pipe that will be connected to the command's standard output 52 // when the command starts. 53 func (r RealRunner) RunStdoutPipe(command string, args ...string) ([]byte, error) { 54 cmd := exec.Command(command, args...) 55 56 stdout, err := cmd.StdoutPipe() 57 if err != nil { 58 klog.Errorf(err.Error()) 59 return []byte{}, err 60 } 61 if err := cmd.Start(); err != nil { 62 klog.Errorf(err.Error()) 63 return []byte{}, err 64 } 65 data, _ := ioutil.ReadAll(stdout) 66 if err := cmd.Wait(); err != nil { 67 klog.Errorf(err.Error()) 68 return []byte{}, err 69 } 70 return data, nil 71 } 72 73 // RunCommandWithLog triggers command passed as arguments and it also does the 74 // following things before command completion 75 // 1. Logs the stdout of command to stdout(standard output) 76 // 2. Logs stderr of the command to standard error 77 func (r RealRunner) RunCommandWithLog(command string, args ...string) ([]byte, error) { 78 // #nosec 79 cmd := exec.Command(command, args...) 80 // Redirect the command output to stdout 81 cmd.Stdout = os.Stdout 82 // Redirect the command output to stderr 83 cmd.Stderr = os.Stderr 84 // Start the command 85 if err := cmd.Start(); err != nil { 86 return []byte{}, err 87 } 88 // below will return error when command exit with return code 1 89 if err := cmd.Wait(); err != nil { 90 return []byte{}, err 91 } 92 return []byte{}, nil 93 } 94 95 // RunCommandWithTimeoutContext executes command provides and returns stdout 96 // error. If command does not returns within given timout interval command will 97 // be killed and return "Context time exceeded" 98 func (r RealRunner) RunCommandWithTimeoutContext(timeout time.Duration, command string, args ...string) ([]byte, error) { 99 ctx, cancel := context.WithTimeout(context.Background(), timeout) 100 defer cancel() 101 102 out, err := exec.CommandContext(ctx, command, args...).CombinedOutput() 103 if err != nil { 104 select { 105 case <-ctx.Done(): 106 return nil, errors.Wrapf(ctx.Err(), "Failed to run command: %v %v", command, args) 107 default: 108 return nil, err 109 } 110 } 111 return out, nil 112 } 113 114 //TestRunner is used as a dummy Runner 115 type TestRunner struct{} 116 117 // RunCombinedOutput is to mock Real runner exec. 118 func (r TestRunner) RunCombinedOutput(command string, args ...string) ([]byte, error) { 119 return []byte("success"), nil 120 } 121 122 // RunStdoutPipe is to mock real runner exec with stdoutpipe. 123 func (r TestRunner) RunStdoutPipe(command string, args ...string) ([]byte, error) { 124 return []byte("success"), nil 125 } 126 127 // RunCommandWithTimeoutContext is to mock Real runner exec. 128 func (r TestRunner) RunCommandWithTimeoutContext(timeout time.Duration, command string, args ...string) ([]byte, error) { 129 return []byte("success"), nil 130 } 131 132 // RunCommandWithLog is to mock real runner exec with stdoutpipe. 133 func (r TestRunner) RunCommandWithLog(command string, args ...string) ([]byte, error) { 134 return []byte("success"), nil 135 }