docker-save-cli/internal/docker/docker.go

91 lines
2.0 KiB
Go

package docker
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func SaveImageTar(ctx context.Context, image, path, name string, doPull, rm bool) error {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return fmt.Errorf("docker client error: %v", err)
}
defer cli.Close()
if doPull {
if err := pullImage(ctx, cli, image); err != nil {
return fmt.Errorf("pull image %s failed: %v", image, err)
}
}
if err := saveToTar(ctx, cli, image, path, name); err != nil {
return fmt.Errorf("save image %s failed: %v", image, err)
}
if rm {
if err := rmImage(ctx, cli, image); err != nil {
return fmt.Errorf("remove image %s failed: %v", image, err)
}
}
return nil
}
func pullImage(ctx context.Context, cli *client.Client, image string) error {
// try pull image
r, err := cli.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil {
return fmt.Errorf("pull image %s failed: %v", image, err)
}
defer r.Close()
io.Copy(os.Stdout, r)
return nil
}
func rmImage(ctx context.Context, cli *client.Client, image string) error {
_, err := cli.ImageRemove(ctx, image, types.ImageRemoveOptions{})
if err != nil {
return err
}
return nil
}
func saveToTar(ctx context.Context, cli *client.Client, image, path, name string) error {
out, err := cli.ImageSave(ctx, []string{image})
if err != nil {
return fmt.Errorf("save image %s failed: %v", image, err)
}
defer out.Close()
filename := name
if filename == "" {
strs := strings.Split(image, "/")
filename = fmt.Sprintf("%s.tar", strings.ReplaceAll(strs[len(strs)-1], ":", "_"))
}
filep := filepath.Join(path, filename)
target, err := os.Create(filep)
if err != nil {
return fmt.Errorf("create file %s failed: %v", filep, err)
}
defer target.Close()
if _, err := io.Copy(target, out); err != nil {
return fmt.Errorf("copy file %s failed: %v", filep, err)
}
return nil
}