func CapAddForTask(taskInfo *mesos.TaskInfo) []string {
	var params []string
	for _, param := range getParams("cap-add", taskInfo) {
		params = append(params, param.Value)
	}
	return params
}
CapAddForTask scans for cap-adds and generate string slice
package container
import (
	"encoding/json"
	"fmt"
	"io"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	retry "github.com/avast/retry-go"
	docker "github.com/fsouza/go-dockerclient"
	mesos "github.com/mesos/mesos-go/api/v1/lib"
	"github.com/pborman/uuid"
	log "github.com/sirupsen/logrus"
)
const (
	PullImageNumRetries = 5
	defaultCpuPeriod = 50000  
)
var portProtocolsTokenizer = regexp.MustCompile(`,\s?`)
type DockerClient interface {
	CreateContainer(opts docker.CreateContainerOptions) (*docker.Container, error)
	InspectContainer(id string) (*docker.Container, error)
	ListContainers(opts docker.ListContainersOptions) ([]docker.APIContainers, error)
	ListImages(docker.ListImagesOptions) ([]docker.APIImages, error)
	Logs(opts docker.LogsOptions) error
	PullImage(docker.PullImageOptions, docker.AuthConfiguration) error
	StartContainer(id string, hostConfig *docker.HostConfig) error
	StopContainer(id string, timeout uint) error
}
func CheckImage(client DockerClient, taskInfo *mesos.TaskInfo) bool {
	images, err := client.ListImages(docker.ListImagesOptions{All: false})
	if err != nil {
		log.Errorf("Failure to list Docker images: %s", err.Error())
		return false
	}
	for _, img := range images {
		for _, tag := range img.RepoTags {
			if tag == taskInfo.Container.Docker.Image {
				return true
			}
		}
	}
	return false
}
func StopContainer(client DockerClient, containerId string, timeout uint) error {
	_ = client.StopContainer(containerId, timeout)
	cntr, err := client.InspectContainer(containerId)
	if err != nil {
		return err
	}
	if cntr.State.Status == "exited" {
		return nil  
	}
	err = client.StopContainer(containerId, timeout)
	if err != nil {
		return err
	}
	if cntr.State.Status != "exited" {
		return fmt.Errorf("Unable to kill container! %s", containerId)
	}
	return nil
}
func PullImage(client DockerClient, taskInfo *mesos.TaskInfo, authConfig *docker.AuthConfiguration) error {
	log.Infof("Pulling Docker image '%s'", taskInfo.Container.Docker.Image)
	var numRetries int
	err := retry.Do(func() error {
		numRetries = numRetries + 1
		err := client.PullImage(
			docker.PullImageOptions{
				Repository: taskInfo.Container.Docker.Image,
			},
			*authConfig,
		)
		if err != nil {
			log.Warnf("Pull failed (retry %d/%d): %s", numRetries, PullImageNumRetries, err)
			return err
		}
		log.Info("Pulled.")
		return nil
	},
		retry.Attempts(PullImageNumRetries),
	)
	return err
}
func GetLogs(client DockerClient, containerId string, since int64, stdout io.Writer, stderr io.Writer) {
	go func() {
		err := client.Logs(docker.LogsOptions{
			Container:    containerId,
			OutputStream: stdout,
			ErrorStream:  stderr,
			Since:        since,
			Stdout:       true,
			Stderr:       true,
		})
		if err != nil {
			log.Errorf("Failed to fetch logs for task: %s", err.Error())
		}
	}()
}
func FollowLogs(client DockerClient, containerId string, since int64, stdout io.Writer, stderr io.Writer) {
	go func() {
		err := client.Logs(docker.LogsOptions{
			Container:    containerId,
			OutputStream: stdout,
			ErrorStream:  stderr,
			Since:        since,
			Stdout:       true,
			Stderr:       true,
			Follow:       true,
		})
		if err != nil {
			log.Errorf("Failed to fetch logs for task: %s", err.Error())
		}
	}()
}
func ConfigForTask(taskInfo *mesos.TaskInfo, forceCpuLimit bool, forceMemoryLimit bool, useCpuShares bool, envVars []string) *docker.CreateContainerOptions {
	labels := LabelsForTask(taskInfo)
	var command []string
	if _, ok := labels["executor.ShellCommand"]; ok {
		command = strings.Split(labels["executor.ShellCommand"], " ")
		delete(labels, "executor.ShellCommand")
	}
	log.Infof("Launching task %s with command '%s'", taskInfo.GetName(), command)
	config := &docker.CreateContainerOptions{
		Name: GetContainerName(&taskInfo.TaskID),
		Config: &docker.Config{
			Env:          EnvForTask(taskInfo, labels, envVars),
			ExposedPorts: PortsForTask(taskInfo),
			Image:        taskInfo.Container.Docker.Image,
			Labels:       labels,
			Cmd:          command,
		},
		HostConfig: &docker.HostConfig{
			Binds:        BindsForTask(taskInfo),
			PortBindings: PortBindingsForTask(taskInfo),
			NetworkMode:  NetworkForTask(taskInfo),
			CapAdd:       CapAddForTask(taskInfo),
			CapDrop:      CapDropForTask(taskInfo),
			VolumeDriver: VolumeDriverForTask(taskInfo),
		},
	}
	setCpuLimit(config, taskInfo, forceCpuLimit, useCpuShares)
	memory := getResource("mem", taskInfo)
	if memory != nil && forceMemoryLimit {
		memoryLimit := int64(memory.Scalar.Value * float64(1024*1024))
		log.Infof("Memory limit set to %.0fMB [HostConfig.Memory=%d] ", memory.Scalar.Value, memoryLimit)
		config.HostConfig.Memory = int64(memoryLimit)
	}
	jsonTaskInfo, _ := json.Marshal(*taskInfo)
	log.Debugf("Mesos TaskInfo: %s", jsonTaskInfo)
	jsonConfig, _ := json.Marshal(config)
	log.Debugf("Final config: %s", jsonConfig)
	return config
}
func setCpuLimit(config *docker.CreateContainerOptions, taskInfo *mesos.TaskInfo, forceCpuLimit bool, useCpuShares bool) {
	cpus := getResource("cpus", taskInfo)
	if cpus == nil || !forceCpuLimit {
		return
	}
	if useCpuShares {
		multiplier := cpus.Scalar.Value / float64(runtime.NumCPU())
		if multiplier > 1.0 {
			log.Errorf("CPUShares enabled, but CPUs value is more than 100%%. Scaling down to 1.0")
			multiplier = 1.0
		}
		config.HostConfig.CPUShares = int64(1024 * multiplier)
		log.Infof("CPU Shares set [HostConfig.CPUShares=%d", config.HostConfig.CPUShares)
		return
	}
	config.HostConfig.CPUPeriod = defaultCpuPeriod
	config.HostConfig.CPUQuota = int64(cpus.Scalar.Value * float64(defaultCpuPeriod))
	log.Infof("CPU limit set [HostConfig.CPUQuota=%d, HostConfig.CPUPeriod=%d]  ", config.HostConfig.CPUQuota, defaultCpuPeriod)
}
func getPortProtocols(port mesos.ContainerInfo_DockerInfo_PortMapping) []string {
	matches := portProtocolsTokenizer.Split(port.GetProtocol(), -1)
	var protocols []string
	for _, protocol := range matches {
		if protocol == "" {
			return []string{"tcp"}
		}
		protocols = append(protocols, protocol)
	}
	return protocols
}
func PortsForTask(taskInfo *mesos.TaskInfo) map[docker.Port]struct{} {
	ports := make(map[docker.Port]struct{}, len(taskInfo.Container.Docker.PortMappings))
	for _, port := range taskInfo.Container.Docker.PortMappings {
		if port.ContainerPort == 0 {
			continue
		}
		for _, proto := range getPortProtocols(port) {
			portStr := docker.Port(strconv.Itoa(int(port.ContainerPort)) + "/" + proto)
			ports[portStr] = struct{}{}
		}
	}
	log.Debugf("Ports: %#v", ports)
	return ports
}
func getParams(key string, taskInfo *mesos.TaskInfo) (params []mesos.Parameter) {
	for _, param := range taskInfo.Container.Docker.Parameters {
		if param.Key == "" || param.Key != key {
			continue
		}
		params = append(params, param)
	}
	return params
}
func getHostname(taskInfo *mesos.TaskInfo) string {
	if taskInfo.Executor == nil || taskInfo.Executor.Command == nil ||
		taskInfo.Executor.Command.Environment == nil ||
		taskInfo.Executor.Command.Environment.Variables == nil {
		return ""
	}
	for _, envVar := range taskInfo.Executor.Command.Environment.Variables {
		if envVar.Name == "TASK_HOST" {
			return *envVar.Value
		}
	}
	return ""
}
func AppendTaskEnv(envVars []string, taskInfo *mesos.TaskInfo) []string {
	if taskInfo.Executor == nil || taskInfo.Executor.Command == nil ||
		taskInfo.Executor.Command.Environment == nil ||
		taskInfo.Executor.Command.Environment.Variables == nil {
		return envVars
	}
	for _, vars := range taskInfo.Executor.Command.Environment.Variables {
		envVars = append(
			envVars,
			fmt.Sprintf("%s=%s", vars.Name, *vars.Value),
		)
	}
	return envVars
}
func EnvForTask(taskInfo *mesos.TaskInfo, labels map[string]string,
	addEnvVars []string) []string {
	var envVars []string
	envVars = AppendTaskEnv(envVars, taskInfo)
	for _, param := range getParams("env", taskInfo) {
		envVars = append(envVars, param.Value)
	}
	envVars = append(envVars, addEnvVars...)
	for _, port := range taskInfo.Container.Docker.PortMappings {
		if port.ContainerPort < 1 || port.HostPort < 1 {
			continue
		}
		envVars = append(
			envVars,
			fmt.Sprintf("MESOS_PORT_%d=%d", port.ContainerPort, port.HostPort),
		)
	}
	hostname := getHostname(taskInfo)
	if len(hostname) > 0 {
		envVars = append(envVars, "MESOS_HOSTNAME="+getHostname(taskInfo))
	}
	svcName, svcOk := labels["ServiceName"]
	envName, envOk := labels["Environment"]
	if svcOk {
		envVars = append(envVars, "SERVICE_NAME="+svcName)
	} else {
		log.Warnf("No ServiceName set for %s", taskInfo.TaskID.Value)
	}
	if envOk {
		envVars = append(envVars, "ENVIRONMENT="+envName)
	} else {
		log.Warnf("No Environment set for %s", taskInfo.TaskID.Value)
	}
	if taskInfo.Container == nil || taskInfo.Container.Docker == nil || taskInfo.Container.Docker.Image == "" {
		log.Warnf("Unable to extract version from Docker image for %s", taskInfo.TaskID)
		return envVars
	}
	versionParts := strings.Split(taskInfo.Container.Docker.Image, ":")
	if len(versionParts) < 2 {
		log.Warnf("Unable to extract version from Docker image for %s", taskInfo.TaskID)
		return envVars
	}
	envVars = append(envVars, "SERVICE_VERSION="+versionParts[1])
	return envVars
}
func LabelsForTask(taskInfo *mesos.TaskInfo) map[string]string {
	labels := make(map[string]string, len(taskInfo.Container.Docker.Parameters))
	for _, param := range getParams("label", taskInfo) {
		values := strings.SplitN(param.Value, "=", 2)
		if len(values) < 2 {
			log.Debugf("Got label with empty value: %s", param.Key)
			continue  
		}
		labels[values[0]] = values[1]
	}
	return labels
}
func BindsForTask(taskInfo *mesos.TaskInfo) []string {
	var binds []string
	for _, binding := range taskInfo.Container.Volumes {
		if binding.Mode != nil && *binding.Mode != mesos.RW {
			binds = append(binds, fmt.Sprintf("%s:%s:ro", *binding.HostPath, binding.ContainerPath))
		} else {
			binds = append(binds, fmt.Sprintf("%s:%s", *binding.HostPath, binding.ContainerPath))
		}
	}
	log.Debugf("Volumes Binds: %#v", binds)
	return binds
}
func PortBindingsForTask(taskInfo *mesos.TaskInfo) map[docker.Port][]docker.PortBinding {
	portBinds := make(map[docker.Port][]docker.PortBinding, len(taskInfo.Container.Docker.PortMappings))
	for _, port := range taskInfo.Container.Docker.PortMappings {
		if port.HostPort < 1 {
			continue
		}
		for _, proto := range getPortProtocols(port) {
			portBinds[docker.Port(strconv.Itoa(int(port.ContainerPort))+"/"+proto)] =
				[]docker.PortBinding{
					{HostPort: strconv.Itoa(int(port.HostPort))},
				}
		}
	}
	log.Debugf("Port Bindings: %#v", portBinds)
	return portBinds
}