func (s *Command) Connect() error {
	host := helpers.StringOrDefault(s.Host, "localhost")
	user := helpers.StringOrDefault(s.User, "root")
	port := helpers.StringOrDefault(s.Port, "22")

	methods, err := s.getSSHAuthMethods()
	if err != nil {
		return err
	}

	config := &ssh.ClientConfig{
		User: user,
		Auth: methods,
	}

	connectRetries := s.ConnectRetries
	if connectRetries == 0 {
		connectRetries = 3
	}

	var finalError error

	for i := 0; i < connectRetries; i++ {
		client, err := ssh.Dial("tcp", host+":"+port, config)
		if err == nil {
			s.client = client
			return nil
		}
		time.Sleep(sshRetryInterval * time.Second)
		finalError = err
	}

	return finalError
}
func (s *DockerExecutor) createContainer(containerType, imageName string, cmd []string, options docker.CreateContainerOptions) (container *docker.Container, err error) {
	hostname := helpers.StringOrDefault(s.Config.Docker.Hostname, s.Build.ProjectUniqueName())
	containerName := s.Build.ProjectUniqueName() + "-" + containerType

	// Fetch image
	image, err := s.getDockerImage(imageName)
	if err != nil {
		return nil, err
	}

	// Fill container options
	options.Name = containerName
	options.Config.Image = image.ID
	options.Config.Hostname = hostname
	options.Config.Cmd = cmd
	options.Config.Labels = s.getLabels(containerType)

	// this will fail potentially some builds if there's name collision
	s.removeContainer(containerName)

	s.Debugln("Creating container", options.Name, "...")
	container, err = s.client.CreateContainer(options)
	if err != nil {
		if container != nil {
			go s.removeContainer(container.ID)
		}
		return nil, err
	}

	s.builds = append(s.builds, container)
	return
}
func (e *AbstractExecutor) generateShellScript() error {
	script := &e.Shell
	script.Build = e.Build
	script.Shell = helpers.StringOrDefault(e.Config.Shell, script.Shell)

	// Add config variables
	for _, environment := range e.Config.Environment {
		keyValue := strings.SplitN(environment, "=", 2)
		if len(keyValue) != 2 {
			continue
		}
		variable := common.BuildVariable{
			Key:   keyValue[0],
			Value: keyValue[1],
		}
		script.Environment = append(script.Environment, variable)
	}

	// Add secure variables
	script.Environment = append(script.Environment, e.Build.Variables...)

	// Generate shell script
	shellScript, err := common.GenerateShellScript(*script)
	if err != nil {
		return err
	}
	e.ShellScript = shellScript
	e.Debugln("Shell script:", shellScript)
	return nil
}
func (e *AbstractExecutor) startBuild() error {
	// Craete pipe where data are read
	reader, writer := io.Pipe()
	go e.ReadTrace(reader)
	e.BuildLog = writer

	// Save hostname
	if e.ShowHostname {
		e.Build.Hostname, _ = os.Hostname()
	}

	// Deduce build directory
	buildsDir := helpers.StringOrDefault(e.Config.BuildsDir, e.DefaultBuildsDir)

	if e.SharedBuildsDir {
		buildsDir = filepath.Join(buildsDir, e.Build.ProjectUniqueDir())
	}
	if slug, err := e.Build.ProjectSlug(); err == nil {
		buildsDir = filepath.Join(buildsDir, slug)
	}

	// Start actual build
	e.Build.StartBuild(buildsDir)
	return nil
}
func (e *AbstractExecutor) startBuild() error {
	// Create pipe where data are read
	reader, writer := io.Pipe()
	e.BuildLog = writer
	go e.readTrace(reader)
	go e.updateTrace(e.Config, e.buildCanceled, e.finishUpdateTrace)

	// Save hostname
	if e.ShowHostname {
		e.Build.Hostname, _ = os.Hostname()
	}

	// Start actual build
	rootDir := helpers.StringOrDefault(e.Config.BuildsDir, e.DefaultBuildsDir)
	cacheDir := helpers.StringOrDefault(e.Config.CacheDir, e.DefaultCacheDir)
	e.Build.StartBuild(rootDir, cacheDir, e.SharedBuildsDir)
	return nil
}
func (e *AbstractExecutor) generateShellScript() error {
	shell := helpers.StringOrDefault(e.Config.Shell, e.DefaultShell)
	shellScript, err := common.GenerateShellScript(shell, e.Build, e.ShellType)
	if err != nil {
		return err
	}
	e.ShellScript = shellScript
	e.Debugln("Shell script:", shellScript)
	return nil
}
func (e *AbstractExecutor) startBuild() error {
	// Create pipe where data are read
	reader, writer := io.Pipe()
	go e.ReadTrace(reader)
	e.BuildLog = writer

	// Save hostname
	if e.ShowHostname {
		e.Build.Hostname, _ = os.Hostname()
	}

	// Start actual build
	rootDir := helpers.StringOrDefault(e.Config.BuildsDir, e.DefaultBuildsDir)
	e.Build.StartBuild(rootDir, e.SharedBuildsDir)
	return nil
}
func (s *ParallelsExecutor) createVM() error {
	baseImage := s.Config.Parallels.BaseName
	if baseImage == "" {
		return errors.New("Missing Image setting from Parallels config")
	}

	templateName := helpers.StringOrDefault(s.Config.Parallels.TemplateName, baseImage+"-template")

	// remove invalid template (removed?)
	templateStatus, _ := prl.Status(templateName)
	if templateStatus == prl.Invalid {
		prl.Unregister(templateName)
	}

	if !prl.Exist(templateName) {
		s.Debugln("Creating template from VM", baseImage, "...")
		err := prl.CreateTemplate(baseImage, templateName)
		if err != nil {
			return err
		}
	}

	s.Debugln("Creating runner from VM template...")
	err := prl.CreateOsVM(s.vmName, templateName)
	if err != nil {
		return err
	}

	s.Debugln("Bootstraping VM...")
	err = prl.Start(s.vmName)
	if err != nil {
		return err
	}

	s.Debugln("Waiting for VM to start...")
	err = prl.TryExec(s.vmName, 120, "exit", "0")
	if err != nil {
		return err
	}

	s.Debugln("Waiting for VM to become responsive...")
	err = s.verifyMachine(s.vmName)
	if err != nil {
		return err
	}
	return nil
}
func (s *DockerExecutor) addCacheVolume(binds, volumesFrom *[]string, containerPath string) error {
	var err error
	containerPath = s.getAbsoluteContainerPath(containerPath)

	// disable cache for automatic container cache, but leave it for host volumes (they are shared on purpose)
	if helpers.BoolOrDefault(s.Config.Docker.DisableCache, false) {
		s.Debugln("Container cache for", containerPath, " is disabled.")
		return nil
	}

	hash := md5.Sum([]byte(containerPath))

	// use host-based cache
	if cacheDir := helpers.StringOrDefault(s.Config.Docker.CacheDir, ""); cacheDir != "" {
		hostPath := fmt.Sprintf("%s/%s/%x", cacheDir, s.Build.ProjectUniqueName(), hash)
		hostPath, err := filepath.Abs(hostPath)
		if err != nil {
			return err
		}
		s.Debugln("Using path", hostPath, "as cache for", containerPath, "...")
		*binds = append(*binds, fmt.Sprintf("%v:%v", hostPath, containerPath))
		return nil
	}

	// get existing cache container
	containerName := fmt.Sprintf("%s-cache-%x", s.Build.ProjectUniqueName(), hash)
	container, _ := s.client.InspectContainer(containerName)

	// check if we have valid cache, if not remove the broken container
	if container != nil && container.Volumes[containerPath] == "" {
		s.removeContainer(container.ID)
		container = nil
	}

	// create new cache container for that project
	if container == nil {
		container, err = s.createCacheVolume(containerName, containerPath)
		if err != nil {
			return err
		}
	}

	s.Debugln("Using container", container.ID, "as cache", containerPath, "...")
	*volumesFrom = append(*volumesFrom, container.ID)
	return nil
}
func (s *DockerExecutor) getImage(imageName string) (*docker.Image, error) {
	s.Debugln("Looking for image", imageName, "...")
	image, err := s.client.InspectImage(imageName)
	if err == nil {
		return image, nil
	}

	s.Println("Pulling docker image", imageName, "...")
	pullImageOptions := docker.PullImageOptions{
		Repository: imageName,
		Registry:   helpers.StringOrDefault(s.Config.Docker.Registry, ""),
	}

	err = s.client.PullImage(pullImageOptions, docker.AuthConfiguration{})
	if err != nil {
		return nil, err
	}

	return s.client.InspectImage(imageName)
}
func Connect(c DockerCredentials, apiVersion string) (*docker.Client, error) {
	endpoint := "unix:///var/run/docker.sock"
	tlsVerify := false
	tlsCertPath := ""

	if host := helpers.StringOrDefault(c.Host, ""); host != "" {
		// read docker config from config
		endpoint = host
		if c.CertPath != nil {
			tlsVerify = true
			tlsCertPath = *c.CertPath
		}
	} else if host := os.Getenv("DOCKER_HOST"); host != "" {
		// read docker config from environment
		endpoint = host
		tlsVerify, _ = strconv.ParseBool(os.Getenv("DOCKER_TLS_VERIFY"))
		tlsCertPath = os.Getenv("DOCKER_CERT_PATH")
	}

	if tlsVerify {
		client, err := docker.NewVersionnedTLSClient(
			endpoint,
			filepath.Join(tlsCertPath, "cert.pem"),
			filepath.Join(tlsCertPath, "key.pem"),
			filepath.Join(tlsCertPath, "ca.pem"),
			apiVersion,
		)
		if err != nil {
			return nil, err
		}

		return client, nil
	} else {
		client, err := docker.NewVersionedClient(endpoint, apiVersion)
		if err != nil {
			return nil, err
		}

		return client, nil
	}
}
func (e *AbstractExecutor) updateShell() error {
	script := &e.Shell
	script.Build = e.Build
	script.Shell = helpers.StringOrDefault(e.Config.Shell, script.Shell)

	// Add config variables
	for _, environment := range e.Config.Environment {
		keyValue := strings.SplitN(environment, "=", 2)
		if len(keyValue) != 2 {
			continue
		}
		variable := common.BuildVariable{
			Key:   keyValue[0],
			Value: keyValue[1],
		}
		script.Environment = append(script.Environment, variable)
	}

	// Add secure variables
	script.Environment = append(script.Environment, e.Build.Variables...)
	return nil
}
func (s *DockerExecutor) createContainer(image *docker.Image, cmd []string) (*docker.Container, error) {
	hostname := helpers.StringOrDefault(s.Config.Docker.Hostname, s.Build.ProjectUniqueName())
	containerName := s.Build.ProjectUniqueName()

	// this will fail potentially some builds if there's name collision
	s.removeContainer(containerName)

	createContainerOptions := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Hostname:     hostname,
			Image:        image.ID,
			Tty:          false,
			AttachStdin:  true,
			AttachStdout: true,
			AttachStderr: true,
			OpenStdin:    true,
			StdinOnce:    true,
			Env:          s.ShellScript.Environment,
			Cmd:          cmd,
		},
		HostConfig: &docker.HostConfig{
			Privileged:    s.Config.Docker.Privileged,
			RestartPolicy: docker.NeverRestart(),
			ExtraHosts:    s.Config.Docker.ExtraHosts,
			Links:         s.Config.Docker.Links,
		},
	}

	s.Debugln("Creating services...")
	links, err := s.createServices()
	if err != nil {
		return nil, err
	}
	createContainerOptions.HostConfig.Links = append(createContainerOptions.HostConfig.Links, links...)

	s.Debugln("Creating cache directories...")
	binds, volumesFrom, err := s.createVolumes(image)
	if err != nil {
		return nil, err
	}
	createContainerOptions.HostConfig.Binds = binds
	createContainerOptions.HostConfig.VolumesFrom = volumesFrom

	s.Debugln("Creating container", createContainerOptions.Name, "...")
	container, err := s.client.CreateContainer(createContainerOptions)
	if err != nil {
		if container != nil {
			go s.removeContainer(container.ID)
		}
		return nil, err
	}

	s.Debugln("Starting container", container.ID, "...")
	err = s.client.StartContainer(container.ID, createContainerOptions.HostConfig)
	if err != nil {
		go s.removeContainer(container.ID)
		return nil, err
	}

	return container, nil
}
func (e *AbstractExecutor) updateShell() error {
	script := &e.Shell
	script.Build = e.Build
	script.Shell = helpers.StringOrDefault(e.Config.Shell, script.Shell)
	return nil
}