package main

import (
	"archive/tar"
	"bytes"
	"context"
	"io"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/api/types/volume"
	"github.com/docker/docker/client"
)

func createTar(data map[string][]byte) (io.Reader, error) {
	var b bytes.Buffer
	tw := tar.NewWriter(&b)
	for path, datum := range data {
		hdr := tar.Header{
			Name: path,
			Mode: 0644,
			Size: int64(len(datum)),
		}
		if err := tw.WriteHeader(&hdr); err != nil {
			return nil, err
		}
		_, err := tw.Write(datum)
		if err != nil {
			return nil, err
		}
	}
	if err := tw.Close(); err != nil {
		return nil, err
	}
	return &b, nil
}

// createVolumeWithData creates a volume with the given data (e.g. data["/foo"] = []byte("bar"))
// Internally, a container is created from the image so as to provision the data to the volume,
// which is attached to the container.
func createVolumeWithData(cli *client.Client, volumeName string, data map[string][]byte, image string) error {
	_, err := cli.VolumeCreate(context.Background(),
		volume.VolumesCreateBody{
			Driver: "local",
			Name:   volumeName,
		})
	if err != nil {
		return err
	}
	mnt := "/mnt"
	miniContainer, err := cli.ContainerCreate(context.Background(),
		&container.Config{
			Image: image,
		},
		&container.HostConfig{
			Mounts: []mount.Mount{
				{
					Type:   mount.TypeVolume,
					Source: volumeName,
					Target: mnt,
				},
			},
		}, nil, "")
	if err != nil {
		return err
	}
	tr, err := createTar(data)
	if err != nil {
		return err
	}
	if cli.CopyToContainer(context.Background(),
		miniContainer.ID, mnt, tr, types.CopyToContainerOptions{}); err != nil {
		return err
	}
	return cli.ContainerRemove(context.Background(),
		miniContainer.ID,
		types.ContainerRemoveOptions{})
}

func hasVolume(cli *client.Client, volumeName string) bool {
	_, err := cli.VolumeInspect(context.Background(), volumeName)
	return err == nil
}

func removeVolume(cli *client.Client, volumeName string) error {
	return cli.VolumeRemove(context.Background(), volumeName, true)
}