2017-07-30 18:05:06 +00:00
|
|
|
package control
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/fsouza/go-dockerclient"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
type dockerEnvironment struct {
|
|
|
|
baseEnvironment
|
|
|
|
|
|
|
|
client *docker.Client
|
|
|
|
container *docker.Container
|
|
|
|
context context.Context
|
|
|
|
|
2017-08-31 22:01:32 +00:00
|
|
|
server *ServerStruct
|
2017-07-30 18:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure DockerEnvironment implements Environment
|
|
|
|
var _ Environment = &dockerEnvironment{}
|
|
|
|
|
|
|
|
// NewDockerEnvironment creates a new docker enviornment
|
|
|
|
// instance and connects to the docker client on the host system
|
|
|
|
// If the container is already running it will try to reattach
|
|
|
|
// to the running container
|
2017-08-31 22:01:32 +00:00
|
|
|
func NewDockerEnvironment(server *ServerStruct) (Environment, error) {
|
2017-07-30 18:05:06 +00:00
|
|
|
env := dockerEnvironment{}
|
|
|
|
|
|
|
|
env.server = server
|
|
|
|
|
|
|
|
client, err := docker.NewClient("unix:///var/run/docker.sock")
|
|
|
|
env.client = client
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Fatal("Failed to connect to docker.")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if env.server.DockerContainer.ID != "" {
|
|
|
|
if err := env.reattach(); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to reattach to existing container.")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &env, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (env *dockerEnvironment) reattach() error {
|
|
|
|
container, err := env.client.InspectContainer(env.server.DockerContainer.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
env.container = container
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create creates the docker container for the environment and applies all
|
|
|
|
// settings to it
|
|
|
|
func (env *dockerEnvironment) Create() error {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithField("serverID", env.server.ID).Debug("Creating docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
// Split image repository and tag to feed it to the library
|
|
|
|
imageParts := strings.Split(env.server.Service().DockerImage, ":")
|
|
|
|
imageRepoParts := strings.Split(imageParts[0], "/")
|
|
|
|
if len(imageRepoParts) >= 3 {
|
|
|
|
// Handle possibly required authentication
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pull docker image
|
|
|
|
var pullImageOpts = docker.PullImageOptions{
|
|
|
|
Repository: imageParts[0],
|
|
|
|
}
|
|
|
|
if len(imageParts) >= 2 {
|
|
|
|
pullImageOpts.Tag = imageParts[1]
|
|
|
|
}
|
|
|
|
log.WithField("image", env.server.service.DockerImage).Debug("Pulling docker image")
|
|
|
|
err := env.client.PullImage(pullImageOpts, docker.AuthConfiguration{})
|
|
|
|
if err != nil {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithError(err).WithField("serverID", env.server.ID).Error("Failed to create docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create docker container
|
|
|
|
// TODO: apply cpu, io, disk limits.
|
|
|
|
containerConfig := &docker.Config{
|
|
|
|
Image: env.server.Service().DockerImage,
|
|
|
|
}
|
|
|
|
containerHostConfig := &docker.HostConfig{
|
|
|
|
Memory: env.server.Settings.Memory,
|
|
|
|
MemorySwap: env.server.Settings.Swap,
|
|
|
|
}
|
|
|
|
createContainerOpts := docker.CreateContainerOptions{
|
2017-09-03 22:20:45 +00:00
|
|
|
Name: "ptdl-" + env.server.UUIDShort(),
|
2017-07-30 18:05:06 +00:00
|
|
|
Config: containerConfig,
|
|
|
|
HostConfig: containerHostConfig,
|
|
|
|
Context: env.context,
|
|
|
|
}
|
|
|
|
container, err := env.client.CreateContainer(createContainerOpts)
|
|
|
|
if err != nil {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithError(err).WithField("serverID", env.server.ID).Error("Failed to create docker container")
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
env.server.DockerContainer.ID = container.ID
|
|
|
|
env.container = container
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroy removes the environment's docker container
|
|
|
|
func (env *dockerEnvironment) Destroy() error {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithField("serverID", env.server.ID).Debug("Destroying docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
err := env.client.RemoveContainer(docker.RemoveContainerOptions{
|
|
|
|
ID: env.server.DockerContainer.ID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithError(err).WithField("serverID", env.server.ID).Error("Failed to destroy docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start starts the environment's docker container
|
|
|
|
func (env *dockerEnvironment) Start() error {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithField("serverID", env.server.ID).Debug("Starting service in docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
if err := env.client.StartContainer(env.container.ID, nil); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to start docker container")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop stops the environment's docker container
|
|
|
|
func (env *dockerEnvironment) Stop() error {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithField("serverID", env.server.ID).Debug("Stopping service in docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
if err := env.client.StopContainer(env.container.ID, 20000); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to stop docker container")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (env *dockerEnvironment) Kill() error {
|
2017-08-31 22:01:32 +00:00
|
|
|
log.WithField("serverID", env.server.ID).Debug("Killing service in docker environment")
|
2017-07-30 18:05:06 +00:00
|
|
|
if err := env.client.KillContainer(docker.KillContainerOptions{
|
|
|
|
ID: env.container.ID,
|
|
|
|
}); err != nil {
|
|
|
|
log.WithError(err).Error("Failed to kill docker container")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exec sends commands to the standard input of the docker container
|
|
|
|
func (env *dockerEnvironment) Exec(command string) error {
|
|
|
|
return nil
|
|
|
|
}
|