mirror of
https://github.com/rancher/os.git
synced 2025-09-04 16:21:07 +00:00
move dependencies to vendor
This commit is contained in:
136
vendor/github.com/docker/libcompose/project/context.go
generated
vendored
Normal file
136
vendor/github.com/docker/libcompose/project/context.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcompose/logger"
|
||||
)
|
||||
|
||||
var projectRegexp = regexp.MustCompile("[^a-zA-Z0-9_.-]")
|
||||
|
||||
// Context holds context meta information about a libcompose project, like
|
||||
// the project name, the compose file, etc.
|
||||
type Context struct {
|
||||
Timeout uint
|
||||
Log bool
|
||||
Volume bool
|
||||
ForceRecreate bool
|
||||
NoRecreate bool
|
||||
Signal int
|
||||
ComposeFile string
|
||||
ComposeBytes []byte
|
||||
ProjectName string
|
||||
isOpen bool
|
||||
ServiceFactory ServiceFactory
|
||||
EnvironmentLookup EnvironmentLookup
|
||||
ConfigLookup ConfigLookup
|
||||
LoggerFactory logger.Factory
|
||||
IgnoreMissingConfig bool
|
||||
Project *Project
|
||||
}
|
||||
|
||||
func (c *Context) readComposeFile() error {
|
||||
if c.ComposeBytes != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
logrus.Debugf("Opening compose file: %s", c.ComposeFile)
|
||||
|
||||
if c.ComposeFile == "-" {
|
||||
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)
|
||||
return err
|
||||
} else if err != nil {
|
||||
logrus.Errorf("Failed to open %s", c.ComposeFile)
|
||||
return err
|
||||
} else {
|
||||
c.ComposeBytes = composeBytes
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Context) determineProject() error {
|
||||
name, err := c.lookupProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ProjectName = projectRegexp.ReplaceAllString(strings.ToLower(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
|
||||
}
|
||||
|
||||
func (c *Context) lookupProjectName() (string, error) {
|
||||
if c.ProjectName != "" {
|
||||
return c.ProjectName, nil
|
||||
}
|
||||
|
||||
if envProject := os.Getenv("COMPOSE_PROJECT_NAME"); envProject != "" {
|
||||
return envProject, nil
|
||||
}
|
||||
|
||||
f, err := filepath.Abs(c.ComposeFile)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to get absolute directory for: %s", c.ComposeFile)
|
||||
return "", err
|
||||
}
|
||||
|
||||
f = toUnixPath(f)
|
||||
|
||||
parent := path.Base(path.Dir(f))
|
||||
if parent != "" && parent != "." {
|
||||
return parent, nil
|
||||
} else if wd, err := os.Getwd(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return path.Base(toUnixPath(wd)), nil
|
||||
}
|
||||
}
|
||||
|
||||
func toUnixPath(p string) string {
|
||||
return strings.Replace(p, "\\", "/", -1)
|
||||
}
|
||||
|
||||
func (c *Context) open() error {
|
||||
if c.isOpen {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.readComposeFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.determineProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.isOpen = true
|
||||
return nil
|
||||
}
|
70
vendor/github.com/docker/libcompose/project/empty.go
generated
vendored
Normal file
70
vendor/github.com/docker/libcompose/project/empty.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package project
|
||||
|
||||
// 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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build implements Service.Build but does nothing.
|
||||
func (e *EmptyService) Build() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Up implements Service.Up but does nothing.
|
||||
func (e *EmptyService) Up() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start implements Service.Start but does nothing.
|
||||
func (e *EmptyService) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Down implements Service.Down but does nothing.
|
||||
func (e *EmptyService) Down() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete implements Service.Delete but does nothing.
|
||||
func (e *EmptyService) Delete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart implements Service.Restart but does nothing.
|
||||
func (e *EmptyService) Restart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log implements Service.Log but does nothing.
|
||||
func (e *EmptyService) Log() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pull implements Service.Pull but does nothing.
|
||||
func (e *EmptyService) Pull() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kill implements Service.Kill but does nothing.
|
||||
func (e *EmptyService) Kill() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Containers implements Service.Containers but does nothing.
|
||||
func (e *EmptyService) Containers() ([]Container, error) {
|
||||
return []Container{}, nil
|
||||
}
|
||||
|
||||
// Scale implements Service.Scale but does nothing.
|
||||
func (e *EmptyService) Scale(count int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info implements Service.Info but does nothing.
|
||||
func (e *EmptyService) Info(qFlag bool) (InfoSet, error) {
|
||||
return InfoSet{}, nil
|
||||
}
|
106
vendor/github.com/docker/libcompose/project/hash.go
generated
vendored
Normal file
106
vendor/github.com/docker/libcompose/project/hash.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
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))
|
||||
}
|
42
vendor/github.com/docker/libcompose/project/info.go
generated
vendored
Normal file
42
vendor/github.com/docker/libcompose/project/info.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func (infos InfoSet) String(titleFlag bool) string {
|
||||
//no error checking, none of this should fail
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
tabwriter := tabwriter.NewWriter(buffer, 4, 4, 2, ' ', 0)
|
||||
|
||||
first := true
|
||||
for _, info := range infos {
|
||||
if first && titleFlag {
|
||||
writeLine(tabwriter, true, info)
|
||||
}
|
||||
first = false
|
||||
writeLine(tabwriter, false, info)
|
||||
}
|
||||
|
||||
tabwriter.Flush()
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func writeLine(writer io.Writer, key bool, info Info) {
|
||||
first := true
|
||||
for _, part := range info {
|
||||
if !first {
|
||||
writer.Write([]byte{'\t'})
|
||||
}
|
||||
first = false
|
||||
if key {
|
||||
writer.Write([]byte(part.Key))
|
||||
} else {
|
||||
writer.Write([]byte(part.Value))
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write([]byte{'\n'})
|
||||
}
|
179
vendor/github.com/docker/libcompose/project/interpolation.go
generated
vendored
Normal file
179
vendor/github.com/docker/libcompose/project/interpolation.go
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
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
Normal file
226
vendor/github.com/docker/libcompose/project/interpolation_test.go
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
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}"`)
|
||||
}
|
76
vendor/github.com/docker/libcompose/project/listener.go
generated
vendored
Normal file
76
vendor/github.com/docker/libcompose/project/listener.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
)
|
||||
|
||||
type defaultListener struct {
|
||||
project *Project
|
||||
listenChan chan Event
|
||||
upCount int
|
||||
}
|
||||
|
||||
// NewDefaultListener create a default listener for the specified project.
|
||||
func NewDefaultListener(p *Project) chan<- Event {
|
||||
l := defaultListener{
|
||||
listenChan: make(chan Event),
|
||||
project: p,
|
||||
}
|
||||
go l.start()
|
||||
return l.listenChan
|
||||
}
|
||||
|
||||
func (d *defaultListener) start() {
|
||||
for event := range d.listenChan {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if event.Data != nil {
|
||||
for k, v := range event.Data {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(k)
|
||||
buffer.WriteString("=")
|
||||
buffer.WriteString(v)
|
||||
}
|
||||
}
|
||||
|
||||
if event.EventType == EventServiceUp {
|
||||
d.upCount++
|
||||
}
|
||||
|
||||
logf := logrus.Debugf
|
||||
|
||||
if infoEvents[event.EventType] {
|
||||
logf = logrus.Infof
|
||||
}
|
||||
|
||||
if event.ServiceName == "" {
|
||||
logf("Project [%s]: %s %s", d.project.Name, event.EventType, buffer.Bytes())
|
||||
} else {
|
||||
logf("[%d/%d] [%s]: %s %s", d.upCount, len(d.project.Configs), event.ServiceName, event.EventType, buffer.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
299
vendor/github.com/docker/libcompose/project/merge.go
generated
vendored
Normal file
299
vendor/github.com/docker/libcompose/project/merge.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
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
Normal file
192
vendor/github.com/docker/libcompose/project/merge_test.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
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)
|
||||
}
|
||||
}
|
410
vendor/github.com/docker/libcompose/project/project.go
generated
vendored
Normal file
410
vendor/github.com/docker/libcompose/project/project.go
generated
vendored
Normal file
@@ -0,0 +1,410 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcompose/logger"
|
||||
"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 {
|
||||
p := &Project{
|
||||
context: context,
|
||||
Configs: make(map[string]*ServiceConfig),
|
||||
}
|
||||
|
||||
if context.LoggerFactory == nil {
|
||||
context.LoggerFactory = &logger.NullLogger{}
|
||||
}
|
||||
|
||||
context.Project = p
|
||||
|
||||
p.listeners = []chan<- Event{NewDefaultListener(p)}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Parse populates project information based on its context. It sets up the name,
|
||||
// the composefile and the composebytes (the composefile content).
|
||||
func (p *Project) Parse() error {
|
||||
err := p.context.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Name = p.context.ProjectName
|
||||
|
||||
if p.context.ComposeFile == "-" {
|
||||
p.File = "."
|
||||
} else {
|
||||
p.File = p.context.ComposeFile
|
||||
}
|
||||
|
||||
if p.context.ComposeBytes != nil {
|
||||
return p.Load(p.context.ComposeBytes)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateService creates a service with the specified name based. It 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]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Failed to find service: %s", name)
|
||||
}
|
||||
|
||||
// Copy because we are about to modify the environment
|
||||
config := *existing
|
||||
|
||||
if p.context.EnvironmentLookup != nil {
|
||||
parsedEnv := make([]string, 0, len(config.Environment.Slice()))
|
||||
|
||||
for _, env := range config.Environment.Slice() {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
if len(parts) > 1 && parts[1] != "" {
|
||||
parsedEnv = append(parsedEnv, env)
|
||||
continue
|
||||
} else {
|
||||
env = parts[0]
|
||||
}
|
||||
|
||||
for _, value := range p.context.EnvironmentLookup.Lookup(env, name, &config) {
|
||||
parsedEnv = append(parsedEnv, value)
|
||||
}
|
||||
}
|
||||
|
||||
config.Environment = NewMaporEqualSlice(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)
|
||||
|
||||
p.Configs[name] = config
|
||||
p.reload = append(p.reload, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load loads the specified byte array (the composefile content) and adds the
|
||||
// service configuration to the project.
|
||||
func (p *Project) Load(bytes []byte) error {
|
||||
configs := make(map[string]*ServiceConfig)
|
||||
configs, err := mergeProject(p, bytes)
|
||||
if err != nil {
|
||||
log.Errorf("Could not parse config for project %s : %v", p.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
for name, config := range configs {
|
||||
err := p.AddConfig(name, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) loadWrappers(wrappers map[string]*serviceWrapper, servicesToConstruct []string) error {
|
||||
for _, name := range servicesToConstruct {
|
||||
wrapper, err := newServiceWrapper(name, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wrappers[name] = wrapper
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
})
|
||||
}), 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()
|
||||
})
|
||||
}), 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()
|
||||
})
|
||||
}), 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)
|
||||
}
|
||||
|
||||
// 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()
|
||||
})
|
||||
}), func(service Service) error {
|
||||
return service.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()
|
||||
})
|
||||
}), nil)
|
||||
}
|
||||
|
||||
// Pull pulls the specified services (like docker pull).
|
||||
func (p *Project) Pull(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()
|
||||
})
|
||||
}), 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()
|
||||
})
|
||||
}), 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()
|
||||
})
|
||||
}), nil)
|
||||
}
|
||||
|
||||
func (p *Project) perform(start, done EventType, services []string, action wrapperAction, cycleAction serviceAction) error {
|
||||
p.Notify(start, "", nil)
|
||||
|
||||
err := p.forEach(services, action, cycleAction)
|
||||
|
||||
p.Notify(done, "", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func isSelected(wrapper *serviceWrapper, selected map[string]bool) bool {
|
||||
return len(selected) == 0 || selected[wrapper.name]
|
||||
}
|
||||
|
||||
func (p *Project) forEach(services []string, action wrapperAction, cycleAction serviceAction) error {
|
||||
selected := make(map[string]bool)
|
||||
wrappers := make(map[string]*serviceWrapper)
|
||||
|
||||
for _, s := range services {
|
||||
selected[s] = true
|
||||
}
|
||||
|
||||
return p.traverse(true, selected, wrappers, action, cycleAction)
|
||||
}
|
||||
|
||||
func (p *Project) startService(wrappers map[string]*serviceWrapper, history []string, selected, launched map[string]bool, wrapper *serviceWrapper, action wrapperAction, cycleAction serviceAction) error {
|
||||
if launched[wrapper.name] {
|
||||
return nil
|
||||
}
|
||||
|
||||
launched[wrapper.name] = true
|
||||
history = append(history, wrapper.name)
|
||||
|
||||
for _, dep := range wrapper.service.DependentServices() {
|
||||
target := wrappers[dep.Target]
|
||||
if target == nil {
|
||||
log.Errorf("Failed to find %s", dep.Target)
|
||||
continue
|
||||
}
|
||||
|
||||
if utils.Contains(history, dep.Target) {
|
||||
cycle := strings.Join(append(history, dep.Target), "->")
|
||||
if dep.Optional {
|
||||
log.Debugf("Ignoring cycle for %s", cycle)
|
||||
wrapper.IgnoreDep(dep.Target)
|
||||
if cycleAction != nil {
|
||||
var err error
|
||||
log.Debugf("Running cycle action for %s", cycle)
|
||||
err = cycleAction(target.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Cycle detected in path %s", cycle)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err := p.startService(wrappers, history, selected, launched, target, action, cycleAction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isSelected(wrapper, selected) {
|
||||
log.Debugf("Launching action for %s", wrapper.name)
|
||||
go action(wrapper, wrappers)
|
||||
} else {
|
||||
wrapper.Ignore()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) traverse(start bool, selected map[string]bool, wrappers map[string]*serviceWrapper, action wrapperAction, cycleAction serviceAction) error {
|
||||
restart := false
|
||||
wrapperList := []string{}
|
||||
|
||||
if start {
|
||||
for name := range p.Configs {
|
||||
wrapperList = append(wrapperList, name)
|
||||
}
|
||||
} else {
|
||||
for _, wrapper := range wrappers {
|
||||
if err := wrapper.Reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
wrapperList = p.reload
|
||||
}
|
||||
|
||||
p.loadWrappers(wrappers, wrapperList)
|
||||
p.reload = []string{}
|
||||
|
||||
// check service name
|
||||
for s := range selected {
|
||||
if wrappers[s] == nil {
|
||||
return errors.New("No such service: " + s)
|
||||
}
|
||||
}
|
||||
|
||||
launched := map[string]bool{}
|
||||
|
||||
for _, wrapper := range wrappers {
|
||||
p.startService(wrappers, []string{}, selected, launched, wrapper, action, cycleAction)
|
||||
}
|
||||
|
||||
var firstError error
|
||||
|
||||
for _, wrapper := range wrappers {
|
||||
if !isSelected(wrapper, selected) {
|
||||
continue
|
||||
}
|
||||
if err := wrapper.Wait(); err == ErrRestart {
|
||||
restart = true
|
||||
} else if err != nil {
|
||||
log.Errorf("Failed to start: %s : %v", wrapper.name, err)
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if restart {
|
||||
if p.ReloadCallback != nil {
|
||||
if err := p.ReloadCallback(); err != nil {
|
||||
log.Errorf("Failed calling callback: %v", err)
|
||||
}
|
||||
}
|
||||
return p.traverse(false, selected, wrappers, action, cycleAction)
|
||||
}
|
||||
return firstError
|
||||
}
|
||||
|
||||
// AddListener adds the specified listener to the project.
|
||||
func (p *Project) AddListener(c chan<- Event) {
|
||||
if !p.hasListeners {
|
||||
for _, l := range p.listeners {
|
||||
close(l)
|
||||
}
|
||||
p.hasListeners = true
|
||||
p.listeners = []chan<- 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 {
|
||||
return
|
||||
}
|
||||
|
||||
event := Event{
|
||||
EventType: eventType,
|
||||
ServiceName: serviceName,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
for _, l := range p.listeners {
|
||||
l <- event
|
||||
}
|
||||
}
|
148
vendor/github.com/docker/libcompose/project/project_test.go
generated
vendored
Normal file
148
vendor/github.com/docker/libcompose/project/project_test.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
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())
|
||||
}
|
||||
}
|
114
vendor/github.com/docker/libcompose/project/service-wrapper.go
generated
vendored
Normal file
114
vendor/github.com/docker/libcompose/project/service-wrapper.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
type serviceWrapper struct {
|
||||
name string
|
||||
service Service
|
||||
done sync.WaitGroup
|
||||
state ServiceState
|
||||
err error
|
||||
project *Project
|
||||
noWait bool
|
||||
ignored map[string]bool
|
||||
}
|
||||
|
||||
func newServiceWrapper(name string, p *Project) (*serviceWrapper, error) {
|
||||
wrapper := &serviceWrapper{
|
||||
name: name,
|
||||
state: StateUnknown,
|
||||
project: p,
|
||||
ignored: map[string]bool{},
|
||||
}
|
||||
|
||||
return wrapper, wrapper.Reset()
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) IgnoreDep(name string) {
|
||||
s.ignored[name] = true
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) Reset() error {
|
||||
if s.state != StateExecuted {
|
||||
service, err := s.project.CreateService(s.name)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create service for %s : %v", s.name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
s.service = service
|
||||
}
|
||||
|
||||
if s.err == ErrRestart {
|
||||
s.err = nil
|
||||
}
|
||||
s.done.Add(1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) Ignore() {
|
||||
defer s.done.Done()
|
||||
|
||||
s.state = StateExecuted
|
||||
s.project.Notify(EventServiceUpIgnored, s.service.Name(), nil)
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) waitForDeps(wrappers map[string]*serviceWrapper) bool {
|
||||
if s.noWait {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, dep := range s.service.DependentServices() {
|
||||
if s.ignored[dep.Target] {
|
||||
continue
|
||||
}
|
||||
|
||||
if wrapper, ok := wrappers[dep.Target]; ok {
|
||||
if wrapper.Wait() == ErrRestart {
|
||||
s.project.Notify(EventProjectReload, wrapper.service.Name(), nil)
|
||||
s.err = ErrRestart
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
log.Errorf("Failed to find %s", dep.Target)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) Do(wrappers map[string]*serviceWrapper, start, done EventType, action func(service Service) error) {
|
||||
defer s.done.Done()
|
||||
|
||||
if s.state == StateExecuted {
|
||||
return
|
||||
}
|
||||
|
||||
if wrappers != nil && !s.waitForDeps(wrappers) {
|
||||
return
|
||||
}
|
||||
|
||||
s.state = StateExecuted
|
||||
|
||||
s.project.Notify(start, s.service.Name(), nil)
|
||||
|
||||
s.err = action(s.service)
|
||||
if s.err == ErrRestart {
|
||||
s.project.Notify(done, s.service.Name(), nil)
|
||||
s.project.Notify(EventProjectReloadTrigger, s.service.Name(), nil)
|
||||
} else if s.err != nil {
|
||||
log.Errorf("Failed %s %s : %v", start, s.name, s.err)
|
||||
} else {
|
||||
s.project.Notify(done, s.service.Name(), nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceWrapper) Wait() error {
|
||||
s.done.Wait()
|
||||
return s.err
|
||||
}
|
295
vendor/github.com/docker/libcompose/project/types.go
generated
vendored
Normal file
295
vendor/github.com/docker/libcompose/project/types.go
generated
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
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,
|
||||
}
|
||||
}
|
297
vendor/github.com/docker/libcompose/project/types_yaml.go
generated
vendored
Normal file
297
vendor/github.com/docker/libcompose/project/types_yaml.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
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) {
|
||||
if s.parts == nil {
|
||||
return "", []string{}, nil
|
||||
}
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *Stringorslice) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts := make([]string, len(value))
|
||||
for k, v := range value {
|
||||
parts[k] = v.(string)
|
||||
}
|
||||
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) {
|
||||
if s.parts == nil {
|
||||
return "", []string{}, nil
|
||||
}
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *Command) UnmarshalYAML(tag string, value interface{}) error {
|
||||
var err error
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts := make([]string, len(value))
|
||||
for k, v := range value {
|
||||
parts[k] = v.(string)
|
||||
}
|
||||
s.parts = parts
|
||||
case string:
|
||||
s.parts, err = shlex.Split(value)
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal Command: %#v", value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if s.parts == nil {
|
||||
return "", map[string]string{}, nil
|
||||
}
|
||||
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 {
|
||||
parts[k.(string)] = v.(string)
|
||||
}
|
||||
s.parts = parts
|
||||
case []interface{}:
|
||||
parts := map[string]string{}
|
||||
for _, str := range value {
|
||||
str := strings.TrimSpace(str.(string))
|
||||
keyValueSlice := strings.SplitN(str, "=", 2)
|
||||
|
||||
key := keyValueSlice[0]
|
||||
val := ""
|
||||
if len(keyValueSlice) == 2 {
|
||||
val = keyValueSlice[1]
|
||||
}
|
||||
parts[key] = val
|
||||
}
|
||||
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) {
|
||||
if s.parts == nil {
|
||||
return "", []string{}, nil
|
||||
}
|
||||
return "", s.parts, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaller interface.
|
||||
func (s *MaporEqualSlice) UnmarshalYAML(tag string, value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case []interface{}:
|
||||
parts := make([]string, len(value))
|
||||
for k, v := range value {
|
||||
parts[k] = v.(string)
|
||||
}
|
||||
s.parts = parts
|
||||
case map[interface{}]interface{}:
|
||||
parts := make([]string, 0, len(value))
|
||||
for k, v := range value {
|
||||
parts = append(parts, strings.Join([]string{k.(string), v.(string)}, "="))
|
||||
}
|
||||
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) {
|
||||
if s.parts == nil {
|
||||
return "", []string{}, nil
|
||||
}
|
||||
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 := make([]string, len(value))
|
||||
for k, v := range value {
|
||||
parts[k] = v.(string)
|
||||
}
|
||||
s.parts = parts
|
||||
case map[interface{}]interface{}:
|
||||
parts := make([]string, 0, len(value))
|
||||
for k, v := range value {
|
||||
parts = append(parts, strings.Join([]string{k.(string), v.(string)}, ":"))
|
||||
}
|
||||
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) {
|
||||
if s.parts == nil {
|
||||
return "", []string{}, nil
|
||||
}
|
||||
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 := make([]string, len(value))
|
||||
for k, v := range value {
|
||||
parts[k] = v.(string)
|
||||
}
|
||||
s.parts = parts
|
||||
case map[interface{}]interface{}:
|
||||
parts := make([]string, 0, len(value))
|
||||
for k, v := range value {
|
||||
parts = append(parts, strings.Join([]string{k.(string), v.(string)}, " "))
|
||||
}
|
||||
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}
|
||||
}
|
194
vendor/github.com/docker/libcompose/project/types_yaml_test.go
generated
vendored
Normal file
194
vendor/github.com/docker/libcompose/project/types_yaml_test.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"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",
|
||||
Entrypoint: Command{[]string{}},
|
||||
Command: Command{[]string{}},
|
||||
Restart: "always",
|
||||
Net: "host",
|
||||
Privileged: true,
|
||||
DNS: Stringorslice{[]string{"8.8.8.8", "8.8.4.4"}},
|
||||
DNSSearch: Stringorslice{[]string{}},
|
||||
EnvFile: Stringorslice{[]string{}},
|
||||
Environment: MaporEqualSlice{[]string{
|
||||
"DAEMON=true",
|
||||
}},
|
||||
Labels: SliceorMap{map[string]string{
|
||||
"io.rancher.os.detach": "true",
|
||||
"io.rancher.os.scope": "system",
|
||||
}},
|
||||
Links: MaporColonSlice{[]string{}},
|
||||
VolumesFrom: []string{
|
||||
"system-volumes",
|
||||
},
|
||||
},
|
||||
"system-volumes": {
|
||||
Image: "state",
|
||||
Entrypoint: Command{[]string{}},
|
||||
Command: Command{[]string{}},
|
||||
Net: "none",
|
||||
ReadOnly: true,
|
||||
Privileged: true,
|
||||
DNS: Stringorslice{[]string{}},
|
||||
DNSSearch: Stringorslice{[]string{}},
|
||||
EnvFile: Stringorslice{[]string{}},
|
||||
Environment: MaporEqualSlice{[]string{}},
|
||||
Labels: SliceorMap{map[string]string{
|
||||
"io.rancher.os.createonly": "true",
|
||||
"io.rancher.os.scope": "system",
|
||||
}},
|
||||
Links: MaporColonSlice{[]string{}},
|
||||
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"`
|
||||
}
|
||||
|
||||
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 = `udav:
|
||||
foos:
|
||||
io.rancher.os.bar: baz
|
||||
io.rancher.os.far: faz
|
||||
bars: []
|
||||
`
|
||||
|
||||
func TestStr2SliceOrMapPtrMap(t *testing.T) {
|
||||
s := map[string]*StructSliceorMap{"udav": {
|
||||
Foos: SliceorMap{map[string]string{"io.rancher.os.bar": "baz", "io.rancher.os.far": "faz"}},
|
||||
Bars: []string{},
|
||||
}}
|
||||
d, err := yaml.Marshal(&s)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, sampleStructSliceorMap, string(d))
|
||||
|
||||
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"))
|
||||
}
|
68
vendor/github.com/docker/libcompose/project/utils.go
generated
vendored
Normal file
68
vendor/github.com/docker/libcompose/project/utils.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
// DefaultDependentServices return the dependent services (as an array of ServiceRelationship)
|
||||
// for the specified project and service. It looks for : links, volumesFrom, net and ipc configuration.
|
||||
func DefaultDependentServices(p *Project, s Service) []ServiceRelationship {
|
||||
config := s.Config()
|
||||
if config == nil {
|
||||
return []ServiceRelationship{}
|
||||
}
|
||||
|
||||
result := []ServiceRelationship{}
|
||||
for _, link := range config.Links.Slice() {
|
||||
result = append(result, NewServiceRelationship(link, RelTypeLink))
|
||||
}
|
||||
|
||||
for _, volumesFrom := range config.VolumesFrom {
|
||||
result = append(result, NewServiceRelationship(volumesFrom, RelTypeVolumesFrom))
|
||||
}
|
||||
|
||||
result = appendNs(p, result, s.Config().Net, RelTypeNetNamespace)
|
||||
result = appendNs(p, result, s.Config().Ipc, RelTypeIpcNamespace)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func appendNs(p *Project, rels []ServiceRelationship, conf string, relType ServiceRelationshipType) []ServiceRelationship {
|
||||
service := GetContainerFromIpcLikeConfig(p, conf)
|
||||
if service != "" {
|
||||
rels = append(rels, NewServiceRelationship(service, relType))
|
||||
}
|
||||
return rels
|
||||
}
|
||||
|
||||
// NameAlias returns the name and alias based on the specified string.
|
||||
// If the name contains a colon (like name:alias) it will split it, otherwise
|
||||
// it will return the specified name as name and alias.
|
||||
func NameAlias(name string) (string, string) {
|
||||
parts := strings.SplitN(name, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
return parts[0], parts[0]
|
||||
}
|
||||
|
||||
// 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)
|
||||
if !ipc.IsContainer() {
|
||||
return ""
|
||||
}
|
||||
|
||||
name := ipc.Container()
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if _, ok := p.Configs[name]; ok {
|
||||
return name
|
||||
}
|
||||
return ""
|
||||
}
|
54
vendor/github.com/docker/libcompose/project/yaml_test.go
generated
vendored
Normal file
54
vendor/github.com/docker/libcompose/project/yaml_test.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
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