func (p *Project) listStoppedContainers(ctx context.Context, services ...string) ([]string, error) {
	stoppedContainers := []string{}
	err := p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(nil, events.NoEvent, events.NoEvent, func(service Service) error {
			containers, innerErr := service.Containers(ctx)
			if innerErr != nil {
				return innerErr
			}
			for _, container := range containers {
				running, innerErr := container.IsRunning(ctx)
				if innerErr != nil {
					log.Error(innerErr)
				}
				if !running {
					containerID, innerErr := container.ID()
					if innerErr != nil {
						log.Error(innerErr)
					}
					stoppedContainers = append(stoppedContainers, containerID)
				}
			}
			return nil
		})
	}), nil)
	if err != nil {
		return nil, err
	}
	return stoppedContainers, nil
}
listStoppedContainers lists the stopped containers for the specified services.
package project
import (
	"errors"
	"fmt"
	"strings"
	"golang.org/x/net/context"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/filters"
	"github.com/docker/libcompose/config"
	"github.com/docker/libcompose/labels"
	"github.com/docker/libcompose/logger"
	"github.com/docker/libcompose/project/events"
	"github.com/docker/libcompose/project/options"
	"github.com/docker/libcompose/utils"
)
type wrapperAction func(*serviceWrapper, map[string]*serviceWrapper)
type serviceAction func(service Service) error
type Project struct {
	Name           string
	ServiceConfigs *config.ServiceConfigs
	VolumeConfigs  map[string]*config.VolumeConfig
	NetworkConfigs map[string]*config.NetworkConfig
	Files          []string
	ReloadCallback func() error
	ParseOptions   *config.ParseOptions
	context       *Context
	clientFactory ClientFactory
	reload        []string
	upCount       int
	listeners     []chan<- events.Event
	hasListeners  bool
}
func NewProject(clientFactory ClientFactory, context *Context, parseOptions *config.ParseOptions) *Project {
	p := &Project{
		context:        context,
		clientFactory:  clientFactory,
		ParseOptions:   parseOptions,
		ServiceConfigs: config.NewServiceConfigs(),
		VolumeConfigs:  make(map[string]*config.VolumeConfig),
		NetworkConfigs: make(map[string]*config.NetworkConfig),
	}
	if context.LoggerFactory == nil {
		context.LoggerFactory = &logger.NullLogger{}
	}
	context.Project = p
	p.listeners = []chan<- events.Event{NewDefaultListener(p)}
	return p
}
func (p *Project) Parse() error {
	err := p.context.open()
	if err != nil {
		return err
	}
	p.Name = p.context.ProjectName
	p.Files = p.context.ComposeFiles
	if len(p.Files) == 1 && p.Files[0] == "-" {
		p.Files = []string{"."}
	}
	if p.context.ComposeBytes != nil {
		for i, composeBytes := range p.context.ComposeBytes {
			file := ""
			if i < len(p.context.ComposeFiles) {
				file = p.Files[i]
			}
			if err := p.load(file, composeBytes); err != nil {
				return err
			}
		}
	}
	return nil
}
func (p *Project) CreateService(name string) (Service, error) {
	existing, ok := p.ServiceConfigs.Get(name)
	if !ok {
		return nil, fmt.Errorf("Failed to find service: %s", name)
	}
	config := *existing
	if p.context.EnvironmentLookup != nil {
		parsedEnv := make([]string, 0, len(config.Environment))
		for _, env := range config.Environment {
			parts := strings.SplitN(env, "=", 2)
			if len(parts) > 1 && parts[1] != "" {
				parsedEnv = append(parsedEnv, env)
				continue
			} else {
				env = parts[0]
			}
			for _, value := range p.context.EnvironmentLookup.Lookup(env, name, &config) {
				parsedEnv = append(parsedEnv, value)
			}
		}
		config.Environment = parsedEnv
	}
	return p.context.ServiceFactory.Create(p, name, &config)
}
func (p *Project) AddConfig(name string, config *config.ServiceConfig) error {
	p.Notify(events.ServiceAdd, name, nil)
	p.ServiceConfigs.Add(name, config)
	p.reload = append(p.reload, name)
	return nil
}
func (p *Project) AddVolumeConfig(name string, config *config.VolumeConfig) error {
	p.Notify(events.VolumeAdd, name, nil)
	p.VolumeConfigs[name] = config
	return nil
}
func (p *Project) AddNetworkConfig(name string, config *config.NetworkConfig) error {
	p.Notify(events.NetworkAdd, name, nil)
	p.NetworkConfigs[name] = config
	return nil
}
func (p *Project) Load(bytes []byte) error {
	return p.load("", bytes)
}
func (p *Project) load(file string, bytes []byte) error {
	serviceConfigs, volumeConfigs, networkConfigs, err := config.Merge(p.ServiceConfigs, p.context.EnvironmentLookup, p.context.ResourceLookup, file, bytes, p.ParseOptions)
	if err != nil {
		log.Errorf("Could not parse config for project %s : %v", p.Name, err)
		return err
	}
	for name, config := range serviceConfigs {
		err := p.AddConfig(name, config)
		if err != nil {
			return err
		}
	}
	for name, config := range volumeConfigs {
		err := p.AddVolumeConfig(name, config)
		if err != nil {
			return err
		}
	}
	for name, config := range networkConfigs {
		err := p.AddNetworkConfig(name, config)
		if err != nil {
			return err
		}
	}
	return nil
}
func (p *Project) loadWrappers(wrappers map[string]*serviceWrapper, servicesToConstruct []string) error {
	for _, name := range servicesToConstruct {
		wrapper, err := newServiceWrapper(name, p)
		if err != nil {
			return err
		}
		wrappers[name] = wrapper
	}
	return nil
}
func (p *Project) Build(ctx context.Context, buildOptions options.Build, services ...string) error {
	return p.perform(events.ProjectBuildStart, events.ProjectBuildDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.ServiceBuildStart, events.ServiceBuild, func(service Service) error {
			return service.Build(ctx, buildOptions)
		})
	}), nil)
}
func (p *Project) Create(ctx context.Context, options options.Create, services ...string) error {
	if options.NoRecreate && options.ForceRecreate {
		return fmt.Errorf("no-recreate and force-recreate cannot be combined")
	}
	return p.perform(events.ProjectCreateStart, events.ProjectCreateDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.ServiceCreateStart, events.ServiceCreate, func(service Service) error {
			return service.Create(ctx, options)
		})
	}), nil)
}
func (p *Project) Stop(ctx context.Context, timeout int, services ...string) error {
	return p.perform(events.ProjectStopStart, events.ProjectStopDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(nil, events.ServiceStopStart, events.ServiceStop, func(service Service) error {
			return service.Stop(ctx, timeout)
		})
	}), nil)
}
func (p *Project) Down(ctx context.Context, opts options.Down, services ...string) error {
	if !opts.RemoveImages.Valid() {
		return fmt.Errorf("--rmi flag must be local, all or empty")
	}
	if err := p.Stop(ctx, 10, services...); err != nil {
		return err
	}
	if opts.RemoveOrphans {
		if err := p.removeOrphanContainers(); err != nil {
			return err
		}
	}
	if err := p.Delete(ctx, options.Delete{
		RemoveVolume: opts.RemoveVolume,
	}, services...); err != nil {
		return err
	}
	return p.forEach([]string{}, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.NoEvent, events.NoEvent, func(service Service) error {
			return service.RemoveImage(ctx, opts.RemoveImages)
		})
	}), func(service Service) error {
		return service.Create(ctx, options.Create{})
	})
}
func (p *Project) removeOrphanContainers() error {
	client := p.clientFactory.Create(nil)
	filter := filters.NewArgs()
	filter.Add("label", labels.PROJECT.EqString(p.Name))
	containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{
		Filter: filter,
	})
	if err != nil {
		return err
	}
	currentServices := map[string]struct{}{}
	for _, serviceName := range p.ServiceConfigs.Keys() {
		currentServices[serviceName] = struct{}{}
	}
	for _, container := range containers {
		serviceLabel := container.Labels[labels.SERVICE.Str()]
		if _, ok := currentServices[serviceLabel]; !ok {
			if err := client.ContainerKill(context.Background(), container.ID, "SIGKILL"); err != nil {
				return err
			}
			if err := client.ContainerRemove(context.Background(), types.ContainerRemoveOptions{
				ContainerID: container.ID,
				Force:       true,
			}); err != nil {
				return err
			}
		}
	}
	return nil
}
func (p *Project) Restart(ctx context.Context, timeout int, services ...string) error {
	return p.perform(events.ProjectRestartStart, events.ProjectRestartDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.ServiceRestartStart, events.ServiceRestart, func(service Service) error {
			return service.Restart(ctx, timeout)
		})
	}), nil)
}
func (p *Project) Port(ctx context.Context, index int, protocol, serviceName, privatePort string) (string, error) {
	service, err := p.CreateService(serviceName)
	if err != nil {
		return "", err
	}
	containers, err := service.Containers(ctx)
	if err != nil {
		return "", err
	}
	if index < 1 || index > len(containers) {
		return "", fmt.Errorf("Invalid index %d", index)
	}
	return containers[index-1].Port(ctx, fmt.Sprintf("%s/%s", privatePort, protocol))
}
func (p *Project) Ps(ctx context.Context, onlyID bool, services ...string) (InfoSet, error) {
	allInfo := InfoSet{}
	for _, name := range p.ServiceConfigs.Keys() {
		service, err := p.CreateService(name)
		if err != nil {
			return nil, err
		}
		info, err := service.Info(ctx, onlyID)
		if err != nil {
			return nil, err
		}
		allInfo = append(allInfo, info...)
	}
	return allInfo, nil
}
func (p *Project) Start(ctx context.Context, services ...string) error {
	return p.perform(events.ProjectStartStart, events.ProjectStartDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.ServiceStartStart, events.ServiceStart, func(service Service) error {
			return service.Start(ctx)
		})
	}), nil)
}
func (p *Project) Run(ctx context.Context, serviceName string, commandParts []string) (int, error) {
	if !p.ServiceConfigs.Has(serviceName) {
		return 1, fmt.Errorf("%s is not defined in the template", serviceName)
	}
	var exitCode int
	err := p.forEach([]string{}, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.ServiceRunStart, events.ServiceRun, func(service Service) error {
			if service.Name() == serviceName {
				code, err := service.Run(ctx, commandParts)
				exitCode = code
				return err
			}
			return nil
		})
	}), func(service Service) error {
		return service.Create(ctx, options.Create{})
	})
	return exitCode, err
}
func (p *Project) Up(ctx context.Context, options options.Up, services ...string) error {
	return p.perform(events.ProjectUpStart, events.ProjectUpDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(wrappers, events.ServiceUpStart, events.ServiceUp, func(service Service) error {
			return service.Up(ctx, options)
		})
	}), func(service Service) error {
		return service.Create(ctx, options.Create)
	})
}
func (p *Project) Log(ctx context.Context, follow bool, services ...string) error {
	return p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(nil, events.NoEvent, events.NoEvent, func(service Service) error {
			return service.Log(ctx, follow)
		})
	}), nil)
}
func (p *Project) Scale(ctx context.Context, timeout int, servicesScale map[string]int) error {
	order := make([]string, 0, 0)
	services := make(map[string]Service)
	for name := range servicesScale {
		if !p.ServiceConfigs.Has(name) {
			return fmt.Errorf("%s is not defined in the template", name)
		}
		service, err := p.CreateService(name)
		if err != nil {
			return fmt.Errorf("Failed to lookup service: %s: %v", service, err)
		}
		order = append(order, name)
		services[name] = service
	}
	for _, name := range order {
		scale := servicesScale[name]
		log.Infof("Setting scale %s=%d...", name, scale)
		err := services[name].Scale(ctx, scale, timeout)
		if err != nil {
			return fmt.Errorf("Failed to set the scale %s=%d: %v", name, scale, err)
		}
	}
	return nil
}
func (p *Project) Pull(ctx context.Context, services ...string) error {
	return p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
		wrapper.Do(nil, events.ServicePullStart, events.ServicePull, func(service Service) error {
			return service.Pull(ctx)
		})
	}), nil)
}