Skip to content

Instantly share code, notes, and snippets.

@LiFeAiR
Created June 14, 2022 15:53
Show Gist options
  • Save LiFeAiR/93cd7f9602b09047e66b95ed5d0560d6 to your computer and use it in GitHub Desktop.
Save LiFeAiR/93cd7f9602b09047e66b95ed5d0560d6 to your computer and use it in GitHub Desktop.
package exechelper
import (
"bytes"
"fmt"
"os/exec"
)
type DockerExecParams struct {
ContainerName string
Mounts map[string]string
CommandArgs []string
CmdImplementation execCmd
}
type execCmd interface {
Command(name string, arg ...string) *exec.Cmd
SetStdout(*bytes.Buffer)
SetStderr(*bytes.Buffer)
GetExitCode() int
Run() error
}
type ec struct {
*exec.Cmd
}
func (e *ec) Command(name string, arg ...string) *exec.Cmd {
e.Cmd = exec.Command(name, arg...)
return e.Cmd
}
func (e *ec) SetStdout(b *bytes.Buffer) {
e.Cmd.Stdout = b
}
func (e *ec) SetStderr(b *bytes.Buffer) {
e.Cmd.Stderr = b
}
func (e *ec) GetExitCode() int {
return e.Cmd.ProcessState.ExitCode()
}
func (e *ec) Run() error {
return e.Cmd.Run()
}
func dockerPullIfNotExists(params *DockerExecParams) error {
ci := params.CmdImplementation
ci.Command("docker", "image", "inspect", params.ContainerName)
var stdOutBytes, stdErrBytes bytes.Buffer
ci.SetStdout(&stdOutBytes)
ci.SetStderr(&stdErrBytes)
err := ci.Run()
if err != nil {
ci.Command("docker", "pull", params.ContainerName)
return ci.Run()
}
return nil
}
func DockerExec(params *DockerExecParams) (string, string, int, error) {
if params.CmdImplementation == nil {
params.CmdImplementation = &ec{}
}
err := dockerPullIfNotExists(params)
if err != nil {
return "", "", 1, fmt.Errorf("error pulling docker container %s: %w", params.ContainerName, err)
}
cmdArgs := []string{"run", "-i", "--rm"}
for mountPoint, mountPath := range params.Mounts {
cmdArgs = append(cmdArgs, "-v", mountPoint+":"+mountPath)
}
cmdArgs = append(cmdArgs, params.ContainerName)
cmdArgs = append(cmdArgs, params.CommandArgs...)
ci := params.CmdImplementation
ci.Command("docker", cmdArgs...)
var stdOutBytes, stdErrBytes bytes.Buffer
ci.SetStdout(&stdOutBytes)
ci.SetStderr(&stdErrBytes)
err = ci.Run()
return stdOutBytes.String(), stdErrBytes.String(), ci.GetExitCode(), err
}
package exechelper
import (
"bytes"
"os"
"os/exec"
"testing"
)
type mockCmdImplementation struct {
*exec.Cmd
}
func (e *mockCmdImplementation) Command(name string, arg ...string) *exec.Cmd {
e.Cmd = exec.Command(name, arg...)
return e.Cmd
}
func (e *mockCmdImplementation) SetStdout(b *bytes.Buffer) {
e.Cmd.Stdout = b
}
func (e *mockCmdImplementation) SetStderr(b *bytes.Buffer) {
e.Cmd.Stderr = b
}
func (e *mockCmdImplementation) GetExitCode() int {
return e.Cmd.ProcessState.ExitCode()
}
func (i *mockCmdImplementation) Run() error {
i.Cmd.ProcessState = &os.ProcessState{}
_, _ = i.Stdout.Write([]byte("hello"))
_, _ = i.Stdout.Write([]byte(""))
return nil
}
func NewMockCmdImplementation() *mockCmdImplementation {
return &mockCmdImplementation{}
}
func TestDockerExec(t *testing.T) {
type args struct {
params *DockerExecParams
}
tests := []struct {
name string
args args
want string
want1 string
want2 int
wantErr bool
}{
{"HelloWorld",
args{&DockerExecParams{
ContainerName: "hello-world",
CmdImplementation: &mockCmdImplementation{},
},
}, "hello", "", 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, got2, err := DockerExec(tt.args.params)
if (err != nil) != tt.wantErr {
t.Errorf("DockerExec() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("DockerExec() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("DockerExec() got1 = %v, want %v", got1, tt.want1)
}
if got2 != tt.want2 {
t.Errorf("DockerExec() got2 = %v, want %v", got2, tt.want2)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment