Skip to content

Instantly share code, notes, and snippets.

@ramizpolic
Last active July 5, 2023 21:09
Show Gist options
  • Save ramizpolic/235267cd10eb79c3cc121d9327f3d059 to your computer and use it in GitHub Desktop.
Save ramizpolic/235267cd10eb79c3cc121d9327f3d059 to your computer and use it in GitHub Desktop.
Running arbitrary scripts in Docker via Golang
package main
import (
"bufio"
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"io"
"os"
"path"
)
func main() {
dc, ctx := getClient(), context.Background()
// Exec data
image := "alpine:3.18.2"
script := `
#!/bin/sh
# Print to console
echo "First line in console"
echo "Second line in console"
echo -e "Console split\n\tLine"
# Pipe to a file
echo "First line in file" > /tmp/docker.file
echo "Second line in file" >> /tmp/docker.file
echo -e "File split\n\tLine" >> /tmp/docker.file
# e.g. Invoke some CLI here
# Fail on purpose, should be visible in status
exit 1
`
// Pull
pl, err := dc.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil {
panic(err)
}
_, _ = io.Copy(io.Discard, pl)
_ = pl.Close()
// Use unique dir to ensure idempotency between runs
localFilePath := path.Join(os.TempDir(), "unique-dir", "docker.file")
// Create
resp, err := dc.ContainerCreate(
ctx,
&container.Config{
Image: image,
Cmd: []string{"sh", "-c", script},
},
&container.HostConfig{
Binds: []string{
// It is possible to attach a file directly,
// but it has to exist on the host already.
// Otherwise, a dir will be created.
// We will attach a dir directly instead and keep it simple.
fmt.Sprintf("%s:/tmp", path.Dir(localFilePath)),
},
},
nil,
nil,
"",
)
if err != nil {
panic(err)
}
// Start
err = dc.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
if err != nil {
panic(err)
}
// Wait
statusCh, errCh := dc.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
select {
case status := <-statusCh:
fmt.Println("wait status: ", status)
case err := <-errCh:
panic(err)
}
// Get logs
out, err := dc.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
})
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(out)
for scanner.Scan() {
data := scanner.Text()
if len(data) <= 8 { // internal flags
continue
}
fmt.Println("log: ", string(data[8:]))
}
// Remove
err = dc.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{
RemoveVolumes: true,
RemoveLinks: false,
Force: true,
})
if err != nil {
panic(err)
}
// Print contents from a local file
err = printFile(localFilePath)
if err != nil {
panic(err)
}
}
func getClient() *client.Client {
dockerClient, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
panic(err)
}
dockerClient.NegotiateAPIVersion(context.Background())
return dockerClient
}
func printFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println("file: ", scanner.Text())
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment