mirror of
https://github.com/rancher/os.git
synced 2025-09-09 10:40:30 +00:00
Bump libcompose and its dependencies
This commit is contained in:
35
vendor/github.com/docker/libcompose/project/client_factory.go
generated
vendored
Normal file
35
vendor/github.com/docker/libcompose/project/client_factory.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"github.com/docker/engine-api/client"
|
||||
composeclient "github.com/docker/libcompose/docker/client"
|
||||
)
|
||||
|
||||
// ClientFactory is a factory to create docker clients.
|
||||
type ClientFactory interface {
|
||||
// Create constructs a Docker client for the given service. The passed in
|
||||
// config may be nil in which case a generic client for the project should
|
||||
// be returned.
|
||||
Create(service Service) client.APIClient
|
||||
}
|
||||
|
||||
type defaultClientFactory struct {
|
||||
client client.APIClient
|
||||
}
|
||||
|
||||
// NewDefaultClientFactory creates and returns the default client factory that uses
|
||||
// github.com/docker/engine-api client.
|
||||
func NewDefaultClientFactory(opts composeclient.Options) (ClientFactory, error) {
|
||||
client, err := composeclient.Create(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &defaultClientFactory{
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *defaultClientFactory) Create(service Service) client.APIClient {
|
||||
return s.client
|
||||
}
|
13
vendor/github.com/docker/libcompose/project/container.go
generated
vendored
Normal file
13
vendor/github.com/docker/libcompose/project/container.go
generated
vendored
Normal 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, error)
|
||||
Name() string
|
||||
Port(ctx context.Context, port string) (string, error)
|
||||
IsRunning(ctx context.Context) (bool, error)
|
||||
}
|
69
vendor/github.com/docker/libcompose/project/context.go
generated
vendored
69
vendor/github.com/docker/libcompose/project/context.go
generated
vendored
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcompose/config"
|
||||
"github.com/docker/libcompose/logger"
|
||||
)
|
||||
|
||||
@@ -18,51 +19,47 @@ 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 {
|
||||
Timeout uint
|
||||
Log bool
|
||||
Volume bool
|
||||
ForceRecreate bool
|
||||
NoRecreate bool
|
||||
Signal int
|
||||
ComposeFile string
|
||||
ComposeBytes []byte
|
||||
ComposeFiles []string
|
||||
ComposeBytes [][]byte
|
||||
ProjectName string
|
||||
isOpen bool
|
||||
ServiceFactory ServiceFactory
|
||||
EnvironmentLookup EnvironmentLookup
|
||||
ConfigLookup ConfigLookup
|
||||
EnvironmentLookup config.EnvironmentLookup
|
||||
ResourceLookup config.ResourceLookup
|
||||
LoggerFactory logger.Factory
|
||||
IgnoreMissingConfig bool
|
||||
Project *Project
|
||||
}
|
||||
|
||||
func (c *Context) readComposeFile() error {
|
||||
func (c *Context) readComposeFiles() error {
|
||||
if c.ComposeBytes != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
logrus.Debugf("Opening compose file: %s", c.ComposeFile)
|
||||
logrus.Debugf("Opening compose files: %s", strings.Join(c.ComposeFiles, ","))
|
||||
|
||||
if c.ComposeFile == "-" {
|
||||
// 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 = composeBytes
|
||||
} else if c.ComposeFile != "" {
|
||||
if composeBytes, err := ioutil.ReadFile(c.ComposeFile); os.IsNotExist(err) {
|
||||
if c.IgnoreMissingConfig {
|
||||
return nil
|
||||
}
|
||||
logrus.Errorf("Failed to find %s", c.ComposeFile)
|
||||
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
|
||||
} else if err != nil {
|
||||
logrus.Errorf("Failed to open %s", c.ComposeFile)
|
||||
return err
|
||||
} else {
|
||||
c.ComposeBytes = composeBytes
|
||||
}
|
||||
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
|
||||
@@ -74,16 +71,12 @@ func (c *Context) determineProject() error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ProjectName = projectRegexp.ReplaceAllString(strings.ToLower(name), "-")
|
||||
c.ProjectName = normalizeName(name)
|
||||
|
||||
if c.ProjectName == "" {
|
||||
return fmt.Errorf("Falied to determine project name")
|
||||
}
|
||||
|
||||
if strings.ContainsAny(c.ProjectName[0:1], "_.-") {
|
||||
c.ProjectName = "x" + c.ProjectName
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,9 +89,14 @@ func (c *Context) lookupProjectName() (string, error) {
|
||||
return envProject, nil
|
||||
}
|
||||
|
||||
f, err := filepath.Abs(c.ComposeFile)
|
||||
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", c.ComposeFile)
|
||||
logrus.Errorf("Failed to get absolute directory for: %s", file)
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -114,6 +112,11 @@ func (c *Context) lookupProjectName() (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -123,7 +126,7 @@ func (c *Context) open() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.readComposeFile(); err != nil {
|
||||
if err := c.readComposeFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
54
vendor/github.com/docker/libcompose/project/empty.go
generated
vendored
54
vendor/github.com/docker/libcompose/project/empty.go
generated
vendored
@@ -1,70 +1,96 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/libcompose/project/options"
|
||||
)
|
||||
|
||||
// EmptyService is a struct that implements Service but does nothing.
|
||||
type EmptyService struct {
|
||||
}
|
||||
|
||||
// Create implements Service.Create but does nothing.
|
||||
func (e *EmptyService) Create() error {
|
||||
func (e *EmptyService) Create(ctx context.Context, options options.Create) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build implements Service.Build but does nothing.
|
||||
func (e *EmptyService) Build() error {
|
||||
func (e *EmptyService) Build(ctx context.Context, buildOptions options.Build) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Up implements Service.Up but does nothing.
|
||||
func (e *EmptyService) Up() error {
|
||||
func (e *EmptyService) Up(ctx context.Context, options options.Up) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start implements Service.Start but does nothing.
|
||||
func (e *EmptyService) Start() error {
|
||||
func (e *EmptyService) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Down implements Service.Down but does nothing.
|
||||
func (e *EmptyService) Down() error {
|
||||
// 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() error {
|
||||
func (e *EmptyService) Delete(ctx context.Context, options options.Delete) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart implements Service.Restart but does nothing.
|
||||
func (e *EmptyService) Restart() error {
|
||||
func (e *EmptyService) Restart(ctx context.Context, timeout int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log implements Service.Log but does nothing.
|
||||
func (e *EmptyService) Log() error {
|
||||
func (e *EmptyService) Log(ctx context.Context, follow bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pull implements Service.Pull but does nothing.
|
||||
func (e *EmptyService) Pull() error {
|
||||
func (e *EmptyService) Pull(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kill implements Service.Kill but does nothing.
|
||||
func (e *EmptyService) Kill() error {
|
||||
func (e *EmptyService) Kill(ctx context.Context, signal string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Containers implements Service.Containers but does nothing.
|
||||
func (e *EmptyService) Containers() ([]Container, error) {
|
||||
func (e *EmptyService) Containers(ctx context.Context) ([]Container, error) {
|
||||
return []Container{}, nil
|
||||
}
|
||||
|
||||
// Scale implements Service.Scale but does nothing.
|
||||
func (e *EmptyService) Scale(count int) error {
|
||||
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(qFlag bool) (InfoSet, error) {
|
||||
func (e *EmptyService) Info(ctx context.Context, qFlag bool) (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) (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
|
||||
}
|
||||
|
197
vendor/github.com/docker/libcompose/project/events/events.go
generated
vendored
Normal file
197
vendor/github.com/docker/libcompose/project/events/events.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
// Package events holds event structures, methods and functions.
|
||||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 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"
|
||||
}
|
||||
|
||||
if m == "" {
|
||||
m = fmt.Sprintf("EventType: %d", int(e))
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
106
vendor/github.com/docker/libcompose/project/hash.go
generated
vendored
106
vendor/github.com/docker/libcompose/project/hash.go
generated
vendored
@@ -1,106 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// GetServiceHash computes and returns a hash that will identify a service.
|
||||
// This hash will be then used to detect if the service definition/configuration
|
||||
// have changed and needs to be recreated.
|
||||
func GetServiceHash(name string, config *ServiceConfig) string {
|
||||
hash := sha1.New()
|
||||
|
||||
io.WriteString(hash, name)
|
||||
|
||||
//Get values of Service through reflection
|
||||
val := reflect.ValueOf(config).Elem()
|
||||
|
||||
//Create slice to sort the keys in Service Config, which allow constant hash ordering
|
||||
serviceKeys := []string{}
|
||||
|
||||
//Create a data structure of map of values keyed by a string
|
||||
unsortedKeyValue := make(map[string]interface{})
|
||||
|
||||
//Get all keys and values in Service Configuration
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
valueField := val.Field(i)
|
||||
keyField := val.Type().Field(i)
|
||||
|
||||
serviceKeys = append(serviceKeys, keyField.Name)
|
||||
unsortedKeyValue[keyField.Name] = valueField.Interface()
|
||||
}
|
||||
|
||||
//Sort serviceKeys alphabetically
|
||||
sort.Strings(serviceKeys)
|
||||
|
||||
//Go through keys and write hash
|
||||
for _, serviceKey := range serviceKeys {
|
||||
serviceValue := unsortedKeyValue[serviceKey]
|
||||
|
||||
io.WriteString(hash, fmt.Sprintf("\n %v: ", serviceKey))
|
||||
|
||||
switch s := serviceValue.(type) {
|
||||
case SliceorMap:
|
||||
sliceKeys := []string{}
|
||||
for lkey := range s.MapParts() {
|
||||
sliceKeys = append(sliceKeys, lkey)
|
||||
}
|
||||
sort.Strings(sliceKeys)
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s=%v, ", sliceKey, s.MapParts()[sliceKey]))
|
||||
}
|
||||
case MaporEqualSlice:
|
||||
sliceKeys := s.Slice()
|
||||
// do not sort keys as the order matters
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
|
||||
}
|
||||
case MaporColonSlice:
|
||||
sliceKeys := s.Slice()
|
||||
// do not sort keys as the order matters
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
|
||||
}
|
||||
case MaporSpaceSlice:
|
||||
sliceKeys := s.Slice()
|
||||
// do not sort keys as the order matters
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
|
||||
}
|
||||
case Command:
|
||||
sliceKeys := s.Slice()
|
||||
// do not sort keys as the order matters
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
|
||||
}
|
||||
case Stringorslice:
|
||||
sliceKeys := s.Slice()
|
||||
sort.Strings(sliceKeys)
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
|
||||
}
|
||||
case []string:
|
||||
sliceKeys := s
|
||||
sort.Strings(sliceKeys)
|
||||
|
||||
for _, sliceKey := range sliceKeys {
|
||||
io.WriteString(hash, fmt.Sprintf("%s, ", sliceKey))
|
||||
}
|
||||
default:
|
||||
io.WriteString(hash, fmt.Sprintf("%v", serviceValue))
|
||||
}
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
11
vendor/github.com/docker/libcompose/project/info.go
generated
vendored
11
vendor/github.com/docker/libcompose/project/info.go
generated
vendored
@@ -6,6 +6,17 @@ import (
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
// InfoPart holds key/value strings.
|
||||
type InfoPart struct {
|
||||
Key, Value string
|
||||
}
|
||||
|
||||
// InfoSet holds a list of Info.
|
||||
type InfoSet []Info
|
||||
|
||||
// Info holds a list of InfoPart.
|
||||
type Info []InfoPart
|
||||
|
||||
func (infos InfoSet) String(titleFlag bool) string {
|
||||
//no error checking, none of this should fail
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
|
39
vendor/github.com/docker/libcompose/project/interface.go
generated
vendored
Normal file
39
vendor/github.com/docker/libcompose/project/interface.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
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 is an interface defining the methods a libcompose project should implement.
|
||||
type APIProject interface {
|
||||
events.Notifier
|
||||
events.Emitter
|
||||
|
||||
Build(ctx context.Context, options options.Build, sevice ...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
|
||||
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, onlyID bool, 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) (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
|
||||
}
|
179
vendor/github.com/docker/libcompose/project/interpolation.go
generated
vendored
179
vendor/github.com/docker/libcompose/project/interpolation.go
generated
vendored
@@ -1,179 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func isNum(c uint8) bool {
|
||||
return c >= '0' && c <= '9'
|
||||
}
|
||||
|
||||
func validVariableNameChar(c uint8) bool {
|
||||
return c == '_' ||
|
||||
c >= 'A' && c <= 'Z' ||
|
||||
c >= 'a' && c <= 'z' ||
|
||||
isNum(c)
|
||||
}
|
||||
|
||||
func parseVariable(line string, pos int, mapping func(string) string) (string, int, bool) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for ; pos < len(line); pos++ {
|
||||
c := line[pos]
|
||||
|
||||
switch {
|
||||
case validVariableNameChar(c):
|
||||
buffer.WriteByte(c)
|
||||
default:
|
||||
return mapping(buffer.String()), pos - 1, true
|
||||
}
|
||||
}
|
||||
|
||||
return mapping(buffer.String()), pos, true
|
||||
}
|
||||
|
||||
func parseVariableWithBraces(line string, pos int, mapping func(string) string) (string, int, bool) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for ; pos < len(line); pos++ {
|
||||
c := line[pos]
|
||||
|
||||
switch {
|
||||
case c == '}':
|
||||
bufferString := buffer.String()
|
||||
|
||||
if bufferString == "" {
|
||||
return "", 0, false
|
||||
}
|
||||
|
||||
return mapping(buffer.String()), pos, true
|
||||
case validVariableNameChar(c):
|
||||
buffer.WriteByte(c)
|
||||
default:
|
||||
return "", 0, false
|
||||
}
|
||||
}
|
||||
|
||||
return "", 0, false
|
||||
}
|
||||
|
||||
func parseInterpolationExpression(line string, pos int, mapping func(string) string) (string, int, bool) {
|
||||
c := line[pos]
|
||||
|
||||
switch {
|
||||
case c == '$':
|
||||
return "$", pos, true
|
||||
case c == '{':
|
||||
return parseVariableWithBraces(line, pos+1, mapping)
|
||||
case !isNum(c) && validVariableNameChar(c):
|
||||
// Variables can't start with a number
|
||||
return parseVariable(line, pos, mapping)
|
||||
default:
|
||||
return "", 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func parseLine(line string, mapping func(string) string) (string, bool) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for pos := 0; pos < len(line); pos++ {
|
||||
c := line[pos]
|
||||
switch {
|
||||
case c == '$':
|
||||
var replaced string
|
||||
var success bool
|
||||
|
||||
replaced, pos, success = parseInterpolationExpression(line, pos+1, mapping)
|
||||
|
||||
if !success {
|
||||
return "", false
|
||||
}
|
||||
|
||||
buffer.WriteString(replaced)
|
||||
default:
|
||||
buffer.WriteByte(c)
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.String(), true
|
||||
}
|
||||
|
||||
func parseConfig(option, service string, data *interface{}, mapping func(string) string) error {
|
||||
switch typedData := (*data).(type) {
|
||||
case string:
|
||||
var success bool
|
||||
|
||||
interpolatedLine, success := parseLine(typedData, mapping)
|
||||
|
||||
if !success {
|
||||
return fmt.Errorf("Invalid interpolation format for \"%s\" option in service \"%s\": \"%s\"", option, service, typedData)
|
||||
}
|
||||
|
||||
// If possible, convert the value to an integer
|
||||
// If the type should be a string and not an int, go-yaml will convert it back into a string
|
||||
lineAsInteger, err := strconv.Atoi(interpolatedLine)
|
||||
|
||||
if err == nil {
|
||||
*data = lineAsInteger
|
||||
} else {
|
||||
*data = interpolatedLine
|
||||
}
|
||||
case []interface{}:
|
||||
for k, v := range typedData {
|
||||
err := parseConfig(option, service, &v, mapping)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
typedData[k] = v
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
for k, v := range typedData {
|
||||
err := parseConfig(option, service, &v, mapping)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
typedData[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func interpolate(environmentLookup EnvironmentLookup, config *rawServiceMap) error {
|
||||
for k, v := range *config {
|
||||
for k2, v2 := range v {
|
||||
err := parseConfig(k2, k, &v2, func(s string) string {
|
||||
values := environmentLookup.Lookup(s, k, nil)
|
||||
|
||||
if len(values) == 0 {
|
||||
logrus.Warnf("The %s variable is not set. Substituting a blank string.", s)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Use first result if many are given
|
||||
value := values[0]
|
||||
|
||||
// Environment variables come in key=value format
|
||||
// Return everything past first '='
|
||||
return strings.SplitN(value, "=", 2)[1]
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*config)[k][k2] = v2
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
226
vendor/github.com/docker/libcompose/project/interpolation_test.go
generated
vendored
226
vendor/github.com/docker/libcompose/project/interpolation_test.go
generated
vendored
@@ -1,226 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testInterpolatedLine(t *testing.T, expectedLine, interpolatedLine string, envVariables map[string]string) {
|
||||
interpolatedLine, _ = parseLine(interpolatedLine, func(s string) string {
|
||||
return envVariables[s]
|
||||
})
|
||||
|
||||
assert.Equal(t, expectedLine, interpolatedLine)
|
||||
}
|
||||
|
||||
func testInvalidInterpolatedLine(t *testing.T, line string) {
|
||||
_, success := parseLine(line, func(string) string {
|
||||
return ""
|
||||
})
|
||||
|
||||
assert.Equal(t, false, success)
|
||||
}
|
||||
|
||||
func TestParseLine(t *testing.T) {
|
||||
variables := map[string]string{
|
||||
"A": "ABC",
|
||||
"X": "XYZ",
|
||||
"E": "",
|
||||
"lower": "WORKED",
|
||||
"MiXeD": "WORKED",
|
||||
"split_VaLue": "WORKED",
|
||||
"9aNumber": "WORKED",
|
||||
"a9Number": "WORKED",
|
||||
}
|
||||
|
||||
testInterpolatedLine(t, "WORKED", "$lower", variables)
|
||||
testInterpolatedLine(t, "WORKED", "${MiXeD}", variables)
|
||||
testInterpolatedLine(t, "WORKED", "${split_VaLue}", variables)
|
||||
// Starting with a number isn't valid
|
||||
testInterpolatedLine(t, "", "$9aNumber", variables)
|
||||
testInterpolatedLine(t, "WORKED", "$a9Number", variables)
|
||||
|
||||
testInterpolatedLine(t, "ABC", "$A", variables)
|
||||
testInterpolatedLine(t, "ABC", "${A}", variables)
|
||||
|
||||
testInterpolatedLine(t, "ABC DE", "$A DE", variables)
|
||||
testInterpolatedLine(t, "ABCDE", "${A}DE", variables)
|
||||
|
||||
testInterpolatedLine(t, "$A", "$$A", variables)
|
||||
testInterpolatedLine(t, "${A}", "$${A}", variables)
|
||||
|
||||
testInterpolatedLine(t, "$ABC", "$$${A}", variables)
|
||||
testInterpolatedLine(t, "$ABC", "$$$A", variables)
|
||||
|
||||
testInterpolatedLine(t, "ABC XYZ", "$A $X", variables)
|
||||
testInterpolatedLine(t, "ABCXYZ", "$A$X", variables)
|
||||
testInterpolatedLine(t, "ABCXYZ", "${A}${X}", variables)
|
||||
|
||||
testInterpolatedLine(t, "", "$B", variables)
|
||||
testInterpolatedLine(t, "", "${B}", variables)
|
||||
testInterpolatedLine(t, "", "$ADE", variables)
|
||||
|
||||
testInterpolatedLine(t, "", "$E", variables)
|
||||
testInterpolatedLine(t, "", "${E}", variables)
|
||||
|
||||
testInvalidInterpolatedLine(t, "${")
|
||||
testInvalidInterpolatedLine(t, "$}")
|
||||
testInvalidInterpolatedLine(t, "${}")
|
||||
testInvalidInterpolatedLine(t, "${ }")
|
||||
testInvalidInterpolatedLine(t, "${A }")
|
||||
testInvalidInterpolatedLine(t, "${ A}")
|
||||
testInvalidInterpolatedLine(t, "${A!}")
|
||||
testInvalidInterpolatedLine(t, "$!")
|
||||
}
|
||||
|
||||
type MockEnvironmentLookup struct {
|
||||
Variables map[string]string
|
||||
}
|
||||
|
||||
func (m MockEnvironmentLookup) Lookup(key, serviceName string, config *ServiceConfig) []string {
|
||||
return []string{fmt.Sprintf("%s=%s", key, m.Variables[key])}
|
||||
}
|
||||
|
||||
func testInterpolatedConfig(t *testing.T, expectedConfig, interpolatedConfig string, envVariables map[string]string) {
|
||||
for k, v := range envVariables {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
|
||||
expectedConfigBytes := []byte(expectedConfig)
|
||||
interpolatedConfigBytes := []byte(interpolatedConfig)
|
||||
|
||||
expectedData := make(rawServiceMap)
|
||||
interpolatedData := make(rawServiceMap)
|
||||
|
||||
yaml.Unmarshal(expectedConfigBytes, &expectedData)
|
||||
yaml.Unmarshal(interpolatedConfigBytes, &interpolatedData)
|
||||
|
||||
_ = interpolate(MockEnvironmentLookup{envVariables}, &interpolatedData)
|
||||
|
||||
for k := range envVariables {
|
||||
os.Unsetenv(k)
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedData, interpolatedData)
|
||||
}
|
||||
|
||||
func testInvalidInterpolatedConfig(t *testing.T, interpolatedConfig string) {
|
||||
interpolatedConfigBytes := []byte(interpolatedConfig)
|
||||
interpolatedData := make(rawServiceMap)
|
||||
yaml.Unmarshal(interpolatedConfigBytes, &interpolatedData)
|
||||
|
||||
err := interpolate(new(MockEnvironmentLookup), &interpolatedData)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestInterpolate(t *testing.T) {
|
||||
testInterpolatedConfig(t,
|
||||
`web:
|
||||
# unbracketed name
|
||||
image: busybox
|
||||
|
||||
# array element
|
||||
ports:
|
||||
- "80:8000"
|
||||
|
||||
# dictionary item value
|
||||
labels:
|
||||
mylabel: "myvalue"
|
||||
|
||||
# unset value
|
||||
hostname: "host-"
|
||||
|
||||
# escaped interpolation
|
||||
command: "${ESCAPED}"`,
|
||||
`web:
|
||||
# unbracketed name
|
||||
image: $IMAGE
|
||||
|
||||
# array element
|
||||
ports:
|
||||
- "${HOST_PORT}:8000"
|
||||
|
||||
# dictionary item value
|
||||
labels:
|
||||
mylabel: "${LABEL_VALUE}"
|
||||
|
||||
# unset value
|
||||
hostname: "host-${UNSET_VALUE}"
|
||||
|
||||
# escaped interpolation
|
||||
command: "$${ESCAPED}"`, map[string]string{
|
||||
"IMAGE": "busybox",
|
||||
"HOST_PORT": "80",
|
||||
"LABEL_VALUE": "myvalue",
|
||||
})
|
||||
|
||||
// Same as above, but testing with equal signs in variables
|
||||
testInterpolatedConfig(t,
|
||||
`web:
|
||||
# unbracketed name
|
||||
image: =busybox
|
||||
|
||||
# array element
|
||||
ports:
|
||||
- "=:8000"
|
||||
|
||||
# dictionary item value
|
||||
labels:
|
||||
mylabel: "myvalue=="
|
||||
|
||||
# unset value
|
||||
hostname: "host-"
|
||||
|
||||
# escaped interpolation
|
||||
command: "${ESCAPED}"`,
|
||||
`web:
|
||||
# unbracketed name
|
||||
image: $IMAGE
|
||||
|
||||
# array element
|
||||
ports:
|
||||
- "${HOST_PORT}:8000"
|
||||
|
||||
# dictionary item value
|
||||
labels:
|
||||
mylabel: "${LABEL_VALUE}"
|
||||
|
||||
# unset value
|
||||
hostname: "host-${UNSET_VALUE}"
|
||||
|
||||
# escaped interpolation
|
||||
command: "$${ESCAPED}"`, map[string]string{
|
||||
"IMAGE": "=busybox",
|
||||
"HOST_PORT": "=",
|
||||
"LABEL_VALUE": "myvalue==",
|
||||
})
|
||||
|
||||
testInvalidInterpolatedConfig(t,
|
||||
`web:
|
||||
image: "${"`)
|
||||
|
||||
testInvalidInterpolatedConfig(t,
|
||||
`web:
|
||||
image: busybox
|
||||
|
||||
# array element
|
||||
ports:
|
||||
- "${}:8000"`)
|
||||
|
||||
testInvalidInterpolatedConfig(t,
|
||||
`web:
|
||||
image: busybox
|
||||
|
||||
# array element
|
||||
ports:
|
||||
- "80:8000"
|
||||
|
||||
# dictionary item value
|
||||
labels:
|
||||
mylabel: "${ LABEL_VALUE}"`)
|
||||
}
|
45
vendor/github.com/docker/libcompose/project/listener.go
generated
vendored
45
vendor/github.com/docker/libcompose/project/listener.go
generated
vendored
@@ -4,39 +4,40 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcompose/project/events"
|
||||
)
|
||||
|
||||
var (
|
||||
infoEvents = map[EventType]bool{
|
||||
EventProjectDeleteDone: true,
|
||||
EventProjectDeleteStart: true,
|
||||
EventProjectDownDone: true,
|
||||
EventProjectDownStart: true,
|
||||
EventProjectRestartDone: true,
|
||||
EventProjectRestartStart: true,
|
||||
EventProjectUpDone: true,
|
||||
EventProjectUpStart: true,
|
||||
EventServiceDeleteStart: true,
|
||||
EventServiceDelete: true,
|
||||
EventServiceDownStart: true,
|
||||
EventServiceDown: true,
|
||||
EventServiceRestartStart: true,
|
||||
EventServiceRestart: true,
|
||||
EventServiceUpStart: true,
|
||||
EventServiceUp: true,
|
||||
infoEvents = map[events.EventType]bool{
|
||||
events.ProjectDeleteDone: true,
|
||||
events.ProjectDeleteStart: true,
|
||||
events.ProjectDownDone: true,
|
||||
events.ProjectDownStart: true,
|
||||
events.ProjectRestartDone: true,
|
||||
events.ProjectRestartStart: true,
|
||||
events.ProjectUpDone: true,
|
||||
events.ProjectUpStart: true,
|
||||
events.ServiceDeleteStart: true,
|
||||
events.ServiceDelete: true,
|
||||
events.ServiceDownStart: true,
|
||||
events.ServiceDown: true,
|
||||
events.ServiceRestartStart: true,
|
||||
events.ServiceRestart: true,
|
||||
events.ServiceUpStart: true,
|
||||
events.ServiceUp: true,
|
||||
}
|
||||
)
|
||||
|
||||
type defaultListener struct {
|
||||
project *Project
|
||||
listenChan chan Event
|
||||
listenChan chan events.Event
|
||||
upCount int
|
||||
}
|
||||
|
||||
// NewDefaultListener create a default listener for the specified project.
|
||||
func NewDefaultListener(p *Project) chan<- Event {
|
||||
func NewDefaultListener(p *Project) chan<- events.Event {
|
||||
l := defaultListener{
|
||||
listenChan: make(chan Event),
|
||||
listenChan: make(chan events.Event),
|
||||
project: p,
|
||||
}
|
||||
go l.start()
|
||||
@@ -57,7 +58,7 @@ func (d *defaultListener) start() {
|
||||
}
|
||||
}
|
||||
|
||||
if event.EventType == EventServiceUp {
|
||||
if event.EventType == events.ServiceUp {
|
||||
d.upCount++
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ func (d *defaultListener) start() {
|
||||
if event.ServiceName == "" {
|
||||
logf("Project [%s]: %s %s", d.project.Name, event.EventType, buffer.Bytes())
|
||||
} else {
|
||||
logf("[%d/%d] [%s]: %s %s", d.upCount, len(d.project.Configs), event.ServiceName, event.EventType, buffer.Bytes())
|
||||
logf("[%d/%d] [%s]: %s %s", d.upCount, d.project.ServiceConfigs.Len(), event.ServiceName, event.EventType, buffer.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
299
vendor/github.com/docker/libcompose/project/merge.go
generated
vendored
299
vendor/github.com/docker/libcompose/project/merge.go
generated
vendored
@@ -1,299 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
"github.com/docker/libcompose/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
// ValidRemotes list the of valid prefixes that can be sent to Docker as a build remote location
|
||||
// This is public for consumers of libcompose to use
|
||||
ValidRemotes = []string{
|
||||
"git://",
|
||||
"git@github.com:",
|
||||
"github.com",
|
||||
"http:",
|
||||
"https:",
|
||||
}
|
||||
noMerge = []string{
|
||||
"links",
|
||||
"volumes_from",
|
||||
}
|
||||
)
|
||||
|
||||
type rawService map[string]interface{}
|
||||
type rawServiceMap map[string]rawService
|
||||
|
||||
func mergeProject(p *Project, bytes []byte) (map[string]*ServiceConfig, error) {
|
||||
configs := make(map[string]*ServiceConfig)
|
||||
|
||||
datas := make(rawServiceMap)
|
||||
if err := yaml.Unmarshal(bytes, &datas); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := interpolate(p.context.EnvironmentLookup, &datas); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for name, data := range datas {
|
||||
data, err := parse(p.context.ConfigLookup, p.context.EnvironmentLookup, p.File, data, datas)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to parse service %s: %v", name, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
datas[name] = data
|
||||
}
|
||||
|
||||
if err := utils.Convert(datas, &configs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adjustValues(configs)
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func adjustValues(configs map[string]*ServiceConfig) {
|
||||
// yaml parser turns "no" into "false" but that is not valid for a restart policy
|
||||
for _, v := range configs {
|
||||
if v.Restart == "false" {
|
||||
v.Restart = "no"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readEnvFile(configLookup ConfigLookup, inFile string, serviceData rawService) (rawService, error) {
|
||||
var config ServiceConfig
|
||||
|
||||
if err := utils.Convert(serviceData, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(config.EnvFile.Slice()) == 0 {
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
if configLookup == nil {
|
||||
return nil, fmt.Errorf("Can not use env_file in file %s no mechanism provided to load files", inFile)
|
||||
}
|
||||
|
||||
vars := config.Environment.Slice()
|
||||
|
||||
for i := len(config.EnvFile.Slice()) - 1; i >= 0; i-- {
|
||||
envFile := config.EnvFile.Slice()[i]
|
||||
content, _, err := configLookup.Lookup(envFile, inFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(content))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
key := strings.SplitAfter(line, "=")[0]
|
||||
|
||||
found := false
|
||||
for _, v := range vars {
|
||||
if strings.HasPrefix(v, key) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
vars = append(vars, line)
|
||||
}
|
||||
}
|
||||
|
||||
if scanner.Err() != nil {
|
||||
return nil, scanner.Err()
|
||||
}
|
||||
}
|
||||
|
||||
serviceData["environment"] = vars
|
||||
|
||||
delete(serviceData, "env_file")
|
||||
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
func resolveBuild(inFile string, serviceData rawService) (rawService, error) {
|
||||
|
||||
build := asString(serviceData["build"])
|
||||
if build == "" {
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
for _, remote := range ValidRemotes {
|
||||
if strings.HasPrefix(build, remote) {
|
||||
return serviceData, nil
|
||||
}
|
||||
}
|
||||
|
||||
current := path.Dir(inFile)
|
||||
|
||||
if build == "." {
|
||||
build = current
|
||||
} else {
|
||||
current = path.Join(current, build)
|
||||
}
|
||||
|
||||
serviceData["build"] = current
|
||||
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
func parse(configLookup ConfigLookup, environmentLookup EnvironmentLookup, inFile string, serviceData rawService, datas rawServiceMap) (rawService, error) {
|
||||
serviceData, err := readEnvFile(configLookup, inFile, serviceData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceData, err = resolveBuild(inFile, serviceData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, ok := serviceData["extends"]
|
||||
if !ok {
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
mapValue, ok := value.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
if configLookup == nil {
|
||||
return nil, fmt.Errorf("Can not use extends in file %s no mechanism provided to files", inFile)
|
||||
}
|
||||
|
||||
file := asString(mapValue["file"])
|
||||
service := asString(mapValue["service"])
|
||||
|
||||
if service == "" {
|
||||
return serviceData, nil
|
||||
}
|
||||
|
||||
var baseService rawService
|
||||
|
||||
if file == "" {
|
||||
if serviceData, ok := datas[service]; ok {
|
||||
baseService, err = parse(configLookup, environmentLookup, inFile, serviceData, datas)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Failed to find service %s to extend", service)
|
||||
}
|
||||
} else {
|
||||
bytes, resolved, err := configLookup.Lookup(file, inFile)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to lookup file %s: %v", file, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var baseRawServices rawServiceMap
|
||||
if err := yaml.Unmarshal(bytes, &baseRawServices); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = interpolate(environmentLookup, &baseRawServices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseService, ok = baseRawServices[service]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)
|
||||
}
|
||||
|
||||
baseService, err = parse(configLookup, environmentLookup, resolved, baseService, baseRawServices)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseService = clone(baseService)
|
||||
|
||||
logrus.Debugf("Merging %#v, %#v", baseService, serviceData)
|
||||
|
||||
for _, k := range noMerge {
|
||||
if _, ok := baseService[k]; ok {
|
||||
source := file
|
||||
if source == "" {
|
||||
source = inFile
|
||||
}
|
||||
return nil, fmt.Errorf("Cannot extend service '%s' in %s: services with '%s' cannot be extended", service, source, k)
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range serviceData {
|
||||
// Image and build are mutually exclusive in merge
|
||||
if k == "image" {
|
||||
delete(baseService, "build")
|
||||
} else if k == "build" {
|
||||
delete(baseService, "image")
|
||||
}
|
||||
existing, ok := baseService[k]
|
||||
if ok {
|
||||
baseService[k] = merge(existing, v)
|
||||
} else {
|
||||
baseService[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Debugf("Merged result %#v", baseService)
|
||||
|
||||
return baseService, nil
|
||||
}
|
||||
|
||||
func merge(existing, value interface{}) interface{} {
|
||||
// append strings
|
||||
if left, lok := existing.([]interface{}); lok {
|
||||
if right, rok := value.([]interface{}); rok {
|
||||
return append(left, right...)
|
||||
}
|
||||
}
|
||||
|
||||
//merge maps
|
||||
if left, lok := existing.(map[interface{}]interface{}); lok {
|
||||
if right, rok := value.(map[interface{}]interface{}); rok {
|
||||
newLeft := make(map[interface{}]interface{})
|
||||
for k, v := range left {
|
||||
newLeft[k] = v
|
||||
}
|
||||
for k, v := range right {
|
||||
newLeft[k] = v
|
||||
}
|
||||
return newLeft
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func clone(in rawService) rawService {
|
||||
result := rawService{}
|
||||
for k, v := range in {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func asString(obj interface{}) string {
|
||||
if v, ok := obj.(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
192
vendor/github.com/docker/libcompose/project/merge_test.go
generated
vendored
192
vendor/github.com/docker/libcompose/project/merge_test.go
generated
vendored
@@ -1,192 +0,0 @@
|
||||
package project
|
||||
|
||||
import "testing"
|
||||
|
||||
type NullLookup struct {
|
||||
}
|
||||
|
||||
func (n *NullLookup) Lookup(file, relativeTo string) ([]byte, string, error) {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
func TestExtendsInheritImage(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ConfigLookup: &NullLookup{},
|
||||
})
|
||||
|
||||
config, err := mergeProject(p, []byte(`
|
||||
parent:
|
||||
image: foo
|
||||
child:
|
||||
extends:
|
||||
service: parent
|
||||
`))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parent := config["parent"]
|
||||
child := config["child"]
|
||||
|
||||
if parent.Image != "foo" {
|
||||
t.Fatal("Invalid image", parent.Image)
|
||||
}
|
||||
|
||||
if child.Build != "" {
|
||||
t.Fatal("Invalid build", child.Build)
|
||||
}
|
||||
|
||||
if child.Image != "foo" {
|
||||
t.Fatal("Invalid image", child.Image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtendsInheritBuild(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ConfigLookup: &NullLookup{},
|
||||
})
|
||||
|
||||
config, err := mergeProject(p, []byte(`
|
||||
parent:
|
||||
build: .
|
||||
child:
|
||||
extends:
|
||||
service: parent
|
||||
`))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parent := config["parent"]
|
||||
child := config["child"]
|
||||
|
||||
if parent.Build != "." {
|
||||
t.Fatal("Invalid build", parent.Build)
|
||||
}
|
||||
|
||||
if child.Build != "." {
|
||||
t.Fatal("Invalid build", child.Build)
|
||||
}
|
||||
|
||||
if child.Image != "" {
|
||||
t.Fatal("Invalid image", child.Image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtendBuildOverImage(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ConfigLookup: &NullLookup{},
|
||||
})
|
||||
|
||||
config, err := mergeProject(p, []byte(`
|
||||
parent:
|
||||
image: foo
|
||||
child:
|
||||
build: .
|
||||
extends:
|
||||
service: parent
|
||||
`))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parent := config["parent"]
|
||||
child := config["child"]
|
||||
|
||||
if parent.Image != "foo" {
|
||||
t.Fatal("Invalid image", parent.Image)
|
||||
}
|
||||
|
||||
if child.Build != "." {
|
||||
t.Fatal("Invalid build", child.Build)
|
||||
}
|
||||
|
||||
if child.Image != "" {
|
||||
t.Fatal("Invalid image", child.Image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtendImageOverBuild(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ConfigLookup: &NullLookup{},
|
||||
})
|
||||
|
||||
config, err := mergeProject(p, []byte(`
|
||||
parent:
|
||||
build: .
|
||||
child:
|
||||
image: foo
|
||||
extends:
|
||||
service: parent
|
||||
`))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parent := config["parent"]
|
||||
child := config["child"]
|
||||
|
||||
if parent.Image != "" {
|
||||
t.Fatal("Invalid image", parent.Image)
|
||||
}
|
||||
|
||||
if parent.Build != "." {
|
||||
t.Fatal("Invalid build", parent.Build)
|
||||
}
|
||||
|
||||
if child.Build != "" {
|
||||
t.Fatal("Invalid build", child.Build)
|
||||
}
|
||||
|
||||
if child.Image != "foo" {
|
||||
t.Fatal("Invalid image", child.Image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestartNo(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ConfigLookup: &NullLookup{},
|
||||
})
|
||||
|
||||
config, err := mergeProject(p, []byte(`
|
||||
test:
|
||||
restart: no
|
||||
image: foo
|
||||
`))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
test := config["test"]
|
||||
|
||||
if test.Restart != "no" {
|
||||
t.Fatal("Invalid restart policy", test.Restart)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestartAlways(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ConfigLookup: &NullLookup{},
|
||||
})
|
||||
|
||||
config, err := mergeProject(p, []byte(`
|
||||
test:
|
||||
restart: always
|
||||
image: foo
|
||||
`))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
test := config["test"]
|
||||
|
||||
if test.Restart != "always" {
|
||||
t.Fatal("Invalid restart policy", test.Restart)
|
||||
}
|
||||
}
|
47
vendor/github.com/docker/libcompose/project/options/types.go
generated
vendored
Normal file
47
vendor/github.com/docker/libcompose/project/options/types.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
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
|
||||
BeforeDeleteCallback func([]string) 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
473
vendor/github.com/docker/libcompose/project/project.go
generated
vendored
473
vendor/github.com/docker/libcompose/project/project.go
generated
vendored
@@ -5,41 +5,49 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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")
|
||||
)
|
||||
|
||||
// Event holds project-wide event informations.
|
||||
type Event struct {
|
||||
EventType EventType
|
||||
ServiceName string
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
type wrapperAction func(*serviceWrapper, map[string]*serviceWrapper)
|
||||
type serviceAction func(service Service) error
|
||||
|
||||
// NewProject create a new project with the specified context.
|
||||
func NewProject(context *Context) *Project {
|
||||
// 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
|
||||
|
||||
context *Context
|
||||
clientFactory ClientFactory
|
||||
reload []string
|
||||
upCount int
|
||||
listeners []chan<- events.Event
|
||||
hasListeners bool
|
||||
}
|
||||
|
||||
// NewProject creates a new project with the specified context.
|
||||
func NewProject(clientFactory ClientFactory, context *Context, parseOptions *config.ParseOptions) *Project {
|
||||
p := &Project{
|
||||
context: context,
|
||||
Configs: make(map[string]*ServiceConfig),
|
||||
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 {
|
||||
@@ -48,7 +56,7 @@ func NewProject(context *Context) *Project {
|
||||
|
||||
context.Project = p
|
||||
|
||||
p.listeners = []chan<- Event{NewDefaultListener(p)}
|
||||
p.listeners = []chan<- events.Event{NewDefaultListener(p)}
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -63,23 +71,31 @@ func (p *Project) Parse() error {
|
||||
|
||||
p.Name = p.context.ProjectName
|
||||
|
||||
if p.context.ComposeFile == "-" {
|
||||
p.File = "."
|
||||
} else {
|
||||
p.File = p.context.ComposeFile
|
||||
p.Files = p.context.ComposeFiles
|
||||
|
||||
if len(p.Files) == 1 && p.Files[0] == "-" {
|
||||
p.Files = []string{"."}
|
||||
}
|
||||
|
||||
if p.context.ComposeBytes != nil {
|
||||
return p.Load(p.context.ComposeBytes)
|
||||
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. It there
|
||||
// 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.Configs[name]
|
||||
existing, ok := p.ServiceConfigs.Get(name)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Failed to find service: %s", name)
|
||||
}
|
||||
@@ -88,9 +104,9 @@ func (p *Project) CreateService(name string) (Service, error) {
|
||||
config := *existing
|
||||
|
||||
if p.context.EnvironmentLookup != nil {
|
||||
parsedEnv := make([]string, 0, len(config.Environment.Slice()))
|
||||
parsedEnv := make([]string, 0, len(config.Environment))
|
||||
|
||||
for _, env := range config.Environment.Slice() {
|
||||
for _, env := range config.Environment {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
if len(parts) > 1 && parts[1] != "" {
|
||||
parsedEnv = append(parsedEnv, env)
|
||||
@@ -104,39 +120,71 @@ func (p *Project) CreateService(name string) (Service, error) {
|
||||
}
|
||||
}
|
||||
|
||||
config.Environment = NewMaporEqualSlice(parsedEnv)
|
||||
config.Environment = parsedEnv
|
||||
}
|
||||
|
||||
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 *ServiceConfig) error {
|
||||
p.Notify(EventServiceAdd, name, nil)
|
||||
func (p *Project) AddConfig(name string, config *config.ServiceConfig) error {
|
||||
p.Notify(events.ServiceAdd, name, nil)
|
||||
|
||||
p.Configs[name] = config
|
||||
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 {
|
||||
configs := make(map[string]*ServiceConfig)
|
||||
configs, err := mergeProject(p, bytes)
|
||||
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 configs {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -153,98 +201,313 @@ func (p *Project) loadWrappers(wrappers map[string]*serviceWrapper, servicesToCo
|
||||
}
|
||||
|
||||
// Build builds the specified services (like docker build).
|
||||
func (p *Project) Build(services ...string) error {
|
||||
return p.perform(EventProjectBuildStart, EventProjectBuildDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(wrappers, EventServiceBuildStart, EventServiceBuild, func(service Service) error {
|
||||
return service.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)
|
||||
}
|
||||
|
||||
// Create creates the specified services (like docker create).
|
||||
func (p *Project) Create(services ...string) error {
|
||||
return p.perform(EventProjectCreateStart, EventProjectCreateDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(wrappers, EventServiceCreateStart, EventServiceCreate, func(service Service) error {
|
||||
return service.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")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// Down stops the specified services (like docker stop).
|
||||
func (p *Project) Down(services ...string) error {
|
||||
return p.perform(EventProjectDownStart, EventProjectDownDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(nil, EventServiceDownStart, EventServiceDown, func(service Service) error {
|
||||
return service.Down()
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Restart restarts the specified services (like docker restart).
|
||||
func (p *Project) Restart(services ...string) error {
|
||||
return p.perform(EventProjectRestartStart, EventProjectRestartDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(wrappers, EventServiceRestartStart, EventServiceRestart, func(service Service) error {
|
||||
return service.Restart()
|
||||
})
|
||||
}), nil)
|
||||
}
|
||||
// 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.removeOrphanContainers(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := p.Delete(ctx, options.Delete{
|
||||
RemoveVolume: opts.RemoveVolume,
|
||||
}, services...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start starts the specified services (like docker start).
|
||||
func (p *Project) Start(services ...string) error {
|
||||
return p.perform(EventProjectStartStart, EventProjectStartDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(wrappers, EventServiceStartStart, EventServiceStart, func(service Service) error {
|
||||
return service.Start()
|
||||
})
|
||||
}), nil)
|
||||
}
|
||||
|
||||
// Up create and start the specified services (kinda like docker run).
|
||||
func (p *Project) Up(services ...string) error {
|
||||
return p.perform(EventProjectUpStart, EventProjectUpDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(wrappers, EventServiceUpStart, EventServiceUp, func(service Service) error {
|
||||
return service.Up()
|
||||
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()
|
||||
return service.Create(ctx, options.Create{})
|
||||
})
|
||||
}
|
||||
|
||||
// Log aggregate and prints out the logs for the specified services.
|
||||
func (p *Project) Log(services ...string) error {
|
||||
return p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(nil, NoEvent, NoEvent, func(service Service) error {
|
||||
return service.Log()
|
||||
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(), container.ID, types.ContainerRemoveOptions{
|
||||
Force: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// Ps list containers for the specified services.
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Run executes a one off command (like `docker run image command`).
|
||||
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
|
||||
}
|
||||
|
||||
// Up creates and starts the specified services (kinda like docker run).
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Pull pulls the specified services (like docker pull).
|
||||
func (p *Project) Pull(services ...string) error {
|
||||
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, EventServicePullStart, EventServicePull, func(service Service) error {
|
||||
return service.Pull()
|
||||
wrapper.Do(nil, events.ServicePullStart, events.ServicePull, func(service Service) error {
|
||||
return service.Pull(ctx)
|
||||
})
|
||||
}), nil)
|
||||
}
|
||||
|
||||
// listStoppedContainers lists the stopped containers for the specified services.
|
||||
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
|
||||
}
|
||||
|
||||
// Delete removes the specified services (like docker rm).
|
||||
func (p *Project) Delete(services ...string) error {
|
||||
return p.perform(EventProjectDeleteStart, EventProjectDeleteDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(nil, EventServiceDeleteStart, EventServiceDelete, func(service Service) error {
|
||||
return service.Delete()
|
||||
func (p *Project) Delete(ctx context.Context, options options.Delete, services ...string) error {
|
||||
stoppedContainers, err := p.listStoppedContainers(ctx, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(stoppedContainers) == 0 {
|
||||
p.Notify(events.ProjectDeleteDone, "", nil)
|
||||
fmt.Println("No stopped containers")
|
||||
return nil
|
||||
}
|
||||
if options.BeforeDeleteCallback != nil && !options.BeforeDeleteCallback(stoppedContainers) {
|
||||
return nil
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// Kill kills the specified services (like docker kill).
|
||||
func (p *Project) Kill(services ...string) error {
|
||||
return p.perform(EventProjectKillStart, EventProjectKillDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
|
||||
wrapper.Do(nil, EventServiceKillStart, EventServiceKill, func(service Service) error {
|
||||
return service.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)
|
||||
}
|
||||
|
||||
func (p *Project) perform(start, done EventType, services []string, action wrapperAction, cycleAction serviceAction) error {
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -324,7 +587,7 @@ func (p *Project) traverse(start bool, selected map[string]bool, wrappers map[st
|
||||
wrapperList := []string{}
|
||||
|
||||
if start {
|
||||
for name := range p.Configs {
|
||||
for _, name := range p.ServiceConfigs.Keys() {
|
||||
wrapperList = append(wrapperList, name)
|
||||
}
|
||||
} else {
|
||||
@@ -349,7 +612,9 @@ func (p *Project) traverse(start bool, selected map[string]bool, wrappers map[st
|
||||
launched := map[string]bool{}
|
||||
|
||||
for _, wrapper := range wrappers {
|
||||
p.startService(wrappers, []string{}, selected, launched, wrapper, action, cycleAction)
|
||||
if err := p.startService(wrappers, []string{}, selected, launched, wrapper, action, cycleAction); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var firstError error
|
||||
@@ -380,25 +645,27 @@ func (p *Project) traverse(start bool, selected map[string]bool, wrappers map[st
|
||||
}
|
||||
|
||||
// AddListener adds the specified listener to the project.
|
||||
func (p *Project) AddListener(c chan<- Event) {
|
||||
// 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<- Event{c}
|
||||
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.
|
||||
func (p *Project) Notify(eventType EventType, serviceName string, data map[string]string) {
|
||||
if eventType == NoEvent {
|
||||
// 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 := Event{
|
||||
event := events.Event{
|
||||
EventType: eventType,
|
||||
ServiceName: serviceName,
|
||||
Data: data,
|
||||
|
148
vendor/github.com/docker/libcompose/project/project_test.go
generated
vendored
148
vendor/github.com/docker/libcompose/project/project_test.go
generated
vendored
@@ -1,148 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestServiceFactory struct {
|
||||
Counts map[string]int
|
||||
}
|
||||
|
||||
type TestService struct {
|
||||
factory *TestServiceFactory
|
||||
name string
|
||||
config *ServiceConfig
|
||||
EmptyService
|
||||
Count int
|
||||
}
|
||||
|
||||
func (t *TestService) Config() *ServiceConfig {
|
||||
return t.config
|
||||
}
|
||||
|
||||
func (t *TestService) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *TestService) Create() error {
|
||||
key := t.name + ".create"
|
||||
t.factory.Counts[key] = t.factory.Counts[key] + 1
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestService) DependentServices() []ServiceRelationship {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServiceFactory) Create(project *Project, name string, serviceConfig *ServiceConfig) (Service, error) {
|
||||
return &TestService{
|
||||
factory: t,
|
||||
config: serviceConfig,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestTwoCall(t *testing.T) {
|
||||
factory := &TestServiceFactory{
|
||||
Counts: map[string]int{},
|
||||
}
|
||||
|
||||
p := NewProject(&Context{
|
||||
ServiceFactory: factory,
|
||||
})
|
||||
p.Configs = map[string]*ServiceConfig{
|
||||
"foo": {},
|
||||
}
|
||||
|
||||
if err := p.Create("foo"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := p.Create("foo"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if factory.Counts["foo.create"] != 2 {
|
||||
t.Fatal("Failed to create twice")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventEquality(t *testing.T) {
|
||||
if fmt.Sprintf("%s", EventServiceStart) != "Started" ||
|
||||
fmt.Sprintf("%v", EventServiceStart) != "Started" {
|
||||
t.Fatalf("EventServiceStart String() doesn't work: %s %v", EventServiceStart, EventServiceStart)
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%s", EventServiceStart) != fmt.Sprintf("%s", EventServiceUp) {
|
||||
t.Fatal("Event messages do not match")
|
||||
}
|
||||
|
||||
if EventServiceStart == EventServiceUp {
|
||||
t.Fatal("Events match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWithBadContent(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ComposeBytes: []byte("garbage"),
|
||||
})
|
||||
|
||||
err := p.Parse()
|
||||
if err == nil {
|
||||
t.Fatal("Should have failed parse")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(err.Error(), "Unknown resolution for 'garbage'") {
|
||||
t.Fatalf("Should have failed parse: %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWithGoodContent(t *testing.T) {
|
||||
p := NewProject(&Context{
|
||||
ComposeBytes: []byte("not-garbage:\n image: foo"),
|
||||
})
|
||||
|
||||
err := p.Parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TestEnvironmentLookup struct {
|
||||
}
|
||||
|
||||
func (t *TestEnvironmentLookup) Lookup(key, serviceName string, config *ServiceConfig) []string {
|
||||
return []string{fmt.Sprintf("%s=X", key)}
|
||||
}
|
||||
|
||||
func TestEnvironmentResolve(t *testing.T) {
|
||||
factory := &TestServiceFactory{
|
||||
Counts: map[string]int{},
|
||||
}
|
||||
|
||||
p := NewProject(&Context{
|
||||
ServiceFactory: factory,
|
||||
EnvironmentLookup: &TestEnvironmentLookup{},
|
||||
})
|
||||
p.Configs = map[string]*ServiceConfig{
|
||||
"foo": {
|
||||
Environment: NewMaporEqualSlice([]string{
|
||||
"A",
|
||||
"A=",
|
||||
"A=B",
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
service, err := p.CreateService("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(service.Config().Environment.Slice(), []string{"A=X", "A=X", "A=B"}) {
|
||||
t.Fatal("Invalid environment", service.Config().Environment.Slice())
|
||||
}
|
||||
}
|
9
vendor/github.com/docker/libcompose/project/service-wrapper.go
generated
vendored
9
vendor/github.com/docker/libcompose/project/service-wrapper.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcompose/project/events"
|
||||
)
|
||||
|
||||
type serviceWrapper struct {
|
||||
@@ -55,7 +56,7 @@ func (s *serviceWrapper) Ignore() {
|
||||
defer s.done.Done()
|
||||
|
||||
s.state = StateExecuted
|
||||
s.project.Notify(EventServiceUpIgnored, s.service.Name(), nil)
|
||||
s.project.Notify(events.ServiceUpIgnored, s.service.Name(), nil)
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) waitForDeps(wrappers map[string]*serviceWrapper) bool {
|
||||
@@ -70,7 +71,7 @@ func (s *serviceWrapper) waitForDeps(wrappers map[string]*serviceWrapper) bool {
|
||||
|
||||
if wrapper, ok := wrappers[dep.Target]; ok {
|
||||
if wrapper.Wait() == ErrRestart {
|
||||
s.project.Notify(EventProjectReload, wrapper.service.Name(), nil)
|
||||
s.project.Notify(events.ProjectReload, wrapper.service.Name(), nil)
|
||||
s.err = ErrRestart
|
||||
return false
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func (s *serviceWrapper) waitForDeps(wrappers map[string]*serviceWrapper) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) Do(wrappers map[string]*serviceWrapper, start, done EventType, action func(service Service) error) {
|
||||
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 {
|
||||
@@ -100,7 +101,7 @@ func (s *serviceWrapper) Do(wrappers map[string]*serviceWrapper, start, done Eve
|
||||
s.err = action(s.service)
|
||||
if s.err == ErrRestart {
|
||||
s.project.Notify(done, s.service.Name(), nil)
|
||||
s.project.Notify(EventProjectReloadTrigger, 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 {
|
||||
|
92
vendor/github.com/docker/libcompose/project/service.go
generated
vendored
Normal file
92
vendor/github.com/docker/libcompose/project/service.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/libcompose/config"
|
||||
"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
|
||||
Info(ctx context.Context, qFlag bool) (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) (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")
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
295
vendor/github.com/docker/libcompose/project/types.go
generated
vendored
295
vendor/github.com/docker/libcompose/project/types.go
generated
vendored
@@ -1,295 +0,0 @@
|
||||
package project
|
||||
|
||||
import "fmt"
|
||||
|
||||
// EventType defines a type of libcompose event.
|
||||
type EventType int
|
||||
|
||||
// Definitions of libcompose events
|
||||
const (
|
||||
NoEvent = EventType(iota)
|
||||
|
||||
EventContainerCreated = EventType(iota)
|
||||
EventContainerStarted = EventType(iota)
|
||||
|
||||
EventServiceAdd = EventType(iota)
|
||||
EventServiceUpStart = EventType(iota)
|
||||
EventServiceUpIgnored = EventType(iota)
|
||||
EventServiceUp = EventType(iota)
|
||||
EventServiceCreateStart = EventType(iota)
|
||||
EventServiceCreate = EventType(iota)
|
||||
EventServiceDeleteStart = EventType(iota)
|
||||
EventServiceDelete = EventType(iota)
|
||||
EventServiceDownStart = EventType(iota)
|
||||
EventServiceDown = EventType(iota)
|
||||
EventServiceRestartStart = EventType(iota)
|
||||
EventServiceRestart = EventType(iota)
|
||||
EventServicePullStart = EventType(iota)
|
||||
EventServicePull = EventType(iota)
|
||||
EventServiceKillStart = EventType(iota)
|
||||
EventServiceKill = EventType(iota)
|
||||
EventServiceStartStart = EventType(iota)
|
||||
EventServiceStart = EventType(iota)
|
||||
EventServiceBuildStart = EventType(iota)
|
||||
EventServiceBuild = EventType(iota)
|
||||
|
||||
EventProjectDownStart = EventType(iota)
|
||||
EventProjectDownDone = EventType(iota)
|
||||
EventProjectCreateStart = EventType(iota)
|
||||
EventProjectCreateDone = EventType(iota)
|
||||
EventProjectUpStart = EventType(iota)
|
||||
EventProjectUpDone = EventType(iota)
|
||||
EventProjectDeleteStart = EventType(iota)
|
||||
EventProjectDeleteDone = EventType(iota)
|
||||
EventProjectRestartStart = EventType(iota)
|
||||
EventProjectRestartDone = EventType(iota)
|
||||
EventProjectReload = EventType(iota)
|
||||
EventProjectReloadTrigger = EventType(iota)
|
||||
EventProjectKillStart = EventType(iota)
|
||||
EventProjectKillDone = EventType(iota)
|
||||
EventProjectStartStart = EventType(iota)
|
||||
EventProjectStartDone = EventType(iota)
|
||||
EventProjectBuildStart = EventType(iota)
|
||||
EventProjectBuildDone = EventType(iota)
|
||||
)
|
||||
|
||||
func (e EventType) String() string {
|
||||
var m string
|
||||
switch e {
|
||||
case EventContainerCreated:
|
||||
m = "Created container"
|
||||
case EventContainerStarted:
|
||||
m = "Started container"
|
||||
|
||||
case EventServiceAdd:
|
||||
m = "Adding"
|
||||
case EventServiceUpStart:
|
||||
m = "Starting"
|
||||
case EventServiceUpIgnored:
|
||||
m = "Ignoring"
|
||||
case EventServiceUp:
|
||||
m = "Started"
|
||||
case EventServiceCreateStart:
|
||||
m = "Creating"
|
||||
case EventServiceCreate:
|
||||
m = "Created"
|
||||
case EventServiceDeleteStart:
|
||||
m = "Deleting"
|
||||
case EventServiceDelete:
|
||||
m = "Deleted"
|
||||
case EventServiceDownStart:
|
||||
m = "Stopping"
|
||||
case EventServiceDown:
|
||||
m = "Stopped"
|
||||
case EventServiceRestartStart:
|
||||
m = "Restarting"
|
||||
case EventServiceRestart:
|
||||
m = "Restarted"
|
||||
case EventServicePullStart:
|
||||
m = "Pulling"
|
||||
case EventServicePull:
|
||||
m = "Pulled"
|
||||
case EventServiceKillStart:
|
||||
m = "Killing"
|
||||
case EventServiceKill:
|
||||
m = "Killed"
|
||||
case EventServiceStartStart:
|
||||
m = "Starting"
|
||||
case EventServiceStart:
|
||||
m = "Started"
|
||||
case EventServiceBuildStart:
|
||||
m = "Building"
|
||||
case EventServiceBuild:
|
||||
m = "Built"
|
||||
|
||||
case EventProjectDownStart:
|
||||
m = "Stopping project"
|
||||
case EventProjectDownDone:
|
||||
m = "Project stopped"
|
||||
case EventProjectCreateStart:
|
||||
m = "Creating project"
|
||||
case EventProjectCreateDone:
|
||||
m = "Project created"
|
||||
case EventProjectUpStart:
|
||||
m = "Starting project"
|
||||
case EventProjectUpDone:
|
||||
m = "Project started"
|
||||
case EventProjectDeleteStart:
|
||||
m = "Deleting project"
|
||||
case EventProjectDeleteDone:
|
||||
m = "Project deleted"
|
||||
case EventProjectRestartStart:
|
||||
m = "Restarting project"
|
||||
case EventProjectRestartDone:
|
||||
m = "Project restarted"
|
||||
case EventProjectReload:
|
||||
m = "Reloading project"
|
||||
case EventProjectReloadTrigger:
|
||||
m = "Triggering project reload"
|
||||
case EventProjectKillStart:
|
||||
m = "Killing project"
|
||||
case EventProjectKillDone:
|
||||
m = "Project killed"
|
||||
case EventProjectStartStart:
|
||||
m = "Starting project"
|
||||
case EventProjectStartDone:
|
||||
m = "Project started"
|
||||
case EventProjectBuildStart:
|
||||
m = "Building project"
|
||||
case EventProjectBuildDone:
|
||||
m = "Project built"
|
||||
}
|
||||
|
||||
if m == "" {
|
||||
m = fmt.Sprintf("EventType: %d", int(e))
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// InfoPart holds key/value strings.
|
||||
type InfoPart struct {
|
||||
Key, Value string
|
||||
}
|
||||
|
||||
// InfoSet holds a list of Info.
|
||||
type InfoSet []Info
|
||||
|
||||
// Info holds a list of InfoPart.
|
||||
type Info []InfoPart
|
||||
|
||||
// ServiceConfig holds libcompose service configuration
|
||||
type ServiceConfig struct {
|
||||
Build string `yaml:"build,omitempty"`
|
||||
CapAdd []string `yaml:"cap_add,omitempty"`
|
||||
CapDrop []string `yaml:"cap_drop,omitempty"`
|
||||
CPUSet string `yaml:"cpuset,omitempty"`
|
||||
CPUShares int64 `yaml:"cpu_shares,omitempty"`
|
||||
Command Command `yaml:"command,flow,omitempty"`
|
||||
ContainerName string `yaml:"container_name,omitempty"`
|
||||
Devices []string `yaml:"devices,omitempty"`
|
||||
DNS Stringorslice `yaml:"dns,omitempty"`
|
||||
DNSSearch Stringorslice `yaml:"dns_search,omitempty"`
|
||||
Dockerfile string `yaml:"dockerfile,omitempty"`
|
||||
DomainName string `yaml:"domainname,omitempty"`
|
||||
Entrypoint Command `yaml:"entrypoint,flow,omitempty"`
|
||||
EnvFile Stringorslice `yaml:"env_file,omitempty"`
|
||||
Environment MaporEqualSlice `yaml:"environment,omitempty"`
|
||||
Hostname string `yaml:"hostname,omitempty"`
|
||||
Image string `yaml:"image,omitempty"`
|
||||
Labels SliceorMap `yaml:"labels,omitempty"`
|
||||
Links MaporColonSlice `yaml:"links,omitempty"`
|
||||
LogDriver string `yaml:"log_driver,omitempty"`
|
||||
MemLimit int64 `yaml:"mem_limit,omitempty"`
|
||||
MemSwapLimit int64 `yaml:"memswap_limit,omitempty"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Net string `yaml:"net,omitempty"`
|
||||
Pid string `yaml:"pid,omitempty"`
|
||||
Uts string `yaml:"uts,omitempty"`
|
||||
Ipc string `yaml:"ipc,omitempty"`
|
||||
Ports []string `yaml:"ports,omitempty"`
|
||||
Privileged bool `yaml:"privileged,omitempty"`
|
||||
Restart string `yaml:"restart,omitempty"`
|
||||
ReadOnly bool `yaml:"read_only,omitempty"`
|
||||
StdinOpen bool `yaml:"stdin_open,omitempty"`
|
||||
SecurityOpt []string `yaml:"security_opt,omitempty"`
|
||||
Tty bool `yaml:"tty,omitempty"`
|
||||
User string `yaml:"user,omitempty"`
|
||||
VolumeDriver string `yaml:"volume_driver,omitempty"`
|
||||
Volumes []string `yaml:"volumes,omitempty"`
|
||||
VolumesFrom []string `yaml:"volumes_from,omitempty"`
|
||||
WorkingDir string `yaml:"working_dir,omitempty"`
|
||||
Expose []string `yaml:"expose,omitempty"`
|
||||
ExternalLinks []string `yaml:"external_links,omitempty"`
|
||||
LogOpt map[string]string `yaml:"log_opt,omitempty"`
|
||||
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
|
||||
}
|
||||
|
||||
// EnvironmentLookup defines methods to provides environment variable loading.
|
||||
type EnvironmentLookup interface {
|
||||
Lookup(key, serviceName string, config *ServiceConfig) []string
|
||||
}
|
||||
|
||||
// ConfigLookup defines methods to provides file loading.
|
||||
type ConfigLookup interface {
|
||||
Lookup(file, relativeTo string) ([]byte, string, error)
|
||||
}
|
||||
|
||||
// Project holds libcompose project information.
|
||||
type Project struct {
|
||||
Name string
|
||||
Configs map[string]*ServiceConfig
|
||||
File string
|
||||
ReloadCallback func() error
|
||||
context *Context
|
||||
reload []string
|
||||
upCount int
|
||||
listeners []chan<- Event
|
||||
hasListeners bool
|
||||
}
|
||||
|
||||
// Service defines what a libcompose service provides.
|
||||
type Service interface {
|
||||
Info(qFlag bool) (InfoSet, error)
|
||||
Name() string
|
||||
Build() error
|
||||
Create() error
|
||||
Up() error
|
||||
Start() error
|
||||
Down() error
|
||||
Delete() error
|
||||
Restart() error
|
||||
Log() error
|
||||
Pull() error
|
||||
Kill() error
|
||||
Config() *ServiceConfig
|
||||
DependentServices() []ServiceRelationship
|
||||
Containers() ([]Container, error)
|
||||
Scale(count int) error
|
||||
}
|
||||
|
||||
// Container defines what a libcompose container provides.
|
||||
type Container interface {
|
||||
ID() (string, error)
|
||||
Name() string
|
||||
Port(port string) (string, error)
|
||||
}
|
||||
|
||||
// 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 *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")
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
328
vendor/github.com/docker/libcompose/project/types_yaml.go
generated
vendored
328
vendor/github.com/docker/libcompose/project/types_yaml.go
generated
vendored
@@ -1,328 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/flynn/go-shlex"
|
||||
)
|
||||
|
||||
// Stringorslice represents a string or an array of strings.
|
||||
// TODO use docker/docker/pkg/stringutils.StrSlice once 1.9.x is released.
|
||||
type Stringorslice struct {
|
||||
parts []string
|
||||
}
|
||||
|
||||
// MarshalYAML implements the Marshaller interface.
|
||||
func (s Stringorslice) MarshalYAML() (tag string, value interface{}, err error) {
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
func toStrings(s []interface{}) ([]string, error) {
|
||||
if len(s) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
r := make([]string, len(s))
|
||||
for k, v := range s {
|
||||
if sv, ok := v.(string); ok {
|
||||
r[k] = sv
|
||||
} else {
|
||||
return nil, fmt.Errorf("Cannot unmarshal '%v' of type %T into a string value", v, v)
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *Stringorslice) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts, err := toStrings(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
case string:
|
||||
s.parts = []string{value}
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal Stringorslice: %#v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the number of parts of the Stringorslice.
|
||||
func (s *Stringorslice) Len() int {
|
||||
if s == nil {
|
||||
return 0
|
||||
}
|
||||
return len(s.parts)
|
||||
}
|
||||
|
||||
// Slice gets the parts of the StrSlice as a Slice of string.
|
||||
func (s *Stringorslice) Slice() []string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.parts
|
||||
}
|
||||
|
||||
// NewStringorslice creates an Stringorslice based on the specified parts (as strings).
|
||||
func NewStringorslice(parts ...string) Stringorslice {
|
||||
return Stringorslice{parts}
|
||||
}
|
||||
|
||||
// Command represents a docker command, can be a string or an array of strings.
|
||||
// FIXME why not use Stringorslice (type Command struct { Stringorslice }
|
||||
type Command struct {
|
||||
parts []string
|
||||
}
|
||||
|
||||
// MarshalYAML implements the Marshaller interface.
|
||||
func (s Command) MarshalYAML() (tag string, value interface{}, err error) {
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *Command) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts, err := toStrings(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
case string:
|
||||
parts, err := shlex.Split(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal Command: %#v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToString returns the parts of the command as a string (joined by spaces).
|
||||
func (s *Command) ToString() string {
|
||||
return strings.Join(s.parts, " ")
|
||||
}
|
||||
|
||||
// Slice gets the parts of the Command as a Slice of string.
|
||||
func (s *Command) Slice() []string {
|
||||
return s.parts
|
||||
}
|
||||
|
||||
// NewCommand create a Command based on the specified parts (as strings).
|
||||
func NewCommand(parts ...string) Command {
|
||||
return Command{parts}
|
||||
}
|
||||
|
||||
// SliceorMap represents a slice or a map of strings.
|
||||
type SliceorMap struct {
|
||||
parts map[string]string
|
||||
}
|
||||
|
||||
// MarshalYAML implements the Marshaller interface.
|
||||
func (s SliceorMap) MarshalYAML() (tag string, value interface{}, err error) {
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *SliceorMap) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
parts := map[string]string{}
|
||||
for k, v := range value {
|
||||
if sk, ok := k.(string); ok {
|
||||
if sv, ok := v.(string); ok {
|
||||
parts[sk] = sv
|
||||
} else {
|
||||
return fmt.Errorf("Cannot unmarshal '%v' of type %T into a string value", v, v)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Cannot unmarshal '%v' of type %T into a string value", k, k)
|
||||
}
|
||||
}
|
||||
s.parts = parts
|
||||
case []interface{}:
|
||||
parts := map[string]string{}
|
||||
for _, s := range value {
|
||||
if str, ok := s.(string); ok {
|
||||
str := strings.TrimSpace(str)
|
||||
keyValueSlice := strings.SplitN(str, "=", 2)
|
||||
|
||||
key := keyValueSlice[0]
|
||||
val := ""
|
||||
if len(keyValueSlice) == 2 {
|
||||
val = keyValueSlice[1]
|
||||
}
|
||||
parts[key] = val
|
||||
} else {
|
||||
return fmt.Errorf("Cannot unmarshal '%v' of type %T into a string value", s, s)
|
||||
}
|
||||
}
|
||||
s.parts = parts
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal SliceorMap: %#v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MapParts get the parts of the SliceorMap as a Map of string.
|
||||
func (s *SliceorMap) MapParts() map[string]string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.parts
|
||||
}
|
||||
|
||||
// NewSliceorMap creates a new SliceorMap based on the specified parts (as map of string).
|
||||
func NewSliceorMap(parts map[string]string) SliceorMap {
|
||||
return SliceorMap{parts}
|
||||
}
|
||||
|
||||
// MaporEqualSlice represents a slice of strings that gets unmarshal from a
|
||||
// YAML map into 'key=value' string.
|
||||
type MaporEqualSlice struct {
|
||||
parts []string
|
||||
}
|
||||
|
||||
// MarshalYAML implements the Marshaller interface.
|
||||
func (s MaporEqualSlice) MarshalYAML() (tag string, value interface{}, err error) {
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
func toSepMapParts(value map[interface{}]interface{}, sep string) ([]string, error) {
|
||||
if len(value) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
parts := make([]string, 0, len(value))
|
||||
for k, v := range value {
|
||||
if sk, ok := k.(string); ok {
|
||||
if sv, ok := v.(string); ok {
|
||||
parts = append(parts, sk+sep+sv)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Cannot unmarshal '%v' of type %T into a string value", v, v)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Cannot unmarshal '%v' of type %T into a string value", k, k)
|
||||
}
|
||||
}
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *MaporEqualSlice) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts, err := toStrings(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
case map[interface{}]interface{}:
|
||||
parts, err := toSepMapParts(value, "=")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal MaporEqualSlice: %#v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Slice gets the parts of the MaporEqualSlice as a Slice of string.
|
||||
func (s *MaporEqualSlice) Slice() []string {
|
||||
return s.parts
|
||||
}
|
||||
|
||||
// NewMaporEqualSlice creates a new MaporEqualSlice based on the specified parts.
|
||||
func NewMaporEqualSlice(parts []string) MaporEqualSlice {
|
||||
return MaporEqualSlice{parts}
|
||||
}
|
||||
|
||||
// MaporColonSlice represents a slice of strings that gets unmarshal from a
|
||||
// YAML map into 'key:value' string.
|
||||
type MaporColonSlice struct {
|
||||
parts []string
|
||||
}
|
||||
|
||||
// MarshalYAML implements the Marshaller interface.
|
||||
func (s MaporColonSlice) MarshalYAML() (tag string, value interface{}, err error) {
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *MaporColonSlice) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts, err := toStrings(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
case map[interface{}]interface{}:
|
||||
parts, err := toSepMapParts(value, ":")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal MaporColonSlice: %#v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Slice gets the parts of the MaporColonSlice as a Slice of string.
|
||||
func (s *MaporColonSlice) Slice() []string {
|
||||
return s.parts
|
||||
}
|
||||
|
||||
// NewMaporColonSlice creates a new MaporColonSlice based on the specified parts.
|
||||
func NewMaporColonSlice(parts []string) MaporColonSlice {
|
||||
return MaporColonSlice{parts}
|
||||
}
|
||||
|
||||
// MaporSpaceSlice represents a slice of strings that gets unmarshal from a
|
||||
// YAML map into 'key value' string.
|
||||
type MaporSpaceSlice struct {
|
||||
parts []string
|
||||
}
|
||||
|
||||
// MarshalYAML implements the Marshaller interface.
|
||||
func (s MaporSpaceSlice) MarshalYAML() (tag string, value interface{}, err error) {
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *MaporSpaceSlice) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts, err := toStrings(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
case map[interface{}]interface{}:
|
||||
parts, err := toSepMapParts(value, " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.parts = parts
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal MaporSpaceSlice: %#v", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Slice gets the parts of the MaporSpaceSlice as a Slice of string.
|
||||
func (s *MaporSpaceSlice) Slice() []string {
|
||||
return s.parts
|
||||
}
|
||||
|
||||
// NewMaporSpaceSlice creates a new MaporSpaceSlice based on the specified parts.
|
||||
func NewMaporSpaceSlice(parts []string) MaporSpaceSlice {
|
||||
return MaporSpaceSlice{parts}
|
||||
}
|
235
vendor/github.com/docker/libcompose/project/types_yaml_test.go
generated
vendored
235
vendor/github.com/docker/libcompose/project/types_yaml_test.go
generated
vendored
@@ -1,235 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type StructStringorslice struct {
|
||||
Foo Stringorslice
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
SystemContainers map[string]*ServiceConfig
|
||||
}
|
||||
|
||||
func newTestConfig() TestConfig {
|
||||
return TestConfig{
|
||||
SystemContainers: map[string]*ServiceConfig{
|
||||
"udev": {
|
||||
Image: "udev",
|
||||
Restart: "always",
|
||||
Net: "host",
|
||||
Privileged: true,
|
||||
DNS: Stringorslice{[]string{"8.8.8.8", "8.8.4.4"}},
|
||||
Environment: MaporEqualSlice{[]string{
|
||||
"DAEMON=true",
|
||||
}},
|
||||
Labels: SliceorMap{map[string]string{
|
||||
"io.rancher.os.detach": "true",
|
||||
"io.rancher.os.scope": "system",
|
||||
}},
|
||||
VolumesFrom: []string{
|
||||
"system-volumes",
|
||||
},
|
||||
},
|
||||
"system-volumes": {
|
||||
Image: "state",
|
||||
Net: "none",
|
||||
ReadOnly: true,
|
||||
Privileged: true,
|
||||
Labels: SliceorMap{map[string]string{
|
||||
"io.rancher.os.createonly": "true",
|
||||
"io.rancher.os.scope": "system",
|
||||
}},
|
||||
Volumes: []string{
|
||||
"/dev:/host/dev",
|
||||
"/var/lib/rancher/conf:/var/lib/rancher/conf",
|
||||
"/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt.rancher",
|
||||
"/lib/modules:/lib/modules",
|
||||
"/lib/firmware:/lib/firmware",
|
||||
"/var/run:/var/run",
|
||||
"/var/log:/var/log",
|
||||
},
|
||||
LogDriver: "json-file",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalConfig(t *testing.T) {
|
||||
config := newTestConfig()
|
||||
bytes, err := yaml.Marshal(config)
|
||||
assert.Nil(t, err)
|
||||
|
||||
config2 := TestConfig{}
|
||||
|
||||
err = yaml.Unmarshal(bytes, &config2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, config, config2)
|
||||
}
|
||||
|
||||
func TestMarshalServiceConfig(t *testing.T) {
|
||||
configPtr := newTestConfig().SystemContainers["udev"]
|
||||
bytes, err := yaml.Marshal(configPtr)
|
||||
assert.Nil(t, err)
|
||||
|
||||
configPtr2 := &ServiceConfig{}
|
||||
|
||||
err = yaml.Unmarshal(bytes, configPtr2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, configPtr, configPtr2)
|
||||
}
|
||||
|
||||
func TestStringorsliceYaml(t *testing.T) {
|
||||
str := `{foo: [bar, baz]}`
|
||||
|
||||
s := StructStringorslice{}
|
||||
yaml.Unmarshal([]byte(str), &s)
|
||||
|
||||
assert.Equal(t, []string{"bar", "baz"}, s.Foo.parts)
|
||||
|
||||
d, err := yaml.Marshal(&s)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s2 := StructStringorslice{}
|
||||
yaml.Unmarshal(d, &s2)
|
||||
|
||||
assert.Equal(t, []string{"bar", "baz"}, s2.Foo.parts)
|
||||
}
|
||||
|
||||
type StructSliceorMap struct {
|
||||
Foos SliceorMap `yaml:"foos,omitempty"`
|
||||
Bars []string `yaml:"bars"`
|
||||
}
|
||||
|
||||
type StructCommand struct {
|
||||
Entrypoint Command `yaml:"entrypoint,flow,omitempty"`
|
||||
Command Command `yaml:"command,flow,omitempty"`
|
||||
}
|
||||
|
||||
func TestSliceOrMapYaml(t *testing.T) {
|
||||
str := `{foos: [bar=baz, far=faz]}`
|
||||
|
||||
s := StructSliceorMap{}
|
||||
yaml.Unmarshal([]byte(str), &s)
|
||||
|
||||
assert.Equal(t, map[string]string{"bar": "baz", "far": "faz"}, s.Foos.parts)
|
||||
|
||||
d, err := yaml.Marshal(&s)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s2 := StructSliceorMap{}
|
||||
yaml.Unmarshal(d, &s2)
|
||||
|
||||
assert.Equal(t, map[string]string{"bar": "baz", "far": "faz"}, s2.Foos.parts)
|
||||
}
|
||||
|
||||
var sampleStructSliceorMap = `
|
||||
foos:
|
||||
io.rancher.os.bar: baz
|
||||
io.rancher.os.far: true
|
||||
bars: []
|
||||
`
|
||||
|
||||
func TestUnmarshalSliceOrMap(t *testing.T) {
|
||||
s := StructSliceorMap{}
|
||||
err := yaml.Unmarshal([]byte(sampleStructSliceorMap), &s)
|
||||
assert.Equal(t, fmt.Errorf("Cannot unmarshal 'true' of type bool into a string value"), err)
|
||||
}
|
||||
|
||||
func TestStr2SliceOrMapPtrMap(t *testing.T) {
|
||||
s := map[string]*StructSliceorMap{"udav": {
|
||||
Foos: SliceorMap{map[string]string{"io.rancher.os.bar": "baz", "io.rancher.os.far": "true"}},
|
||||
Bars: []string{},
|
||||
}}
|
||||
d, err := yaml.Marshal(&s)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s2 := map[string]*StructSliceorMap{}
|
||||
yaml.Unmarshal(d, &s2)
|
||||
|
||||
assert.Equal(t, s, s2)
|
||||
}
|
||||
|
||||
type StructMaporslice struct {
|
||||
Foo MaporEqualSlice
|
||||
}
|
||||
|
||||
func contains(list []string, item string) bool {
|
||||
for _, test := range list {
|
||||
if test == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestMaporsliceYaml(t *testing.T) {
|
||||
str := `{foo: {bar: baz, far: faz}}`
|
||||
|
||||
s := StructMaporslice{}
|
||||
yaml.Unmarshal([]byte(str), &s)
|
||||
|
||||
assert.Equal(t, 2, len(s.Foo.parts))
|
||||
assert.True(t, contains(s.Foo.parts, "bar=baz"))
|
||||
assert.True(t, contains(s.Foo.parts, "far=faz"))
|
||||
|
||||
d, err := yaml.Marshal(&s)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s2 := StructMaporslice{}
|
||||
yaml.Unmarshal(d, &s2)
|
||||
|
||||
assert.Equal(t, 2, len(s2.Foo.parts))
|
||||
assert.True(t, contains(s2.Foo.parts, "bar=baz"))
|
||||
assert.True(t, contains(s2.Foo.parts, "far=faz"))
|
||||
}
|
||||
|
||||
var sampleStructCommand = `command: bash`
|
||||
|
||||
func TestUnmarshalCommand(t *testing.T) {
|
||||
s := &StructCommand{}
|
||||
err := yaml.Unmarshal([]byte(sampleStructCommand), s)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []string{"bash"}, s.Command.Slice())
|
||||
assert.Nil(t, s.Entrypoint.Slice())
|
||||
|
||||
bytes, err := yaml.Marshal(s)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s2 := &StructCommand{}
|
||||
err = yaml.Unmarshal(bytes, s2)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []string{"bash"}, s2.Command.Slice())
|
||||
assert.Nil(t, s2.Entrypoint.Slice())
|
||||
}
|
||||
|
||||
var sampleEmptyCommand = `{}`
|
||||
|
||||
func TestUnmarshalEmptyCommand(t *testing.T) {
|
||||
s := &StructCommand{}
|
||||
err := yaml.Unmarshal([]byte(sampleEmptyCommand), s)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, s.Command.Slice())
|
||||
|
||||
bytes, err := yaml.Marshal(s)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "entrypoint: []\ncommand: []", strings.TrimSpace(string(bytes)))
|
||||
|
||||
s2 := &StructCommand{}
|
||||
err = yaml.Unmarshal(bytes, s2)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, s2.Command.Slice())
|
||||
}
|
14
vendor/github.com/docker/libcompose/project/utils.go
generated
vendored
14
vendor/github.com/docker/libcompose/project/utils.go
generated
vendored
@@ -3,7 +3,7 @@ package project
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
// DefaultDependentServices return the dependent services (as an array of ServiceRelationship)
|
||||
@@ -15,7 +15,7 @@ func DefaultDependentServices(p *Project, s Service) []ServiceRelationship {
|
||||
}
|
||||
|
||||
result := []ServiceRelationship{}
|
||||
for _, link := range config.Links.Slice() {
|
||||
for _, link := range config.Links {
|
||||
result = append(result, NewServiceRelationship(link, RelTypeLink))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ func DefaultDependentServices(p *Project, s Service) []ServiceRelationship {
|
||||
result = append(result, NewServiceRelationship(volumesFrom, RelTypeVolumesFrom))
|
||||
}
|
||||
|
||||
result = appendNs(p, result, s.Config().Net, RelTypeNetNamespace)
|
||||
for _, dependsOn := range config.DependsOn {
|
||||
result = append(result, NewServiceRelationship(dependsOn, RelTypeDependsOn))
|
||||
}
|
||||
|
||||
result = appendNs(p, result, s.Config().NetworkMode, RelTypeNetNamespace)
|
||||
result = appendNs(p, result, s.Config().Ipc, RelTypeIpcNamespace)
|
||||
|
||||
return result
|
||||
@@ -51,7 +55,7 @@ func NameAlias(name string) (string, string) {
|
||||
// GetContainerFromIpcLikeConfig returns name of the service that shares the IPC
|
||||
// namespace with the specified service.
|
||||
func GetContainerFromIpcLikeConfig(p *Project, conf string) string {
|
||||
ipc := runconfig.IpcMode(conf)
|
||||
ipc := container.IpcMode(conf)
|
||||
if !ipc.IsContainer() {
|
||||
return ""
|
||||
}
|
||||
@@ -61,7 +65,7 @@ func GetContainerFromIpcLikeConfig(p *Project, conf string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
if _, ok := p.Configs[name]; ok {
|
||||
if p.ServiceConfigs.Has(name) {
|
||||
return name
|
||||
}
|
||||
return ""
|
||||
|
54
vendor/github.com/docker/libcompose/project/yaml_test.go
generated
vendored
54
vendor/github.com/docker/libcompose/project/yaml_test.go
generated
vendored
@@ -1,54 +0,0 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type structStringorslice struct {
|
||||
Foo Stringorslice `yaml:"foo,flow,omitempty"`
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
s := &structStringorslice{Foo: NewStringorslice("a", "b", "c")}
|
||||
b, err := yaml.Marshal(s)
|
||||
assert.Equal(t, "foo: [a, b, c]\n", string(b))
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestMarshalEmpty(t *testing.T) {
|
||||
s := &structStringorslice{}
|
||||
b, err := yaml.Marshal(s)
|
||||
assert.Equal(t, "foo: []\n", string(b))
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestUnmarshalSlice(t *testing.T) {
|
||||
expected := &structStringorslice{Foo: NewStringorslice("a", "b", "c")}
|
||||
b := []byte("foo: [a, b, c]\n")
|
||||
s := &structStringorslice{}
|
||||
err := yaml.Unmarshal(b, s)
|
||||
assert.Equal(t, expected, s)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestUnmarshalString(t *testing.T) {
|
||||
expected := &structStringorslice{Foo: NewStringorslice("abc")}
|
||||
b := []byte("foo: abc\n")
|
||||
s := &structStringorslice{}
|
||||
err := yaml.Unmarshal(b, s)
|
||||
assert.Equal(t, expected, s)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestUnmarshalEmpty(t *testing.T) {
|
||||
expected := &structStringorslice{Foo: NewStringorslice()}
|
||||
b := []byte("{}\n")
|
||||
s := &structStringorslice{}
|
||||
err := yaml.Unmarshal(b, s)
|
||||
assert.Equal(t, expected, s)
|
||||
assert.Nil(t, err)
|
||||
}
|
Reference in New Issue
Block a user