Update traefik dependencies (docker/docker and related) (#1823)

Update traefik dependencies (docker/docker and related)

- Update dependencies
- Fix compilation problems
- Remove vdemeester/docker-events (in docker api now)
- Remove `integration/vendor`
- Use `testImport`
- update some deps.
- regenerate the lock from scratch (after a `glide cc`)
This commit is contained in:
Vincent Demeester 2017-07-06 16:28:13 +02:00 committed by Ludovic Fernandez
parent 7d178f49b4
commit b7daa2f3a4
1301 changed files with 21476 additions and 150099 deletions

View file

@ -0,0 +1,13 @@
package project
import (
"golang.org/x/net/context"
)
// Container defines what a libcompose container provides.
type Container interface {
ID() string
Name() string
Port(ctx context.Context, port string) (string, error)
IsRunning(ctx context.Context) bool
}

141
vendor/github.com/docker/libcompose/project/context.go generated vendored Normal file
View file

@ -0,0 +1,141 @@
package project
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/logger"
)
var projectRegexp = regexp.MustCompile("[^a-zA-Z0-9_.-]")
// Context holds context meta information about a libcompose project, like
// the project name, the compose file, etc.
type Context struct {
ComposeFiles []string
ComposeBytes [][]byte
ProjectName string
isOpen bool
ServiceFactory ServiceFactory
NetworksFactory NetworksFactory
VolumesFactory VolumesFactory
EnvironmentLookup config.EnvironmentLookup
ResourceLookup config.ResourceLookup
LoggerFactory logger.Factory
IgnoreMissingConfig bool
Project *Project
}
func (c *Context) readComposeFiles() error {
if c.ComposeBytes != nil {
return nil
}
logrus.Debugf("Opening compose files: %s", strings.Join(c.ComposeFiles, ","))
// Handle STDIN (`-f -`)
if len(c.ComposeFiles) == 1 && c.ComposeFiles[0] == "-" {
composeBytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
logrus.Errorf("Failed to read compose file from stdin: %v", err)
return err
}
c.ComposeBytes = [][]byte{composeBytes}
return nil
}
for _, composeFile := range c.ComposeFiles {
composeBytes, err := ioutil.ReadFile(composeFile)
if err != nil && !os.IsNotExist(err) {
logrus.Errorf("Failed to open the compose file: %s", composeFile)
return err
}
if err != nil && !c.IgnoreMissingConfig {
logrus.Errorf("Failed to find the compose file: %s", composeFile)
return err
}
c.ComposeBytes = append(c.ComposeBytes, composeBytes)
}
return nil
}
func (c *Context) determineProject() error {
name, err := c.lookupProjectName()
if err != nil {
return err
}
c.ProjectName = normalizeName(name)
if c.ProjectName == "" {
return fmt.Errorf("Falied to determine project name")
}
return nil
}
func (c *Context) lookupProjectName() (string, error) {
if c.ProjectName != "" {
return c.ProjectName, nil
}
if envProject := os.Getenv("COMPOSE_PROJECT_NAME"); envProject != "" {
return envProject, nil
}
file := "."
if len(c.ComposeFiles) > 0 {
file = c.ComposeFiles[0]
}
f, err := filepath.Abs(file)
if err != nil {
logrus.Errorf("Failed to get absolute directory for: %s", file)
return "", err
}
f = toUnixPath(f)
parent := path.Base(path.Dir(f))
if parent != "" && parent != "." {
return parent, nil
} else if wd, err := os.Getwd(); err != nil {
return "", err
} else {
return path.Base(toUnixPath(wd)), nil
}
}
func normalizeName(name string) string {
r := regexp.MustCompile("[^a-z0-9]+")
return r.ReplaceAllString(strings.ToLower(name), "")
}
func toUnixPath(p string) string {
return strings.Replace(p, "\\", "/", -1)
}
func (c *Context) open() error {
if c.isOpen {
return nil
}
if err := c.readComposeFiles(); err != nil {
return err
}
if err := c.determineProject(); err != nil {
return err
}
c.isOpen = true
return nil
}

139
vendor/github.com/docker/libcompose/project/empty.go generated vendored Normal file
View file

@ -0,0 +1,139 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// this ensures EmptyService implements Service
// useful since it's easy to forget adding new functions to EmptyService
var _ Service = (*EmptyService)(nil)
// EmptyService is a struct that implements Service but does nothing.
type EmptyService struct {
}
// Create implements Service.Create but does nothing.
func (e *EmptyService) Create(ctx context.Context, options options.Create) error {
return nil
}
// Build implements Service.Build but does nothing.
func (e *EmptyService) Build(ctx context.Context, buildOptions options.Build) error {
return nil
}
// Up implements Service.Up but does nothing.
func (e *EmptyService) Up(ctx context.Context, options options.Up) error {
return nil
}
// Start implements Service.Start but does nothing.
func (e *EmptyService) Start(ctx context.Context) error {
return nil
}
// Stop implements Service.Stop() but does nothing.
func (e *EmptyService) Stop(ctx context.Context, timeout int) error {
return nil
}
// Delete implements Service.Delete but does nothing.
func (e *EmptyService) Delete(ctx context.Context, options options.Delete) error {
return nil
}
// Restart implements Service.Restart but does nothing.
func (e *EmptyService) Restart(ctx context.Context, timeout int) error {
return nil
}
// Log implements Service.Log but does nothing.
func (e *EmptyService) Log(ctx context.Context, follow bool) error {
return nil
}
// Pull implements Service.Pull but does nothing.
func (e *EmptyService) Pull(ctx context.Context) error {
return nil
}
// Kill implements Service.Kill but does nothing.
func (e *EmptyService) Kill(ctx context.Context, signal string) error {
return nil
}
// Containers implements Service.Containers but does nothing.
func (e *EmptyService) Containers(ctx context.Context) ([]Container, error) {
return []Container{}, nil
}
// Scale implements Service.Scale but does nothing.
func (e *EmptyService) Scale(ctx context.Context, count int, timeout int) error {
return nil
}
// Info implements Service.Info but does nothing.
func (e *EmptyService) Info(ctx context.Context) (InfoSet, error) {
return InfoSet{}, nil
}
// Pause implements Service.Pause but does nothing.
func (e *EmptyService) Pause(ctx context.Context) error {
return nil
}
// Unpause implements Service.Pause but does nothing.
func (e *EmptyService) Unpause(ctx context.Context) error {
return nil
}
// Run implements Service.Run but does nothing.
func (e *EmptyService) Run(ctx context.Context, commandParts []string, options options.Run) (int, error) {
return 0, nil
}
// RemoveImage implements Service.RemoveImage but does nothing.
func (e *EmptyService) RemoveImage(ctx context.Context, imageType options.ImageType) error {
return nil
}
// Events implements Service.Events but does nothing.
func (e *EmptyService) Events(ctx context.Context, events chan events.ContainerEvent) error {
return nil
}
// DependentServices implements Service.DependentServices with empty slice.
func (e *EmptyService) DependentServices() []ServiceRelationship {
return []ServiceRelationship{}
}
// Config implements Service.Config with empty config.
func (e *EmptyService) Config() *config.ServiceConfig {
return &config.ServiceConfig{}
}
// Name implements Service.Name with empty name.
func (e *EmptyService) Name() string {
return ""
}
// this ensures EmptyNetworks implements Networks
var _ Networks = (*EmptyNetworks)(nil)
// EmptyNetworks is a struct that implements Networks but does nothing.
type EmptyNetworks struct {
}
// Initialize implements Networks.Initialize but does nothing.
func (e *EmptyNetworks) Initialize(ctx context.Context) error {
return nil
}
// Remove implements Networks.Remove but does nothing.
func (e *EmptyNetworks) Remove(ctx context.Context) error {
return nil
}

View file

@ -0,0 +1,224 @@
// Package events holds event structures, methods and functions.
package events
import (
"fmt"
"time"
)
// Notifier defines the methods an event notifier should have.
type Notifier interface {
Notify(eventType EventType, serviceName string, data map[string]string)
}
// Emitter defines the methods an event emitter should have.
type Emitter interface {
AddListener(c chan<- Event)
}
// Event holds project-wide event informations.
type Event struct {
EventType EventType
ServiceName string
Data map[string]string
}
// ContainerEvent holds attributes of container events.
type ContainerEvent struct {
Service string `json:"service"`
Event string `json:"event"`
ID string `json:"id"`
Time time.Time `json:"time"`
Attributes map[string]string `json:"attributes"`
Type string `json:"type"`
}
// EventType defines a type of libcompose event.
type EventType int
// Definitions of libcompose events
const (
NoEvent = EventType(iota)
ContainerCreated = EventType(iota)
ContainerStarted = EventType(iota)
ServiceAdd = EventType(iota)
ServiceUpStart = EventType(iota)
ServiceUpIgnored = EventType(iota)
ServiceUp = EventType(iota)
ServiceCreateStart = EventType(iota)
ServiceCreate = EventType(iota)
ServiceDeleteStart = EventType(iota)
ServiceDelete = EventType(iota)
ServiceDownStart = EventType(iota)
ServiceDown = EventType(iota)
ServiceRestartStart = EventType(iota)
ServiceRestart = EventType(iota)
ServicePullStart = EventType(iota)
ServicePull = EventType(iota)
ServiceKillStart = EventType(iota)
ServiceKill = EventType(iota)
ServiceStartStart = EventType(iota)
ServiceStart = EventType(iota)
ServiceBuildStart = EventType(iota)
ServiceBuild = EventType(iota)
ServicePauseStart = EventType(iota)
ServicePause = EventType(iota)
ServiceUnpauseStart = EventType(iota)
ServiceUnpause = EventType(iota)
ServiceStopStart = EventType(iota)
ServiceStop = EventType(iota)
ServiceRunStart = EventType(iota)
ServiceRun = EventType(iota)
VolumeAdd = EventType(iota)
NetworkAdd = EventType(iota)
ProjectDownStart = EventType(iota)
ProjectDownDone = EventType(iota)
ProjectCreateStart = EventType(iota)
ProjectCreateDone = EventType(iota)
ProjectUpStart = EventType(iota)
ProjectUpDone = EventType(iota)
ProjectDeleteStart = EventType(iota)
ProjectDeleteDone = EventType(iota)
ProjectRestartStart = EventType(iota)
ProjectRestartDone = EventType(iota)
ProjectReload = EventType(iota)
ProjectReloadTrigger = EventType(iota)
ProjectKillStart = EventType(iota)
ProjectKillDone = EventType(iota)
ProjectStartStart = EventType(iota)
ProjectStartDone = EventType(iota)
ProjectBuildStart = EventType(iota)
ProjectBuildDone = EventType(iota)
ProjectPauseStart = EventType(iota)
ProjectPauseDone = EventType(iota)
ProjectUnpauseStart = EventType(iota)
ProjectUnpauseDone = EventType(iota)
ProjectStopStart = EventType(iota)
ProjectStopDone = EventType(iota)
)
func (e EventType) String() string {
var m string
switch e {
case ContainerCreated:
m = "Created container"
case ContainerStarted:
m = "Started container"
case ServiceAdd:
m = "Adding"
case ServiceUpStart:
m = "Starting"
case ServiceUpIgnored:
m = "Ignoring"
case ServiceUp:
m = "Started"
case ServiceCreateStart:
m = "Creating"
case ServiceCreate:
m = "Created"
case ServiceDeleteStart:
m = "Deleting"
case ServiceDelete:
m = "Deleted"
case ServiceStopStart:
m = "Stopping"
case ServiceStop:
m = "Stopped"
case ServiceDownStart:
m = "Stopping"
case ServiceDown:
m = "Stopped"
case ServiceRestartStart:
m = "Restarting"
case ServiceRestart:
m = "Restarted"
case ServicePullStart:
m = "Pulling"
case ServicePull:
m = "Pulled"
case ServiceKillStart:
m = "Killing"
case ServiceKill:
m = "Killed"
case ServiceStartStart:
m = "Starting"
case ServiceStart:
m = "Started"
case ServiceBuildStart:
m = "Building"
case ServiceBuild:
m = "Built"
case ServiceRunStart:
m = "Executing"
case ServiceRun:
m = "Executed"
case ServicePauseStart:
m = "Pausing"
case ServicePause:
m = "Paused"
case ServiceUnpauseStart:
m = "Unpausing"
case ServiceUnpause:
m = "Unpaused"
case ProjectDownStart:
m = "Stopping project"
case ProjectDownDone:
m = "Project stopped"
case ProjectStopStart:
m = "Stopping project"
case ProjectStopDone:
m = "Project stopped"
case ProjectCreateStart:
m = "Creating project"
case ProjectCreateDone:
m = "Project created"
case ProjectUpStart:
m = "Starting project"
case ProjectUpDone:
m = "Project started"
case ProjectDeleteStart:
m = "Deleting project"
case ProjectDeleteDone:
m = "Project deleted"
case ProjectRestartStart:
m = "Restarting project"
case ProjectRestartDone:
m = "Project restarted"
case ProjectReload:
m = "Reloading project"
case ProjectReloadTrigger:
m = "Triggering project reload"
case ProjectKillStart:
m = "Killing project"
case ProjectKillDone:
m = "Project killed"
case ProjectStartStart:
m = "Starting project"
case ProjectStartDone:
m = "Project started"
case ProjectBuildStart:
m = "Building project"
case ProjectBuildDone:
m = "Project built"
case ProjectPauseStart:
m = "Pausing project"
case ProjectPauseDone:
m = "Project paused"
case ProjectUnpauseStart:
m = "Unpausing project"
case ProjectUnpauseDone:
m = "Project unpaused"
}
if m == "" {
m = fmt.Sprintf("EventType: %d", int(e))
}
return m
}

48
vendor/github.com/docker/libcompose/project/info.go generated vendored Normal file
View file

@ -0,0 +1,48 @@
package project
import (
"bytes"
"io"
"text/tabwriter"
)
// InfoSet holds a list of Info.
type InfoSet []Info
// Info holds a list of InfoPart.
type Info map[string]string
func (infos InfoSet) String(columns []string, titleFlag bool) string {
//no error checking, none of this should fail
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
tabwriter := tabwriter.NewWriter(buffer, 4, 4, 2, ' ', 0)
first := true
for _, info := range infos {
if first && titleFlag {
writeLine(tabwriter, columns, true, info)
}
first = false
writeLine(tabwriter, columns, false, info)
}
tabwriter.Flush()
return buffer.String()
}
func writeLine(writer io.Writer, columns []string, key bool, info Info) {
first := true
for _, column := range columns {
if !first {
writer.Write([]byte{'\t'})
}
first = false
if key {
writer.Write([]byte(column))
} else {
writer.Write([]byte(info[column]))
}
}
writer.Write([]byte{'\n'})
}

View file

@ -0,0 +1,64 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// APIProject defines the methods a libcompose project should implement.
type APIProject interface {
events.Notifier
events.Emitter
Build(ctx context.Context, options options.Build, sevice ...string) error
Config() (string, error)
Create(ctx context.Context, options options.Create, services ...string) error
Delete(ctx context.Context, options options.Delete, services ...string) error
Down(ctx context.Context, options options.Down, services ...string) error
Events(ctx context.Context, services ...string) (chan events.ContainerEvent, error)
Kill(ctx context.Context, signal string, services ...string) error
Log(ctx context.Context, follow bool, services ...string) error
Pause(ctx context.Context, services ...string) error
Ps(ctx context.Context, services ...string) (InfoSet, error)
// FIXME(vdemeester) we could use nat.Port instead ?
Port(ctx context.Context, index int, protocol, serviceName, privatePort string) (string, error)
Pull(ctx context.Context, services ...string) error
Restart(ctx context.Context, timeout int, services ...string) error
Run(ctx context.Context, serviceName string, commandParts []string, options options.Run) (int, error)
Scale(ctx context.Context, timeout int, servicesScale map[string]int) error
Start(ctx context.Context, services ...string) error
Stop(ctx context.Context, timeout int, services ...string) error
Unpause(ctx context.Context, services ...string) error
Up(ctx context.Context, options options.Up, services ...string) error
Parse() error
CreateService(name string) (Service, error)
AddConfig(name string, config *config.ServiceConfig) error
Load(bytes []byte) error
Containers(ctx context.Context, filter Filter, services ...string) ([]string, error)
GetServiceConfig(service string) (*config.ServiceConfig, bool)
}
// Filter holds filter element to filter containers
type Filter struct {
State State
}
// State defines the supported state you can filter on
type State string
// Definitions of filter states
const (
AnyState = State("")
Running = State("running")
Stopped = State("stopped")
)
// RuntimeProject defines runtime-specific methods for a libcompose implementation.
type RuntimeProject interface {
RemoveOrphans(ctx context.Context, projectName string, serviceConfigs *config.ServiceConfigs) error
}

View file

@ -0,0 +1,81 @@
package project
import (
"bytes"
"github.com/Sirupsen/logrus"
"github.com/docker/libcompose/project/events"
)
var (
infoEvents = map[events.EventType]bool{
events.ServiceDeleteStart: true,
events.ServiceDelete: true,
events.ServiceDownStart: true,
events.ServiceDown: true,
events.ServiceStopStart: true,
events.ServiceStop: true,
events.ServiceKillStart: true,
events.ServiceKill: true,
events.ServiceCreateStart: true,
events.ServiceCreate: true,
events.ServiceStartStart: true,
events.ServiceStart: true,
events.ServiceRestartStart: true,
events.ServiceRestart: true,
events.ServiceUpStart: true,
events.ServiceUp: true,
events.ServicePauseStart: true,
events.ServicePause: true,
events.ServiceUnpauseStart: true,
events.ServiceUnpause: true,
}
)
type defaultListener struct {
project *Project
listenChan chan events.Event
upCount int
}
// NewDefaultListener create a default listener for the specified project.
func NewDefaultListener(p *Project) chan<- events.Event {
l := defaultListener{
listenChan: make(chan events.Event),
project: p,
}
go l.start()
return l.listenChan
}
func (d *defaultListener) start() {
for event := range d.listenChan {
buffer := bytes.NewBuffer(nil)
if event.Data != nil {
for k, v := range event.Data {
if buffer.Len() > 0 {
buffer.WriteString(", ")
}
buffer.WriteString(k)
buffer.WriteString("=")
buffer.WriteString(v)
}
}
if event.EventType == events.ServiceUp {
d.upCount++
}
logf := logrus.Debugf
if infoEvents[event.EventType] {
logf = logrus.Infof
}
if event.ServiceName == "" {
logf("Project [%s]: %s %s", d.project.Name, event.EventType, buffer.Bytes())
} else {
logf("[%d/%d] [%s]: %s %s", d.upCount, d.project.ServiceConfigs.Len(), event.ServiceName, event.EventType, buffer.Bytes())
}
}
}

19
vendor/github.com/docker/libcompose/project/network.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/config"
)
// Networks defines the methods a libcompose network aggregate should define.
type Networks interface {
Initialize(ctx context.Context) error
Remove(ctx context.Context) error
}
// NetworksFactory is an interface factory to create Networks object for the specified
// configurations (service, networks, …)
type NetworksFactory interface {
Create(projectName string, networkConfigs map[string]*config.NetworkConfig, serviceConfigs *config.ServiceConfigs, networkEnabled bool) (Networks, error)
}

View file

@ -0,0 +1,52 @@
package options
// Build holds options of compose build.
type Build struct {
NoCache bool
ForceRemove bool
Pull bool
}
// Delete holds options of compose rm.
type Delete struct {
RemoveVolume bool
RemoveRunning bool
}
// Down holds options of compose down.
type Down struct {
RemoveVolume bool
RemoveImages ImageType
RemoveOrphans bool
}
// Create holds options of compose create.
type Create struct {
NoRecreate bool
ForceRecreate bool
NoBuild bool
ForceBuild bool
}
// Run holds options of compose run.
type Run struct {
Detached bool
}
// Up holds options of compose up.
type Up struct {
Create
}
// ImageType defines the type of image (local, all)
type ImageType string
// Valid indicates whether the image type is valid.
func (i ImageType) Valid() bool {
switch string(i) {
case "", "local", "all":
return true
default:
return false
}
}

559
vendor/github.com/docker/libcompose/project/project.go generated vendored Normal file
View file

@ -0,0 +1,559 @@
package project
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"golang.org/x/net/context"
log "github.com/Sirupsen/logrus"
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/logger"
"github.com/docker/libcompose/lookup"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/utils"
"github.com/docker/libcompose/yaml"
)
// ComposeVersion is name of docker-compose.yml file syntax supported version
const ComposeVersion = "1.5.0"
type wrapperAction func(*serviceWrapper, map[string]*serviceWrapper)
type serviceAction func(service Service) error
// Project holds libcompose project information.
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
runtime RuntimeProject
networks Networks
volumes Volumes
configVersion string
context *Context
reload []string
upCount int
listeners []chan<- events.Event
hasListeners bool
}
// NewProject creates a new project with the specified context.
func NewProject(context *Context, runtime RuntimeProject, parseOptions *config.ParseOptions) *Project {
p := &Project{
context: context,
runtime: runtime,
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{}
}
if context.ResourceLookup == nil {
context.ResourceLookup = &lookup.FileResourceLookup{}
}
if context.EnvironmentLookup == nil {
var envPath, absPath, cwd string
var err error
if len(context.ComposeFiles) > 0 {
absPath, err = filepath.Abs(context.ComposeFiles[0])
dir, _ := path.Split(absPath)
envPath = filepath.Join(dir, ".env")
} else {
cwd, err = os.Getwd()
envPath = filepath.Join(cwd, ".env")
}
if err != nil {
log.Errorf("Could not get the rooted path name to the current directory: %v", err)
return nil
}
context.EnvironmentLookup = &lookup.ComposableEnvLookup{
Lookups: []config.EnvironmentLookup{
&lookup.EnvfileLookup{
Path: envPath,
},
&lookup.OsEnvLookup{},
},
}
}
context.Project = p
p.listeners = []chan<- events.Event{NewDefaultListener(p)}
return p
}
// Parse populates project information based on its context. It sets up the name,
// the composefile and the composebytes (the composefile content).
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
}
// CreateService creates a service with the specified name based. If there
// is no config in the project for this service, it will return an error.
func (p *Project) CreateService(name string) (Service, error) {
existing, ok := p.GetServiceConfig(name)
if !ok {
return nil, fmt.Errorf("Failed to find service: %s", name)
}
// Copy because we are about to modify the environment
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 {
parsedEnv = append(parsedEnv, env)
continue
} else {
env = parts[0]
}
for _, value := range p.context.EnvironmentLookup.Lookup(env, &config) {
parsedEnv = append(parsedEnv, value)
}
}
config.Environment = parsedEnv
// check the environment for extra build Args that are set but not given a value in the compose file
for arg, value := range config.Build.Args {
if *value == "\x00" {
envValue := p.context.EnvironmentLookup.Lookup(arg, &config)
// depending on what we get back we do different things
switch l := len(envValue); l {
case 0:
delete(config.Build.Args, arg)
case 1:
parts := strings.SplitN(envValue[0], "=", 2)
config.Build.Args[parts[0]] = &parts[1]
default:
return nil, fmt.Errorf("tried to set Build Arg %#v to multi-value %#v", arg, envValue)
}
}
}
}
return p.context.ServiceFactory.Create(p, name, &config)
}
// AddConfig adds the specified service config for the specified name.
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
}
// AddVolumeConfig adds the specified volume config for the specified name.
func (p *Project) AddVolumeConfig(name string, config *config.VolumeConfig) error {
p.Notify(events.VolumeAdd, name, nil)
p.VolumeConfigs[name] = config
return nil
}
// AddNetworkConfig adds the specified network config for the specified name.
func (p *Project) AddNetworkConfig(name string, config *config.NetworkConfig) error {
p.Notify(events.NetworkAdd, name, nil)
p.NetworkConfigs[name] = config
return nil
}
// Load loads the specified byte array (the composefile content) and adds the
// service configuration to the project.
// FIXME is it needed ?
func (p *Project) Load(bytes []byte) error {
return p.load("", bytes)
}
func (p *Project) load(file string, bytes []byte) error {
version, 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
}
p.configVersion = version
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
}
}
for name, config := range serviceConfigs {
err := p.AddConfig(name, config)
if err != nil {
return err
}
}
// Update network configuration a little bit
p.handleNetworkConfig()
p.handleVolumeConfig()
if p.context.NetworksFactory != nil {
networks, err := p.context.NetworksFactory.Create(p.Name, p.NetworkConfigs, p.ServiceConfigs, p.isNetworkEnabled())
if err != nil {
return err
}
p.networks = networks
}
if p.context.VolumesFactory != nil {
volumes, err := p.context.VolumesFactory.Create(p.Name, p.VolumeConfigs, p.ServiceConfigs, p.isVolumeEnabled())
if err != nil {
return err
}
p.volumes = volumes
}
return nil
}
func (p *Project) handleNetworkConfig() {
if p.isNetworkEnabled() {
for _, serviceName := range p.ServiceConfigs.Keys() {
serviceConfig, _ := p.ServiceConfigs.Get(serviceName)
if serviceConfig.NetworkMode != "" {
continue
}
if serviceConfig.Networks == nil || len(serviceConfig.Networks.Networks) == 0 {
// Add default as network
serviceConfig.Networks = &yaml.Networks{
Networks: []*yaml.Network{
{
Name: "default",
RealName: fmt.Sprintf("%s_%s", p.Name, "default"),
},
},
}
p.AddNetworkConfig("default", &config.NetworkConfig{})
}
// Consolidate the name of the network
// FIXME(vdemeester) probably shouldn't be there, maybe move that to interface/factory
for _, network := range serviceConfig.Networks.Networks {
net, ok := p.NetworkConfigs[network.Name]
if ok && net != nil {
if net.External.External {
network.RealName = network.Name
if net.External.Name != "" {
network.RealName = net.External.Name
}
} else {
network.RealName = p.Name + "_" + network.Name
}
} else {
network.RealName = p.Name + "_" + network.Name
p.NetworkConfigs[network.Name] = &config.NetworkConfig{
External: yaml.External{External: false},
}
}
// Ignoring if we don't find the network, it will be catched later
}
}
}
}
func (p *Project) isNetworkEnabled() bool {
return p.configVersion == "2"
}
func (p *Project) handleVolumeConfig() {
if p.isVolumeEnabled() {
for _, serviceName := range p.ServiceConfigs.Keys() {
serviceConfig, _ := p.ServiceConfigs.Get(serviceName)
// Consolidate the name of the volume
// FIXME(vdemeester) probably shouldn't be there, maybe move that to interface/factory
if serviceConfig.Volumes == nil {
continue
}
for _, volume := range serviceConfig.Volumes.Volumes {
if !IsNamedVolume(volume.Source) {
continue
}
vol, ok := p.VolumeConfigs[volume.Source]
if !ok || vol == nil {
continue
}
if vol.External.External {
if vol.External.Name != "" {
volume.Source = vol.External.Name
}
} else {
volume.Source = p.Name + "_" + volume.Source
}
}
}
}
}
func (p *Project) isVolumeEnabled() bool {
return p.configVersion == "2"
}
// initialize sets up required element for project before any action (on project and service).
// This means it's not needed to be called on Config for example.
func (p *Project) initialize(ctx context.Context) error {
if p.networks != nil {
if err := p.networks.Initialize(ctx); err != nil {
return err
}
}
if p.volumes != nil {
if err := p.volumes.Initialize(ctx); 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) perform(start, done events.EventType, services []string, action wrapperAction, cycleAction serviceAction) error {
p.Notify(start, "", nil)
err := p.forEach(services, action, cycleAction)
p.Notify(done, "", nil)
return err
}
func isSelected(wrapper *serviceWrapper, selected map[string]bool) bool {
return len(selected) == 0 || selected[wrapper.name]
}
func (p *Project) forEach(services []string, action wrapperAction, cycleAction serviceAction) error {
selected := make(map[string]bool)
wrappers := make(map[string]*serviceWrapper)
for _, s := range services {
selected[s] = true
}
return p.traverse(true, selected, wrappers, action, cycleAction)
}
func (p *Project) startService(wrappers map[string]*serviceWrapper, history []string, selected, launched map[string]bool, wrapper *serviceWrapper, action wrapperAction, cycleAction serviceAction) error {
if launched[wrapper.name] {
return nil
}
launched[wrapper.name] = true
history = append(history, wrapper.name)
for _, dep := range wrapper.service.DependentServices() {
target := wrappers[dep.Target]
if target == nil {
log.Debugf("Failed to find %s", dep.Target)
return fmt.Errorf("Service '%s' has a link to service '%s' which is undefined", wrapper.name, dep.Target)
}
if utils.Contains(history, dep.Target) {
cycle := strings.Join(append(history, dep.Target), "->")
if dep.Optional {
log.Debugf("Ignoring cycle for %s", cycle)
wrapper.IgnoreDep(dep.Target)
if cycleAction != nil {
var err error
log.Debugf("Running cycle action for %s", cycle)
err = cycleAction(target.service)
if err != nil {
return err
}
}
} else {
return fmt.Errorf("Cycle detected in path %s", cycle)
}
continue
}
err := p.startService(wrappers, history, selected, launched, target, action, cycleAction)
if err != nil {
return err
}
}
if isSelected(wrapper, selected) {
log.Debugf("Launching action for %s", wrapper.name)
go action(wrapper, wrappers)
} else {
wrapper.Ignore()
}
return nil
}
func (p *Project) traverse(start bool, selected map[string]bool, wrappers map[string]*serviceWrapper, action wrapperAction, cycleAction serviceAction) error {
restart := false
wrapperList := []string{}
if start {
for _, name := range p.ServiceConfigs.Keys() {
wrapperList = append(wrapperList, name)
}
} else {
for _, wrapper := range wrappers {
if err := wrapper.Reset(); err != nil {
return err
}
}
wrapperList = p.reload
}
p.loadWrappers(wrappers, wrapperList)
p.reload = []string{}
// check service name
for s := range selected {
if wrappers[s] == nil {
return errors.New("No such service: " + s)
}
}
launched := map[string]bool{}
for _, wrapper := range wrappers {
if err := p.startService(wrappers, []string{}, selected, launched, wrapper, action, cycleAction); err != nil {
return err
}
}
var firstError error
for _, wrapper := range wrappers {
if !isSelected(wrapper, selected) {
continue
}
if err := wrapper.Wait(); err == ErrRestart {
restart = true
} else if err != nil {
log.Errorf("Failed to start: %s : %v", wrapper.name, err)
if firstError == nil {
firstError = err
}
}
}
if restart {
if p.ReloadCallback != nil {
if err := p.ReloadCallback(); err != nil {
log.Errorf("Failed calling callback: %v", err)
}
}
return p.traverse(false, selected, wrappers, action, cycleAction)
}
return firstError
}
// AddListener adds the specified listener to the project.
// This implements implicitly events.Emitter.
func (p *Project) AddListener(c chan<- events.Event) {
if !p.hasListeners {
for _, l := range p.listeners {
close(l)
}
p.hasListeners = true
p.listeners = []chan<- events.Event{c}
} else {
p.listeners = append(p.listeners, c)
}
}
// Notify notifies all project listener with the specified eventType, service name and datas.
// This implements implicitly events.Notifier interface.
func (p *Project) Notify(eventType events.EventType, serviceName string, data map[string]string) {
if eventType == events.NoEvent {
return
}
event := events.Event{
EventType: eventType,
ServiceName: serviceName,
Data: data,
}
for _, l := range p.listeners {
l <- event
}
}
// GetServiceConfig looks up a service config for a given service name, returning the ServiceConfig
// object and a bool flag indicating whether it was found
func (p *Project) GetServiceConfig(name string) (*config.ServiceConfig, bool) {
return p.ServiceConfigs.Get(name)
}
// IsNamedVolume returns whether the specified volume (string) is a named volume or not.
func IsNamedVolume(volume string) bool {
return !strings.HasPrefix(volume, ".") && !strings.HasPrefix(volume, "/") && !strings.HasPrefix(volume, "~")
}

View file

@ -0,0 +1,17 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Build builds the specified services (like docker build).
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)
}

View file

@ -0,0 +1,27 @@
package project
import (
"github.com/docker/libcompose/config"
"gopkg.in/yaml.v2"
)
// ExportedConfig holds config attribute that will be exported
type ExportedConfig struct {
Version string `yaml:"version,omitempty"`
Services map[string]*config.ServiceConfig `yaml:"services"`
Volumes map[string]*config.VolumeConfig `yaml:"volumes"`
Networks map[string]*config.NetworkConfig `yaml:"networks"`
}
// Config validates and print the compose file.
func (p *Project) Config() (string, error) {
cfg := ExportedConfig{
Version: "2.0",
Services: p.ServiceConfigs.All(),
Volumes: p.VolumeConfigs,
Networks: p.NetworkConfigs,
}
bytes, err := yaml.Marshal(cfg)
return string(bytes), err
}

View file

@ -0,0 +1,49 @@
package project
import (
"fmt"
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Containers lists the containers for the specified services. Can be filter using
// the Filter struct.
func (p *Project) Containers(ctx context.Context, filter Filter, services ...string) ([]string, error) {
containers := []string{}
err := p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(nil, events.NoEvent, events.NoEvent, func(service Service) error {
serviceContainers, innerErr := service.Containers(ctx)
if innerErr != nil {
return innerErr
}
for _, container := range serviceContainers {
running := container.IsRunning(ctx)
switch filter.State {
case Running:
if !running {
continue
}
case Stopped:
if running {
continue
}
case AnyState:
// Don't do a thing
default:
// Invalid state filter
return fmt.Errorf("Invalid container filter: %s", filter.State)
}
containerID := container.ID()
containers = append(containers, containerID)
}
return nil
})
}), nil)
if err != nil {
return nil, err
}
return containers, nil
}

View file

@ -0,0 +1,25 @@
package project
import (
"fmt"
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Create creates the specified services (like docker create).
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")
}
if err := p.initialize(ctx); err != nil {
return err
}
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)
}

View file

@ -0,0 +1,17 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Delete removes the specified services (like docker rm).
func (p *Project) Delete(ctx context.Context, options options.Delete, services ...string) error {
return p.perform(events.ProjectDeleteStart, events.ProjectDeleteDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(nil, events.ServiceDeleteStart, events.ServiceDelete, func(service Service) error {
return service.Delete(ctx, options)
})
}), nil)
}

View file

@ -0,0 +1,56 @@
package project
import (
"fmt"
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Down stops the specified services and clean related containers (like docker stop + docker rm).
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.runtime.RemoveOrphans(ctx, p.Name, p.ServiceConfigs); err != nil {
return err
}
}
if err := p.Delete(ctx, options.Delete{
RemoveVolume: opts.RemoveVolume,
}, services...); err != nil {
return err
}
networks, err := p.context.NetworksFactory.Create(p.Name, p.NetworkConfigs, p.ServiceConfigs, p.isNetworkEnabled())
if err != nil {
return err
}
if err := networks.Remove(ctx); err != nil {
return err
}
if opts.RemoveVolume {
volumes, err := p.context.VolumesFactory.Create(p.Name, p.VolumeConfigs, p.ServiceConfigs, p.isVolumeEnabled())
if err != nil {
return err
}
if err := volumes.Remove(ctx); 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{})
})
}

View file

@ -0,0 +1,24 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Events listen for real time events from containers (of the project).
func (p *Project) Events(ctx context.Context, services ...string) (chan events.ContainerEvent, error) {
events := make(chan events.ContainerEvent)
if len(services) == 0 {
services = p.ServiceConfigs.Keys()
}
// FIXME(vdemeester) handle errors (chan) here
for _, service := range services {
s, err := p.CreateService(service)
if err != nil {
return nil, err
}
go s.Events(ctx, events)
}
return events, nil
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Kill kills the specified services (like docker kill).
func (p *Project) Kill(ctx context.Context, signal string, services ...string) error {
return p.perform(events.ProjectKillStart, events.ProjectKillDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(nil, events.ServiceKillStart, events.ServiceKill, func(service Service) error {
return service.Kill(ctx, signal)
})
}), nil)
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Log aggregates and prints out the logs for the specified services.
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)
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Pause pauses the specified services containers (like docker pause).
func (p *Project) Pause(ctx context.Context, services ...string) error {
return p.perform(events.ProjectPauseStart, events.ProjectPauseDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(nil, events.ServicePauseStart, events.ServicePause, func(service Service) error {
return service.Pause(ctx)
})
}), nil)
}

View file

@ -0,0 +1,26 @@
package project
import (
"fmt"
"golang.org/x/net/context"
)
// Port returns the public port for a port binding of the specified service.
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))
}

View file

@ -0,0 +1,28 @@
package project
import "golang.org/x/net/context"
// Ps list containers for the specified services.
func (p *Project) Ps(ctx context.Context, services ...string) (InfoSet, error) {
allInfo := InfoSet{}
if len(services) == 0 {
services = p.ServiceConfigs.Keys()
}
for _, name := range services {
service, err := p.CreateService(name)
if err != nil {
return nil, err
}
info, err := service.Info(ctx)
if err != nil {
return nil, err
}
allInfo = append(allInfo, info...)
}
return allInfo, nil
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Pull pulls the specified services (like docker pull).
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)
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Restart restarts the specified services (like docker restart).
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)
}

View file

@ -0,0 +1,35 @@
package project
import (
"fmt"
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Run executes a one off command (like `docker run image command`).
func (p *Project) Run(ctx context.Context, serviceName string, commandParts []string, opts options.Run) (int, error) {
if !p.ServiceConfigs.Has(serviceName) {
return 1, fmt.Errorf("%s is not defined in the template", serviceName)
}
if err := p.initialize(ctx); err != nil {
return 1, err
}
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, opts)
exitCode = code
return err
}
return nil
})
}), func(service Service) error {
return service.Create(ctx, options.Create{})
})
return exitCode, err
}

View file

@ -0,0 +1,40 @@
package project
import (
"fmt"
"golang.org/x/net/context"
log "github.com/Sirupsen/logrus"
)
// Scale scales the specified services.
func (p *Project) Scale(ctx context.Context, timeout int, servicesScale map[string]int) error {
// This code is a bit verbose but I wanted to parse everything up front
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
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Start starts the specified services (like docker start).
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)
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Stop stops the specified services (like docker stop).
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)
}

View file

@ -0,0 +1,16 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
)
// Unpause pauses the specified services containers (like docker pause).
func (p *Project) Unpause(ctx context.Context, services ...string) error {
return p.perform(events.ProjectUnpauseStart, events.ProjectUnpauseDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(nil, events.ServiceUnpauseStart, events.ServiceUnpause, func(service Service) error {
return service.Unpause(ctx)
})
}), nil)
}

View file

@ -0,0 +1,22 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Up creates and starts the specified services (kinda like docker run).
func (p *Project) Up(ctx context.Context, options options.Up, services ...string) error {
if err := p.initialize(ctx); err != nil {
return err
}
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)
})
}

View file

@ -0,0 +1,115 @@
package project
import (
"sync"
log "github.com/Sirupsen/logrus"
"github.com/docker/libcompose/project/events"
)
type serviceWrapper struct {
name string
service Service
done sync.WaitGroup
state ServiceState
err error
project *Project
noWait bool
ignored map[string]bool
}
func newServiceWrapper(name string, p *Project) (*serviceWrapper, error) {
wrapper := &serviceWrapper{
name: name,
state: StateUnknown,
project: p,
ignored: map[string]bool{},
}
return wrapper, wrapper.Reset()
}
func (s *serviceWrapper) IgnoreDep(name string) {
s.ignored[name] = true
}
func (s *serviceWrapper) Reset() error {
if s.state != StateExecuted {
service, err := s.project.CreateService(s.name)
if err != nil {
log.Errorf("Failed to create service for %s : %v", s.name, err)
return err
}
s.service = service
}
if s.err == ErrRestart {
s.err = nil
}
s.done.Add(1)
return nil
}
func (s *serviceWrapper) Ignore() {
defer s.done.Done()
s.state = StateExecuted
s.project.Notify(events.ServiceUpIgnored, s.service.Name(), nil)
}
func (s *serviceWrapper) waitForDeps(wrappers map[string]*serviceWrapper) bool {
if s.noWait {
return true
}
for _, dep := range s.service.DependentServices() {
if s.ignored[dep.Target] {
continue
}
if wrapper, ok := wrappers[dep.Target]; ok {
if wrapper.Wait() == ErrRestart {
s.project.Notify(events.ProjectReload, wrapper.service.Name(), nil)
s.err = ErrRestart
return false
}
} else {
log.Errorf("Failed to find %s", dep.Target)
}
}
return true
}
func (s *serviceWrapper) Do(wrappers map[string]*serviceWrapper, start, done events.EventType, action func(service Service) error) {
defer s.done.Done()
if s.state == StateExecuted {
return
}
if wrappers != nil && !s.waitForDeps(wrappers) {
return
}
s.state = StateExecuted
s.project.Notify(start, s.service.Name(), nil)
s.err = action(s.service)
if s.err == ErrRestart {
s.project.Notify(done, s.service.Name(), nil)
s.project.Notify(events.ProjectReloadTrigger, s.service.Name(), nil)
} else if s.err != nil {
log.Errorf("Failed %s %s : %v", start, s.name, s.err)
} else {
s.project.Notify(done, s.service.Name(), nil)
}
}
func (s *serviceWrapper) Wait() error {
s.done.Wait()
return s.err
}

97
vendor/github.com/docker/libcompose/project/service.go generated vendored Normal file
View file

@ -0,0 +1,97 @@
package project
import (
"errors"
"golang.org/x/net/context"
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/project/events"
"github.com/docker/libcompose/project/options"
)
// Service defines what a libcompose service provides.
type Service interface {
Build(ctx context.Context, buildOptions options.Build) error
Create(ctx context.Context, options options.Create) error
Delete(ctx context.Context, options options.Delete) error
Events(ctx context.Context, messages chan events.ContainerEvent) error
Info(ctx context.Context) (InfoSet, error)
Log(ctx context.Context, follow bool) error
Kill(ctx context.Context, signal string) error
Pause(ctx context.Context) error
Pull(ctx context.Context) error
Restart(ctx context.Context, timeout int) error
Run(ctx context.Context, commandParts []string, options options.Run) (int, error)
Scale(ctx context.Context, count int, timeout int) error
Start(ctx context.Context) error
Stop(ctx context.Context, timeout int) error
Unpause(ctx context.Context) error
Up(ctx context.Context, options options.Up) error
RemoveImage(ctx context.Context, imageType options.ImageType) error
Containers(ctx context.Context) ([]Container, error)
DependentServices() []ServiceRelationship
Config() *config.ServiceConfig
Name() string
}
// ServiceState holds the state of a service.
type ServiceState string
// State definitions
var (
StateExecuted = ServiceState("executed")
StateUnknown = ServiceState("unknown")
)
// Error definitions
var (
ErrRestart = errors.New("Restart execution")
ErrUnsupported = errors.New("UnsupportedOperation")
)
// ServiceFactory is an interface factory to create Service object for the specified
// project, with the specified name and service configuration.
type ServiceFactory interface {
Create(project *Project, name string, serviceConfig *config.ServiceConfig) (Service, error)
}
// ServiceRelationshipType defines the type of service relationship.
type ServiceRelationshipType string
// RelTypeLink means the services are linked (docker links).
const RelTypeLink = ServiceRelationshipType("")
// RelTypeNetNamespace means the services share the same network namespace.
const RelTypeNetNamespace = ServiceRelationshipType("netns")
// RelTypeIpcNamespace means the service share the same ipc namespace.
const RelTypeIpcNamespace = ServiceRelationshipType("ipc")
// RelTypeVolumesFrom means the services share some volumes.
const RelTypeVolumesFrom = ServiceRelationshipType("volumesFrom")
// RelTypeDependsOn means the dependency was explicitly set using 'depends_on'.
const RelTypeDependsOn = ServiceRelationshipType("dependsOn")
// RelTypeNetworkMode means the services depends on another service on networkMode
const RelTypeNetworkMode = ServiceRelationshipType("networkMode")
// ServiceRelationship holds the relationship information between two services.
type ServiceRelationship struct {
Target, Alias string
Type ServiceRelationshipType
Optional bool
}
// NewServiceRelationship creates a new Relationship based on the specified alias
// and relationship type.
func NewServiceRelationship(nameAlias string, relType ServiceRelationshipType) ServiceRelationship {
name, alias := NameAlias(nameAlias)
return ServiceRelationship{
Target: name,
Alias: alias,
Type: relType,
}
}

47
vendor/github.com/docker/libcompose/project/utils.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
package project
import (
"strings"
)
// DefaultDependentServices return the dependent services (as an array of ServiceRelationship)
// for the specified project and service. It looks for : links, volumesFrom, net and ipc configuration.
func DefaultDependentServices(p *Project, s Service) []ServiceRelationship {
config := s.Config()
if config == nil {
return []ServiceRelationship{}
}
result := []ServiceRelationship{}
for _, link := range config.Links {
result = append(result, NewServiceRelationship(link, RelTypeLink))
}
for _, volumesFrom := range config.VolumesFrom {
result = append(result, NewServiceRelationship(volumesFrom, RelTypeVolumesFrom))
}
for _, dependsOn := range config.DependsOn {
result = append(result, NewServiceRelationship(dependsOn, RelTypeDependsOn))
}
if config.NetworkMode != "" {
if strings.HasPrefix(config.NetworkMode, "service:") {
serviceName := config.NetworkMode[8:]
result = append(result, NewServiceRelationship(serviceName, RelTypeNetworkMode))
}
}
return result
}
// NameAlias returns the name and alias based on the specified string.
// If the name contains a colon (like name:alias) it will split it, otherwise
// it will return the specified name as name and alias.
func NameAlias(name string) (string, string) {
parts := strings.SplitN(name, ":", 2)
if len(parts) == 2 {
return parts[0], parts[1]
}
return parts[0], parts[0]
}

19
vendor/github.com/docker/libcompose/project/volume.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/config"
)
// Volumes defines the methods a libcompose volume aggregate should define.
type Volumes interface {
Initialize(ctx context.Context) error
Remove(ctx context.Context) error
}
// VolumesFactory is an interface factory to create Volumes object for the specified
// configurations (service, volumes, …)
type VolumesFactory interface {
Create(projectName string, volumeConfigs map[string]*config.VolumeConfig, serviceConfigs *config.ServiceConfigs, volumeEnabled bool) (Volumes, error)
}