Exemple #1
0
// stopContainers stops and removes the containers.
func stopContainers(app *App) error {
	if app.containerIDs == nil {
		return nil
	}

	var err error

	// Stop and remove all app containers.
	// Do this in the reverse order, because the container IDs are
	// sorted to the startup order.
	for i := len(app.containerIDs) - 1; i >= 0; i-- {
		err = docker.StopAndDeleteContainer(app.containerIDs[i])
		if err != nil {
			return fmt.Errorf("failed to stop and remove container '%s': %v", app.containerIDs[i], err)
		}

		app.containerIDs = app.containerIDs[:i]
	}

	// Clear the container slice completly.
	app.containerIDs = nil

	return nil
}
Exemple #2
0
func startContainers(app *App) (err error) {
	// Stop already started containers on error.
	defer func() {
		if err != nil && len(app.containerIDs) > 0 {
			if errS := stopContainers(app); errS != nil {
				log.Errorf("failed to stop and delete previous app containers: %v", errS)
			}
		}
	}()

	// Clear the container IDs slice.
	app.containerIDs = nil

	// Get the app's directory path.
	volumesPath := app.VolumesDirectoryPath()
	sourcePath := app.SourceDirectoryPath()

	// Get the turtlefile.
	turtlefile, err := app.Turtlefile()
	if err != nil {
		return err
	}

	// Get the container name prefix.
	cNamePrefix := app.ContainerNamePrefix()

	// Start each app container.
	// Hint: the containers are already sorted by the turtlefile Load method.
	for _, container := range turtlefile.Containers {
		// Create the docker container name.
		containerName := cNamePrefix + container.Name

		// Check if a container with the same name is present.
		c, err := docker.GetContainerByName(containerName)
		if err != nil {
			return err
		} else if c != nil {
			// Stop and remove it.
			err = docker.StopAndDeleteContainer(c.ID)
			if err != nil {
				return fmt.Errorf("failed to stop and remove container '%s': %v", c.ID, err)
			}
		}

		// Create the port bindings.
		portBindings := make(map[d.Port][]d.PortBinding)
		for _, p := range app.settings.Ports {
			// Skip if this is not for this container or if disabled.
			if p.ContainerName != container.Name || p.HostPort <= 0 {
				continue
			}

			portProtocol := d.Port(fmt.Sprintf("%v/%s", p.ContainerPort, p.Protocol))

			portBindings[portProtocol] = []d.PortBinding{
				d.PortBinding{
					HostPort: fmt.Sprintf("%v", p.HostPort),
				},
			}
		}

		// Create the links.
		links := make([]string, len(container.Links))
		for i, l := range container.Links {
			links[i] = cNamePrefix + l + ":" + l
		}

		// Create the environment variables slice.
		env, err := app.getEnv(container.Name)
		if err != nil {
			return err
		}

		// Add the static environment variables.
		env = append(container.Env, env...)

		// Create the bind volumes slice.
		binds := container.GetVolumeBinds(volumesPath)

		// Create the host config.
		hostConfig := &d.HostConfig{
			RestartPolicy:   d.NeverRestart(), // the docker daemon will not restart the container automatically.
			Links:           links,
			Privileged:      false,
			PublishAllPorts: false,
			PortBindings:    portBindings,
			Binds:           binds,
			NetworkMode:     container.NetworkMode,
		}

		// Check if the container image should be build from source locally.
		isLocalBuild := container.IsLocalBuild()

		// Create the container image and image name.
		imageName := container.Image
		if isLocalBuild {
			imageName = containerName
		}
		image := imageName + ":" + container.Tag

		// Create the container config.
		cConfig := &d.Config{
			Image:           image,
			Hostname:        container.Hostname,
			Domainname:      container.Domainname,
			Env:             env,
			Cmd:             container.Cmd,
			Entrypoint:      container.Entrypoint,
			WorkingDir:      container.WorkingDir,
			DNS:             container.DNS,
			NetworkDisabled: container.NetworkDisabled,
		}

		// Create the container options.
		options := &d.CreateContainerOptions{
			Name:       containerName,
			Config:     cConfig,
			HostConfig: hostConfig,
		}

		// Check if the image exists.
		_, err = docker.Client.InspectImage(image)
		if err != nil {
			// Check whenever to build or pull the image.
			if isLocalBuild {
				app.setState("building local docker image: " + image)
				log.Infof("building local docker image: %s", image)

				// Build the local image.
				err = docker.Build(imageName, container.Tag, container.BuildPath(sourcePath))
				if err != nil {
					return fmt.Errorf("failed to build image '%s': %v", image, err)
				}
			} else {
				app.setState("pulling docker image: " + image)
				log.Infof("pulling docker image: %s", image)

				// Pull the image.
				err = docker.Client.PullImage(d.PullImageOptions{
					Repository: container.Image,
					Tag:        container.Tag,
				}, d.AuthConfiguration{})

				if err != nil {
					return fmt.Errorf("failed to pull docker image '%s': %v", image, err)
				}
			}
		}

		app.setState("starting container: " + containerName)
		log.Infof("starting container: %s", containerName)

		// Create the docker container.
		c, err = docker.Client.CreateContainer(*options)
		if err != nil {
			return fmt.Errorf("failed to create docker container:\nName: %s\nConfig: %+v\nHost Config: %+v\nError: %v",
				options.Name, options.Config, options.HostConfig, err)
		}

		// Start the container.
		err = docker.Client.StartContainer(c.ID, hostConfig)
		if err != nil {
			return fmt.Errorf("failed to start docker container:\nHost Config: %+v\nError: %v", hostConfig, err)
		}

		// Add the continer ID to the slice.
		app.containerIDs = append(app.containerIDs, c.ID)

		// Wait x milliseconds after the container started.
		// This delays the next container startup.
		// If set to 0, use the default value.
		if container.WaitAfterStartup == 0 {
			time.Sleep(defaultContainerWaitAfterStartup)
		} else {
			time.Sleep(time.Duration(container.WaitAfterStartup) * time.Millisecond)
		}
	}

	// Set the app state.
	app.setState("running")

	// Wait, to be sure all containers started up.
	time.Sleep(waitAfterAppStart)

	return nil
}