package main
import (
"bytes"
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"io"
"log"
"time"
)
/*
library for executing commands in containers based on go
*/
type DockerClient struct {
timeout int // docker client request timeout
client *client.Client
}
// ExecResult exec result
type ExecResult struct {
StdOut string // std output
StdErr string // std error
ExitCode int // exit code
}
func NewDockerClient(timeout int) (*DockerClient, error) {
if timeout < 0 {
return nil, fmt.Errorf("negative numbers are not allowed")
}
if timeout == 0 {
timeout = 10
}
return &DockerClient{
timeout: timeout}, nil
}
// Open open docker cli
func (dc *DockerClient) Open() error {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation(), client.WithTimeout(time.Duration(dc.timeout)*time.Second))
if err != nil {
return err
}
dc.client = cli
return nil
}
// Close close docker cli
func (dc *DockerClient) Close() error {
if dc.client == nil {
return nil
}
return dc.client.Close()
}
// Exec exec command
func (dc *DockerClient) Exec(ctx context.Context, containerID string, command []string) (types.IDResponse, error) {
if dc.client == nil {
return types.IDResponse{
}, fmt.Errorf("docker cli has been closed")
}
config := types.ExecConfig{
AttachStdout: true,
AttachStderr: true,
Cmd: command,
}
return dc.client.ContainerExecCreate(ctx, containerID, config)
}
// InspectExecResp get exec command
func (dc *DockerClient) InspectExecResp(ctx context.Context, id string) (ExecResult, error) {
if dc.client == nil {
return ExecResult{
}, fmt.Errorf("docker cli has been closed")
}
var execResult ExecResult
resp, err := dc.client.ContainerExecAttach(ctx, id, types.ExecStartCheck{
})
if err != nil {
return execResult, err
}
defer resp.Close()
// read the output
var outBuf, errBuf bytes.Buffer
outputDone := make(chan error)
go func() {
// StdCopy demultiplexes the stream into two buffers
_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
outputDone <- err
}()
select {
case err := <-outputDone:
if err != nil {
return execResult, err
}
break
case <-ctx.Done():
return execResult, ctx.Err()
}
stdout, err := io.ReadAll(&outBuf)
if err != nil {
return execResult, err
}
stderr, err := io.ReadAll(&errBuf)
if err != nil {
return execResult, err
}
res, err := dc.client.ContainerExecInspect(ctx, id)
if err != nil {
return execResult, err
}
execResult.ExitCode = res.ExitCode
execResult.StdOut = string(stdout)
execResult.StdErr = string(stderr)
return execResult, nil
}
func main() {
containerID := "redis" // containerName or containerID
command1 := []string{
"ls", "-l"}
command2 := []string{
"uname", "-a"}
dockerClient, err := NewDockerClient(3)
if err != nil {
log.Fatal(err)
}
err = dockerClient.Open()
if err != nil {
log.Fatal(err)
}
defer func(dockerClient *DockerClient) {
err := dockerClient.Close()
if err != nil {
log.Fatal(err)
}
}(dockerClient)
idResponse1, err := dockerClient.Exec(context.Background(), containerID, command1)
if err != nil {
log.Fatal(err)
}
resp1, err := dockerClient.InspectExecResp(context.Background(), idResponse1.ID)
if err != nil {
log.Fatal(err)
}
log.Printf("%+v\n", resp1)
idResponse2, err := dockerClient.Exec(context.Background(), containerID, command2)
if err != nil {
log.Fatal(err)
}
resp2, err := dockerClient.InspectExecResp(context.Background(), idResponse2.ID)
if err != nil {
log.Fatal(err)
}
log.Printf("%+v", resp2)
}
基于go实现的在容器中执行命令的封装库
猜你喜欢
转载自blog.csdn.net/qq_36940806/article/details/134297638
今日推荐
周排行