package docker import ( "errors" "fmt" "os" "os/exec" "path/filepath" ) type IClient interface { Save(imageName, dest string) (string, error) } var ErrDockerClientSave = errors.New("unable to save image into tar") // Client is a simple Docker client wrapping the local Docker daemon. // It does not use the Docker API but instead shell command and collect the output. // // NOTE: for now, it's ok, it only needs one command so, no need to add a fat dedicated // library with full Docker client API. type Client struct{} var _ IClient = (*Client)(nil) func NewClient() Client { return Client{} } // Save saves the `imageName` (tag included) in tar format in the target directory: `dest`. // The `dest` directory must exist with correct permissions. func (c *Client) Save(imageName, dest string) (string, error) { destInfo, err := os.Stat(dest) if err != nil { return "", fmt.Errorf("unable to stat file, dir=%s, err=%v", dest, err) } if !destInfo.IsDir() { return "", fmt.Errorf("dest file must be a directory, dir=%s, err=%v", dest, err) } tarFile := fmt.Sprintf("%s.tar", imageName) cmd := exec.Command("docker", "save", "-o", tarFile, imageName) cmd.Dir = dest if _, err := cmd.Output(); err != nil { return "", fmt.Errorf( "%w, dir=%s, image=%s, err=%v", ErrDockerClientSave, dest, imageName, err, ) } return filepath.Join(dest, tarFile), nil }