173 lines
5.0 KiB
Go
173 lines
5.0 KiB
Go
package docker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
dockerTypes "github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/image"
|
|
"github.com/docker/docker/api/types/mount"
|
|
"github.com/docker/docker/pkg/namesgenerator"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type DockerContainer struct {
|
|
ID DockerID
|
|
Name string
|
|
Volumes []*DockerVolume
|
|
}
|
|
|
|
// Creates a container in the docker daemon and returns the descriptor for the container
|
|
func (d *DockerClient) CreateDockerContainer(ctx context.Context, imageName string, vols []*DockerVolume, environment []string, hosts []string, name *string) (*DockerContainer, error) {
|
|
for _, host := range hosts {
|
|
if host == ":" {
|
|
return nil, fmt.Errorf("invalid host %s", host)
|
|
}
|
|
}
|
|
|
|
if name == nil {
|
|
containerName := fmt.Sprintf("flux-%s", namesgenerator.GetRandomName(0))
|
|
name = &containerName
|
|
}
|
|
d.logger.Debugw("Creating container", zap.String("container_name", *name))
|
|
mounts := make([]mount.Mount, len(vols))
|
|
volumes := make(map[string]struct{}, len(vols))
|
|
for i, volume := range vols {
|
|
volumes[volume.VolumeID] = struct{}{}
|
|
|
|
mounts[i] = mount.Mount{
|
|
Type: mount.TypeVolume,
|
|
Source: volume.VolumeID,
|
|
Target: volume.Mountpoint,
|
|
ReadOnly: false,
|
|
}
|
|
}
|
|
|
|
resp, err := d.client.ContainerCreate(ctx, &container.Config{
|
|
Image: imageName,
|
|
Env: environment,
|
|
Volumes: volumes,
|
|
Labels: map[string]string{
|
|
"managed-by": "flux",
|
|
},
|
|
},
|
|
&container.HostConfig{
|
|
RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped},
|
|
NetworkMode: "bridge",
|
|
Mounts: mounts,
|
|
ExtraHosts: hosts,
|
|
},
|
|
nil,
|
|
nil,
|
|
*name,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := &DockerContainer{
|
|
ID: DockerID(resp.ID),
|
|
Name: *name,
|
|
Volumes: vols,
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (d *DockerClient) ContainerRemove(ctx context.Context, containerID DockerID, options container.RemoveOptions) error {
|
|
d.logger.Debugw("Removing container", zap.String("container_id", string(containerID)))
|
|
return d.client.ContainerRemove(ctx, string(containerID), options)
|
|
}
|
|
|
|
func (d *DockerClient) StartContainer(ctx context.Context, containerID DockerID) error {
|
|
return d.client.ContainerStart(ctx, string(containerID), container.StartOptions{})
|
|
}
|
|
|
|
// blocks until the container returns a 200 status code
|
|
func (d *DockerClient) ContainerWait(ctx context.Context, containerID DockerID, port uint16) error {
|
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
|
defer cancel()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("container failed to become ready in time")
|
|
|
|
default:
|
|
containerJSON, err := d.ContainerInspect(ctx, containerID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if containerJSON.State.Running {
|
|
resp, err := http.Get(fmt.Sprintf("http://%s:%d/", containerJSON.NetworkSettings.IPAddress, port))
|
|
if err == nil && resp.StatusCode == http.StatusOK {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
time.Sleep(time.Second)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *DockerClient) DeleteDockerContainer(ctx context.Context, containerID DockerID) error {
|
|
d.logger.Debugw("Removing container", zap.String("container_id", string(containerID)))
|
|
return d.client.ContainerRemove(ctx, string(containerID), container.RemoveOptions{})
|
|
}
|
|
|
|
func (d *DockerClient) GetContainerIp(containerID DockerID) (string, error) {
|
|
containerJSON, err := d.client.ContainerInspect(context.Background(), string(containerID))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
ip := containerJSON.NetworkSettings.IPAddress
|
|
|
|
return ip, nil
|
|
}
|
|
|
|
type ContainerStatus struct {
|
|
// Can be "created", "running", "paused", "restarting", "removing", "exited", or "dead"
|
|
Status string
|
|
ExitCode int
|
|
}
|
|
|
|
func (d *DockerClient) GetContainerStatus(containerID DockerID) (*ContainerStatus, error) {
|
|
containerJSON, err := d.client.ContainerInspect(context.Background(), string(containerID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
containerStatus := &ContainerStatus{
|
|
Status: containerJSON.State.Status,
|
|
ExitCode: containerJSON.State.ExitCode,
|
|
}
|
|
|
|
return containerStatus, nil
|
|
}
|
|
|
|
func (d *DockerClient) StopContainer(ctx context.Context, containerID DockerID) error {
|
|
d.logger.Debugw("Stopping container", zap.String("container_id", string(containerID)))
|
|
return d.client.ContainerStop(ctx, string(containerID), container.StopOptions{})
|
|
}
|
|
|
|
func (d *DockerClient) ImagePull(ctx context.Context, imageName string, options image.PullOptions) (io.ReadCloser, error) {
|
|
d.logger.Debugw("Pulling image", zap.String("image", imageName))
|
|
return d.client.ImagePull(ctx, imageName, options)
|
|
}
|
|
|
|
func (d *DockerClient) ContainerInspect(ctx context.Context, containerID DockerID) (dockerTypes.ContainerJSON, error) {
|
|
d.logger.Debugw("Inspecting container", zap.String("container_id", string(containerID)))
|
|
return d.client.ContainerInspect(ctx, string(containerID))
|
|
}
|
|
|
|
func (d *DockerClient) ContainerStart(ctx context.Context, containerID string, options container.StartOptions) error {
|
|
d.logger.Debugw("Starting container", zap.String("container_id", containerID))
|
|
return d.client.ContainerStart(ctx, containerID, options)
|
|
}
|