// 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 }
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 }