bump(github.com/docker/libcompose) v0.3.0

This commit is contained in:
Tomas Kral
2016-08-22 10:51:22 +02:00
parent 223b4f37fa
commit e341adce0e
57 changed files with 944 additions and 12907 deletions
+1 -1
View File
@@ -6,10 +6,10 @@ import (
"fmt"
"strings"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/docker/docker/pkg/urlutil"
"github.com/docker/libcompose/utils"
composeYaml "github.com/docker/libcompose/yaml"
"gopkg.in/yaml.v2"
)
var (
+1 -1
View File
@@ -5,8 +5,8 @@ import (
"path"
"github.com/Sirupsen/logrus"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/docker/libcompose/utils"
"gopkg.in/yaml.v2"
)
// MergeServicesV1 merges a v1 compose file into an existing set of service configs
+1 -1
View File
@@ -5,8 +5,8 @@ import (
"path"
"github.com/Sirupsen/logrus"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/docker/libcompose/utils"
"gopkg.in/yaml.v2"
)
// MergeServicesV2 merges a v2 compose file into an existing set of service configs
+18 -11
View File
@@ -23,9 +23,9 @@ type ServiceConfigV1 struct {
CapAdd []string `yaml:"cap_add,omitempty"`
CapDrop []string `yaml:"cap_drop,omitempty"`
CgroupParent string `yaml:"cgroup_parent,omitempty"`
CPUQuota int64 `yaml:"cpu_quota,omitempty"`
CPUQuota yaml.StringorInt `yaml:"cpu_quota,omitempty"`
CPUSet string `yaml:"cpuset,omitempty"`
CPUShares int64 `yaml:"cpu_shares,omitempty"`
CPUShares yaml.StringorInt `yaml:"cpu_shares,omitempty"`
Command yaml.Command `yaml:"command,flow,omitempty"`
ContainerName string `yaml:"container_name,omitempty"`
Devices []string `yaml:"devices,omitempty"`
@@ -42,8 +42,8 @@ type ServiceConfigV1 struct {
Links yaml.MaporColonSlice `yaml:"links,omitempty"`
LogDriver string `yaml:"log_driver,omitempty"`
MacAddress string `yaml:"mac_address,omitempty"`
MemLimit int64 `yaml:"mem_limit,omitempty"`
MemSwapLimit int64 `yaml:"memswap_limit,omitempty"`
MemLimit yaml.StringorInt `yaml:"mem_limit,omitempty"`
MemSwapLimit yaml.StringorInt `yaml:"memswap_limit,omitempty"`
Name string `yaml:"name,omitempty"`
Net string `yaml:"net,omitempty"`
Pid string `yaml:"pid,omitempty"`
@@ -53,7 +53,7 @@ type ServiceConfigV1 struct {
Privileged bool `yaml:"privileged,omitempty"`
Restart string `yaml:"restart,omitempty"`
ReadOnly bool `yaml:"read_only,omitempty"`
ShmSize int64 `yaml:"shm_size,omitempty"`
ShmSize yaml.StringorInt `yaml:"shm_size,omitempty"`
StdinOpen bool `yaml:"stdin_open,omitempty"`
SecurityOpt []string `yaml:"security_opt,omitempty"`
Tty bool `yaml:"tty,omitempty"`
@@ -81,8 +81,8 @@ type ServiceConfig struct {
CapAdd []string `yaml:"cap_add,omitempty"`
CapDrop []string `yaml:"cap_drop,omitempty"`
CPUSet string `yaml:"cpuset,omitempty"`
CPUShares int64 `yaml:"cpu_shares,omitempty"`
CPUQuota int64 `yaml:"cpu_quota,omitempty"`
CPUShares yaml.StringorInt `yaml:"cpu_shares,omitempty"`
CPUQuota yaml.StringorInt `yaml:"cpu_quota,omitempty"`
Command yaml.Command `yaml:"command,flow,omitempty"`
CgroupParent string `yaml:"cgroup_parent,omitempty"`
ContainerName string `yaml:"container_name,omitempty"`
@@ -105,18 +105,18 @@ type ServiceConfig struct {
Links yaml.MaporColonSlice `yaml:"links,omitempty"`
Logging Log `yaml:"logging,omitempty"`
MacAddress string `yaml:"mac_address,omitempty"`
MemLimit int64 `yaml:"mem_limit,omitempty"`
MemSwapLimit int64 `yaml:"memswap_limit,omitempty"`
MemLimit yaml.StringorInt `yaml:"mem_limit,omitempty"`
MemSwapLimit yaml.StringorInt `yaml:"memswap_limit,omitempty"`
NetworkMode string `yaml:"network_mode,omitempty"`
Networks *yaml.Networks `yaml:"networks,omitempty"`
Pid string `yaml:"pid,omitempty"`
Ports []string `yaml:"ports,omitempty"`
Privileged bool `yaml:"privileged,omitempty"`
SecurityOpt []string `yaml:"security_opt,omitempty"`
ShmSize int64 `yaml:"shm_size,omitempty"`
ShmSize yaml.StringorInt `yaml:"shm_size,omitempty"`
StopSignal string `yaml:"stop_signal,omitempty"`
VolumeDriver string `yaml:"volume_driver,omitempty"`
Volumes []string `yaml:"volumes,omitempty"`
Volumes *yaml.Volumes `yaml:"volumes,omitempty"`
VolumesFrom []string `yaml:"volumes_from,omitempty"`
Uts string `yaml:"uts,omitempty"`
Restart string `yaml:"restart,omitempty"`
@@ -201,6 +201,13 @@ func (c *ServiceConfigs) Add(name string, service *ServiceConfig) {
c.mu.Unlock()
}
// Remove removes the config with the specified name
func (c *ServiceConfigs) Remove(name string) {
c.mu.Lock()
delete(c.m, name)
c.mu.Unlock()
}
// Len returns the len of the configs
func (c *ServiceConfigs) Len() int {
c.mu.RLock()
+31 -21
View File
@@ -16,6 +16,7 @@ import (
composeclient "github.com/docker/libcompose/docker/client"
"github.com/docker/libcompose/project"
"github.com/docker/libcompose/utils"
// "github.com/docker/libcompose/yaml"
)
// ConfigWrapper wraps Config, HostConfig and NetworkingConfig for a container.
@@ -36,6 +37,16 @@ func Filter(vs []string, f func(string) bool) []string {
return r
}
func toMap(vs []string) map[string]struct{} {
m := map[string]struct{}{}
for _, v := range vs {
if v != "" {
m[v] = struct{}{}
}
}
return m
}
func isBind(s string) bool {
return strings.ContainsRune(s, ':')
}
@@ -58,21 +69,18 @@ func ConvertToAPI(serviceConfig *config.ServiceConfig, ctx project.Context, clie
return &result, nil
}
func isNamedVolume(volume string) bool {
return !strings.HasPrefix(volume, ".") && !strings.HasPrefix(volume, "/") && !strings.HasPrefix(volume, "~")
}
func volumes(c *config.ServiceConfig, ctx project.Context) map[string]struct{} {
volumes := make(map[string]struct{}, len(c.Volumes))
for k, v := range c.Volumes {
if len(ctx.ComposeFiles) > 0 && !isNamedVolume(v) {
v = ctx.ResourceLookup.ResolvePath(v, ctx.ComposeFiles[0])
}
c.Volumes[k] = v
if isVolume(v) {
volumes[v] = struct{}{}
func volumes(c *config.ServiceConfig, ctx project.Context) []string {
if c.Volumes == nil {
return []string{}
}
volumes := make([]string, len(c.Volumes.Volumes))
for _, v := range c.Volumes.Volumes {
vol := v
if len(ctx.ComposeFiles) > 0 && !project.IsNamedVolume(v.Source) {
sourceVol := ctx.ResourceLookup.ResolvePath(v.String(), ctx.ComposeFiles[0])
vol.Source = strings.SplitN(sourceVol, ":", 2)[0]
}
volumes = append(volumes, vol.String())
}
return volumes
}
@@ -141,6 +149,8 @@ func Convert(c *config.ServiceConfig, ctx project.Context, clientFactory compose
}
}
vols := volumes(c, ctx)
config := &container.Config{
Entrypoint: strslice.StrSlice(utils.CopySlice(c.Entrypoint)),
Hostname: c.Hostname,
@@ -154,7 +164,7 @@ func Convert(c *config.ServiceConfig, ctx project.Context, clientFactory compose
Tty: c.Tty,
OpenStdin: c.StdinOpen,
WorkingDir: c.WorkingDir,
Volumes: volumes(c, ctx),
Volumes: toMap(Filter(vols, isVolume)),
MacAddress: c.MacAddress,
}
@@ -171,10 +181,10 @@ func Convert(c *config.ServiceConfig, ctx project.Context, clientFactory compose
resources := container.Resources{
CgroupParent: c.CgroupParent,
Memory: c.MemLimit,
MemorySwap: c.MemSwapLimit,
CPUShares: c.CPUShares,
CPUQuota: c.CPUQuota,
Memory: int64(c.MemLimit),
MemorySwap: int64(c.MemSwapLimit),
CPUShares: int64(c.CPUShares),
CPUQuota: int64(c.CPUQuota),
CpusetCpus: c.CPUSet,
Ulimits: ulimits,
Devices: deviceMappings,
@@ -228,7 +238,7 @@ func Convert(c *config.ServiceConfig, ctx project.Context, clientFactory compose
CapDrop: strslice.StrSlice(utils.CopySlice(c.CapDrop)),
ExtraHosts: utils.CopySlice(c.ExtraHosts),
Privileged: c.Privileged,
Binds: Filter(c.Volumes, isBind),
Binds: Filter(vols, isBind),
DNS: utils.CopySlice(c.DNS),
DNSSearch: utils.CopySlice(c.DNSSearch),
LogConfig: container.LogConfig{
@@ -242,7 +252,7 @@ func Convert(c *config.ServiceConfig, ctx project.Context, clientFactory compose
IpcMode: container.IpcMode(c.Ipc),
PortBindings: portBindings,
RestartPolicy: *restartPolicy,
ShmSize: c.ShmSize,
ShmSize: int64(c.ShmSize),
SecurityOpt: utils.CopySlice(c.SecurityOpt),
VolumeDriver: c.VolumeDriver,
Resources: resources,
+8
View File
@@ -12,6 +12,7 @@ import (
"github.com/docker/libcompose/config"
"github.com/docker/libcompose/docker/client"
"github.com/docker/libcompose/docker/network"
"github.com/docker/libcompose/docker/volume"
"github.com/docker/libcompose/labels"
"github.com/docker/libcompose/lookup"
"github.com/docker/libcompose/project"
@@ -66,6 +67,13 @@ func NewProject(context *Context, parseOptions *config.ParseOptions) (project.AP
context.NetworksFactory = networksFactory
}
if context.VolumesFactory == nil {
volumesFactory := &volume.DockerFactory{
ClientFactory: context.ClientFactory,
}
context.VolumesFactory = volumesFactory
}
// FIXME(vdemeester) Remove the context duplication ?
runtime := &Project{
clientFactory: context.ClientFactory,
+154
View File
@@ -0,0 +1,154 @@
package volume
import (
"fmt"
"golang.org/x/net/context"
"github.com/docker/engine-api/client"
"github.com/docker/engine-api/types"
"github.com/docker/libcompose/config"
composeclient "github.com/docker/libcompose/docker/client"
"github.com/docker/libcompose/project"
)
// Volume holds attributes and method for a volume definition in compose
type Volume struct {
client client.VolumeAPIClient
projectName string
name string
driver string
driverOptions map[string]string
external bool
// TODO (shouze) missing labels
}
func (v *Volume) fullName() string {
name := v.projectName + "_" + v.name
if v.external {
name = v.name
}
return name
}
// Inspect inspect the current volume
func (v *Volume) Inspect(ctx context.Context) (types.Volume, error) {
return v.client.VolumeInspect(ctx, v.fullName())
}
// Remove removes the current volume (from docker engine)
func (v *Volume) Remove(ctx context.Context) error {
if v.external {
fmt.Printf("Volume %s is external, skipping", v.fullName())
return nil
}
fmt.Printf("Removing volume %q\n", v.fullName())
return v.client.VolumeRemove(ctx, v.fullName())
}
// EnsureItExists make sure the volume exists and return an error if it does not exists
// and cannot be created.
func (v *Volume) EnsureItExists(ctx context.Context) error {
volumeResource, err := v.Inspect(ctx)
if v.external {
if client.IsErrVolumeNotFound(err) {
// FIXME(shouze) introduce some libcompose error type
return fmt.Errorf("Volume %s declared as external, but could not be found. Please create the volume manually using docker volume create %s and try again", v.name, v.name)
}
return err
}
if err != nil && client.IsErrVolumeNotFound(err) {
return v.create(ctx)
}
if volumeResource.Driver != v.driver {
return fmt.Errorf("Volume %q needs to be recreated - driver has changed", v.name)
}
return err
}
func (v *Volume) create(ctx context.Context) error {
fmt.Printf("Creating volume %q with driver %q\n", v.fullName(), v.driver)
_, err := v.client.VolumeCreate(ctx, types.VolumeCreateRequest{
Name: v.fullName(),
Driver: v.driver,
DriverOpts: v.driverOptions,
// TODO (shouze) missing labels
})
return err
}
// NewVolume creates a new volume from the specified name and config.
func NewVolume(projectName, name string, config *config.VolumeConfig, client client.VolumeAPIClient) *Volume {
return &Volume{
client: client,
projectName: projectName,
name: name,
driver: config.Driver,
driverOptions: config.DriverOpts,
external: config.External.External,
}
}
// Volumes holds a list of volume
type Volumes struct {
volumes []*Volume
volumeEnabled bool
}
// Initialize make sure volume exists if volume is enabled
func (v *Volumes) Initialize(ctx context.Context) error {
if !v.volumeEnabled {
return nil
}
for _, volume := range v.volumes {
err := volume.EnsureItExists(ctx)
if err != nil {
return err
}
}
return nil
}
// Remove removes volumes (clean-up)
func (v *Volumes) Remove(ctx context.Context) error {
if !v.volumeEnabled {
return nil
}
for _, volume := range v.volumes {
err := volume.Remove(ctx)
if err != nil {
return err
}
}
return nil
}
// VolumesFromServices creates a new Volumes struct based on volumes configurations and
// services configuration. If a volume is defined but not used by any service, it will return
// an error along the Volumes.
func VolumesFromServices(cli client.VolumeAPIClient, projectName string, volumeConfigs map[string]*config.VolumeConfig, services *config.ServiceConfigs, volumeEnabled bool) (*Volumes, error) {
var err error
volumes := make([]*Volume, 0, len(volumeConfigs))
for name, config := range volumeConfigs {
volume := NewVolume(projectName, name, config, cli)
volumes = append(volumes, volume)
}
return &Volumes{
volumes: volumes,
volumeEnabled: volumeEnabled,
}, err
}
// DockerFactory implements project.VolumesFactory
type DockerFactory struct {
ClientFactory composeclient.Factory
}
// Create implements project.VolumesFactory Create method.
// It creates a Volumes (that implements project.Volumes) from specified configurations.
func (f *DockerFactory) Create(projectName string, volumeConfigs map[string]*config.VolumeConfig, serviceConfigs *config.ServiceConfigs, volumeEnabled bool) (project.Volumes, error) {
cli := f.ClientFactory.Create(nil)
return VolumesFromServices(cli, projectName, volumeConfigs, serviceConfigs, volumeEnabled)
}
+1
View File
@@ -25,6 +25,7 @@ type Context struct {
isOpen bool
ServiceFactory ServiceFactory
NetworksFactory NetworksFactory
VolumesFactory VolumesFactory
EnvironmentLookup config.EnvironmentLookup
ResourceLookup config.ResourceLookup
LoggerFactory logger.Factory
+16 -1
View File
@@ -37,9 +37,24 @@ type APIProject interface {
CreateService(name string) (Service, error)
AddConfig(name string, config *config.ServiceConfig) error
Load(bytes []byte) error
ListStoppedContainers(ctx context.Context, services ...string) ([]string, error)
Containers(ctx context.Context, filter Filter, services ...string) ([]string, error)
}
// Filter holds filter element to filter containers
type Filter struct {
State State
}
// State defines the supported state you can filter on
type State string
// Definitions of filter states
const (
AnyState = State("")
Running = State("running")
Stopped = State("stopped")
)
// RuntimeProject defines runtime-specific methods for a libcompose implementation.
type RuntimeProject interface {
RemoveOrphans(ctx context.Context, projectName string, serviceConfigs *config.ServiceConfigs) error
+106 -25
View File
@@ -31,6 +31,7 @@ type Project struct {
runtime RuntimeProject
networks Networks
volumes Volumes
configVersion string
context *Context
reload []string
@@ -205,6 +206,31 @@ func (p *Project) load(file string, bytes []byte) error {
}
// Update network configuration a little bit
p.handleNetworkConfig()
p.handleVolumeConfig()
if p.context.NetworksFactory != nil {
networks, err := p.context.NetworksFactory.Create(p.Name, p.NetworkConfigs, p.ServiceConfigs, p.isNetworkEnabled())
if err != nil {
return err
}
p.networks = networks
}
if p.context.VolumesFactory != nil {
volumes, err := p.context.VolumesFactory.Create(p.Name, p.VolumeConfigs, p.ServiceConfigs, p.isVolumeEnabled())
if err != nil {
return err
}
p.volumes = volumes
}
return nil
}
func (p *Project) handleNetworkConfig() {
if p.isNetworkEnabled() {
for _, serviceName := range p.ServiceConfigs.Keys() {
serviceConfig, _ := p.ServiceConfigs.Get(serviceName)
@@ -238,24 +264,47 @@ func (p *Project) load(file string, bytes []byte) error {
}
}
}
// FIXME(vdemeester) Not sure about this..
if p.context.NetworksFactory != nil {
networks, err := p.context.NetworksFactory.Create(p.Name, p.NetworkConfigs, p.ServiceConfigs, p.isNetworkEnabled())
if err != nil {
return err
}
p.networks = networks
}
return nil
}
func (p *Project) isNetworkEnabled() bool {
return p.configVersion == "2"
}
func (p *Project) handleVolumeConfig() {
if p.isVolumeEnabled() {
for _, serviceName := range p.ServiceConfigs.Keys() {
serviceConfig, _ := p.ServiceConfigs.Get(serviceName)
// Consolidate the name of the volume
// FIXME(vdemeester) probably shouldn't be there, maybe move that to interface/factory
if serviceConfig.Volumes == nil {
continue
}
for _, volume := range serviceConfig.Volumes.Volumes {
if !IsNamedVolume(volume.Source) {
continue
}
vol, ok := p.VolumeConfigs[volume.Source]
if !ok {
continue
}
if vol.External.External {
if vol.External.Name != "" {
volume.Source = vol.External.Name
}
} else {
volume.Source = p.Name + "_" + volume.Source
}
}
}
}
}
func (p *Project) isVolumeEnabled() bool {
return p.configVersion == "2"
}
// initialize sets up required element for project before any action (on project and service).
// This means it's not needed to be called on Config for example.
func (p *Project) initialize(ctx context.Context) error {
@@ -264,7 +313,11 @@ func (p *Project) initialize(ctx context.Context) error {
return err
}
}
// TODO Initialize volumes
if p.volumes != nil {
if err := p.volumes.Initialize(ctx); err != nil {
return err
}
}
return nil
}
@@ -337,6 +390,16 @@ func (p *Project) Down(ctx context.Context, opts options.Down, services ...strin
return err
}
if opts.RemoveVolume {
volumes, err := p.context.VolumesFactory.Create(p.Name, p.VolumeConfigs, p.ServiceConfigs, p.isVolumeEnabled())
if err != nil {
return err
}
if err := volumes.Remove(ctx); err != nil {
return err
}
}
return p.forEach([]string{}, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(wrappers, events.NoEvent, events.NoEvent, func(service Service) error {
return service.RemoveImage(ctx, opts.RemoveImages)
@@ -496,37 +559,50 @@ func (p *Project) Pull(ctx context.Context, services ...string) error {
}), nil)
}
// ListStoppedContainers lists the stopped containers for the specified services.
func (p *Project) ListStoppedContainers(ctx context.Context, services ...string) ([]string, error) {
stoppedContainers := []string{}
// Containers lists the containers for the specified services. Can be filter using
// the Filter struct.
func (p *Project) Containers(ctx context.Context, filter Filter, services ...string) ([]string, error) {
containers := []string{}
err := p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
wrapper.Do(nil, events.NoEvent, events.NoEvent, func(service Service) error {
containers, innerErr := service.Containers(ctx)
serviceContainers, innerErr := service.Containers(ctx)
if innerErr != nil {
return innerErr
}
for _, container := range containers {
for _, container := range serviceContainers {
running, innerErr := container.IsRunning(ctx)
if innerErr != nil {
log.Error(innerErr)
}
if !running {
containerID, innerErr := container.ID()
if innerErr != nil {
log.Error(innerErr)
switch filter.State {
case Running:
if !running {
continue
}
stoppedContainers = append(stoppedContainers, containerID)
case Stopped:
if running {
continue
}
case AnyState:
// Don't do a thing
default:
// Invalid state filter
return fmt.Errorf("Invalid container filter: %s", filter.State)
}
containerID, innerErr := container.ID()
if innerErr != nil {
log.Error(innerErr)
}
containers = append(containers, containerID)
}
return nil
})
}), nil)
if err != nil {
return nil, err
}
return stoppedContainers, nil
return containers, nil
}
// Delete removes the specified services (like docker rm).
@@ -733,3 +809,8 @@ func (p *Project) Notify(eventType events.EventType, serviceName string, data ma
l <- event
}
}
// IsNamedVolume returns whether the specified volume (string) is a named volume or not.
func IsNamedVolume(volume string) bool {
return !strings.HasPrefix(volume, ".") && !strings.HasPrefix(volume, "/") && !strings.HasPrefix(volume, "~")
}
+19
View File
@@ -0,0 +1,19 @@
package project
import (
"golang.org/x/net/context"
"github.com/docker/libcompose/config"
)
// Volumes defines the methods a libcompose volume aggregate should define.
type Volumes interface {
Initialize(ctx context.Context) error
Remove(ctx context.Context) error
}
// VolumesFactory is an interface factory to create Volumes object for the specified
// configurations (service, volumes, …)
type VolumesFactory interface {
Create(projectName string, volumeConfigs map[string]*config.VolumeConfig, serviceConfigs *config.ServiceConfigs, volumeEnabled bool) (Volumes, error)
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"github.com/Sirupsen/logrus"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"gopkg.in/yaml.v2"
)
// InParallel holds a pool and a waitgroup to execute tasks in parallel and to be able
+1 -1
View File
@@ -2,7 +2,7 @@ package version
var (
// VERSION should be updated by hand at each release
VERSION = "0.3.0-dev"
VERSION = "0.3.0"
// GITCOMMIT will be overwritten automatically by the build system
GITCOMMIT = "HEAD"
+18 -11
View File
@@ -1,6 +1,7 @@
package yaml
import (
"errors"
"fmt"
"strconv"
"strings"
@@ -15,7 +16,7 @@ type Build struct {
}
// MarshalYAML implements the Marshaller interface.
func (b Build) MarshalYAML() (tag string, value interface{}, err error) {
func (b Build) MarshalYAML() (interface{}, error) {
m := map[string]interface{}{}
if b.Context != "" {
m["context"] = b.Context
@@ -26,16 +27,20 @@ func (b Build) MarshalYAML() (tag string, value interface{}, err error) {
if len(b.Args) > 0 {
m["args"] = b.Args
}
return "", m, nil
return m, nil
}
// UnmarshalYAML implements the Unmarshaller interface.
func (b *Build) UnmarshalYAML(tag string, value interface{}) error {
switch v := value.(type) {
case string:
b.Context = v
case map[interface{}]interface{}:
for mapKey, mapValue := range v {
func (b *Build) UnmarshalYAML(unmarshal func(interface{}) error) error {
var stringType string
if err := unmarshal(&stringType); err == nil {
b.Context = stringType
return nil
}
var mapType map[interface{}]interface{}
if err := unmarshal(&mapType); err == nil {
for mapKey, mapValue := range mapType {
switch mapKey {
case "context":
b.Context = mapValue.(string)
@@ -52,10 +57,10 @@ func (b *Build) UnmarshalYAML(tag string, value interface{}) error {
continue
}
}
default:
return fmt.Errorf("Failed to unmarshal Build: %#v", value)
return nil
}
return nil
return errors.New("Failed to unmarshal Build")
}
func handleBuildArgs(value interface{}) (map[string]string, error) {
@@ -98,6 +103,8 @@ func handleBuildArgMap(m map[interface{}]interface{}) (map[string]string, error)
switch a := mapValue.(type) {
case string:
argValue = a
case int:
argValue = strconv.Itoa(a)
case int64:
argValue = strconv.Itoa(int(a))
default:
+23 -13
View File
@@ -1,6 +1,7 @@
package yaml
import (
"errors"
"fmt"
"github.com/docker/engine-api/types/strslice"
@@ -11,22 +12,31 @@ import (
type Command strslice.StrSlice
// UnmarshalYAML implements the Unmarshaller interface.
func (s *Command) UnmarshalYAML(tag string, value interface{}) error {
switch value := value.(type) {
case []interface{}:
parts, err := toStrings(value)
func (s *Command) UnmarshalYAML(unmarshal func(interface{}) error) error {
var stringType string
if err := unmarshal(&stringType); err == nil {
parts, err := shlex.Split(stringType)
if err != nil {
return err
}
*s = parts
case string:
parts, err := shlex.Split(value)
if err != nil {
return err
}
*s = parts
default:
return fmt.Errorf("Failed to unmarshal Command: %#v", value)
return nil
}
return nil
var sliceType []interface{}
if err := unmarshal(&sliceType); err == nil {
parts, err := toStrings(sliceType)
if err != nil {
return err
}
*s = parts
return nil
}
var interfaceType interface{}
if err := unmarshal(&interfaceType); err == nil {
fmt.Println(interfaceType)
}
return errors.New("Failed to unmarshal Command")
}
+17 -24
View File
@@ -1,9 +1,5 @@
package yaml
import (
"fmt"
)
// External represents an external network entry in compose file.
// It can be a boolean (true|false) or have a name
type External struct {
@@ -12,33 +8,30 @@ type External struct {
}
// MarshalYAML implements the Marshaller interface.
func (n External) MarshalYAML() (tag string, value interface{}, err error) {
func (n External) MarshalYAML() (interface{}, error) {
if n.Name == "" {
return "", n.External, nil
return n.External, nil
}
return "", map[string]interface{}{
return map[string]interface{}{
"name": n.Name,
}, nil
}
// UnmarshalYAML implements the Unmarshaller interface.
func (n *External) UnmarshalYAML(tag string, value interface{}) error {
switch v := value.(type) {
case bool:
n.External = v
case map[interface{}]interface{}:
for mapKey, mapValue := range v {
switch mapKey {
case "name":
n.Name = mapValue.(string)
default:
// Ignore unknown keys
continue
}
}
n.External = true
default:
return fmt.Errorf("Failed to unmarshal External: %#v", value)
func (n *External) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&n.External); err == nil {
return nil
}
var dummyExternal struct {
Name string
}
err := unmarshal(&dummyExternal)
if err != nil {
return err
}
n.Name = dummyExternal.Name
n.External = true
return nil
}
+16 -11
View File
@@ -1,6 +1,7 @@
package yaml
import (
"errors"
"fmt"
)
@@ -20,20 +21,20 @@ type Network struct {
}
// MarshalYAML implements the Marshaller interface.
func (n Networks) MarshalYAML() (tag string, value interface{}, err error) {
func (n Networks) MarshalYAML() (interface{}, error) {
m := map[string]*Network{}
for _, network := range n.Networks {
m[network.Name] = network
}
return "", m, nil
return m, nil
}
// UnmarshalYAML implements the Unmarshaller interface.
func (n *Networks) UnmarshalYAML(tag string, value interface{}) error {
switch v := value.(type) {
case []interface{}:
func (n *Networks) UnmarshalYAML(unmarshal func(interface{}) error) error {
var sliceType []interface{}
if err := unmarshal(&sliceType); err == nil {
n.Networks = []*Network{}
for _, network := range v {
for _, network := range sliceType {
name, ok := network.(string)
if !ok {
return fmt.Errorf("Cannot unmarshal '%v' to type %T into a string value", name, name)
@@ -42,9 +43,13 @@ func (n *Networks) UnmarshalYAML(tag string, value interface{}) error {
Name: name,
})
}
case map[interface{}]interface{}:
return nil
}
var mapType map[interface{}]interface{}
if err := unmarshal(&mapType); err == nil {
n.Networks = []*Network{}
for mapKey, mapValue := range v {
for mapKey, mapValue := range mapType {
name, ok := mapKey.(string)
if !ok {
return fmt.Errorf("Cannot unmarshal '%v' to type %T into a string value", name, name)
@@ -55,10 +60,10 @@ func (n *Networks) UnmarshalYAML(tag string, value interface{}) error {
}
n.Networks = append(n.Networks, network)
}
default:
return fmt.Errorf("Failed to unmarshal Networks: %#v", value)
return nil
}
return nil
return errors.New("Failed to unmarshal Networks")
}
func handleNetwork(name string, value interface{}) (*Network, error) {
+83 -46
View File
@@ -1,6 +1,7 @@
package yaml
import (
"errors"
"fmt"
"strconv"
"strings"
@@ -8,50 +9,65 @@ import (
"github.com/docker/engine-api/types/strslice"
)
// Stringorslice represents a string or an array of strings.
// Using engine-api Strslice and augment it with YAML marshalling stuff.
// StringorInt represents a string or an integer.
type StringorInt int64
// UnmarshalYAML implements the Unmarshaller interface.
func (s *StringorInt) UnmarshalYAML(unmarshal func(interface{}) error) error {
var intType int64
if err := unmarshal(&intType); err == nil {
*s = StringorInt(intType)
return nil
}
var stringType string
if err := unmarshal(&stringType); err == nil {
intType, err := strconv.ParseInt(stringType, 10, 64)
if err != nil {
return err
}
*s = StringorInt(intType)
return nil
}
return errors.New("Failed to unmarshal StringorInt")
}
// Stringorslice represents
// Using engine-api Strslice and augment it with YAML marshalling stuff. a string or an array of strings.
type Stringorslice strslice.StrSlice
// UnmarshalYAML implements the Unmarshaller interface.
func (s *Stringorslice) UnmarshalYAML(tag string, value interface{}) error {
switch value := value.(type) {
case []interface{}:
parts, err := toStrings(value)
func (s *Stringorslice) UnmarshalYAML(unmarshal func(interface{}) error) error {
var stringType string
if err := unmarshal(&stringType); err == nil {
*s = []string{stringType}
return nil
}
var sliceType []interface{}
if err := unmarshal(&sliceType); err == nil {
parts, err := toStrings(sliceType)
if err != nil {
return err
}
*s = parts
case string:
*s = []string{value}
default:
return fmt.Errorf("Failed to unmarshal Stringorslice: %#v", value)
return nil
}
return nil
return errors.New("Failed to unmarshal Stringorslice")
}
// SliceorMap represents a slice or a map of strings.
type SliceorMap map[string]string
// UnmarshalYAML implements the Unmarshaller interface.
func (s *SliceorMap) UnmarshalYAML(tag string, value interface{}) error {
switch value := value.(type) {
case map[interface{}]interface{}:
func (s *SliceorMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
var sliceType []interface{}
if err := unmarshal(&sliceType); err == nil {
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
case []interface{}:
parts := map[string]string{}
for _, s := range value {
for _, s := range sliceType {
if str, ok := s.(string); ok {
str := strings.TrimSpace(str)
keyValueSlice := strings.SplitN(str, "=", 2)
@@ -67,10 +83,28 @@ func (s *SliceorMap) UnmarshalYAML(tag string, value interface{}) error {
}
}
*s = parts
default:
return fmt.Errorf("Failed to unmarshal SliceorMap: %#v", value)
return nil
}
return nil
var mapType map[interface{}]interface{}
if err := unmarshal(&mapType); err == nil {
parts := map[string]string{}
for k, v := range mapType {
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
return nil
}
return errors.New("Failed to unmarshal SliceorMap")
}
// MaporEqualSlice represents a slice of strings that gets unmarshal from a
@@ -78,8 +112,8 @@ func (s *SliceorMap) UnmarshalYAML(tag string, value interface{}) error {
type MaporEqualSlice []string
// UnmarshalYAML implements the Unmarshaller interface.
func (s *MaporEqualSlice) UnmarshalYAML(tag string, value interface{}) error {
parts, err := unmarshalToStringOrSepMapParts(value, "=")
func (s *MaporEqualSlice) UnmarshalYAML(unmarshal func(interface{}) error) error {
parts, err := unmarshalToStringOrSepMapParts(unmarshal, "=")
if err != nil {
return err
}
@@ -97,8 +131,8 @@ func (s *MaporEqualSlice) ToMap() map[string]string {
type MaporColonSlice []string
// UnmarshalYAML implements the Unmarshaller interface.
func (s *MaporColonSlice) UnmarshalYAML(tag string, value interface{}) error {
parts, err := unmarshalToStringOrSepMapParts(value, ":")
func (s *MaporColonSlice) UnmarshalYAML(unmarshal func(interface{}) error) error {
parts, err := unmarshalToStringOrSepMapParts(unmarshal, ":")
if err != nil {
return err
}
@@ -116,8 +150,8 @@ func (s *MaporColonSlice) ToMap() map[string]string {
type MaporSpaceSlice []string
// UnmarshalYAML implements the Unmarshaller interface.
func (s *MaporSpaceSlice) UnmarshalYAML(tag string, value interface{}) error {
parts, err := unmarshalToStringOrSepMapParts(value, " ")
func (s *MaporSpaceSlice) UnmarshalYAML(unmarshal func(interface{}) error) error {
parts, err := unmarshalToStringOrSepMapParts(unmarshal, " ")
if err != nil {
return err
}
@@ -130,15 +164,16 @@ func (s *MaporSpaceSlice) ToMap() map[string]string {
return toMap(*s, " ")
}
func unmarshalToStringOrSepMapParts(value interface{}, key string) ([]string, error) {
switch value := value.(type) {
case []interface{}:
return toStrings(value)
case map[interface{}]interface{}:
return toSepMapParts(value, key)
default:
return nil, fmt.Errorf("Failed to unmarshal Map or Slice: %#v", value)
func unmarshalToStringOrSepMapParts(unmarshal func(interface{}) error, key string) ([]string, error) {
var sliceType []interface{}
if err := unmarshal(&sliceType); err == nil {
return toStrings(sliceType)
}
var mapType map[interface{}]interface{}
if err := unmarshal(&mapType); err == nil {
return toSepMapParts(mapType, key)
}
return nil, errors.New("Failed to unmarshal MaporSlice")
}
func toSepMapParts(value map[interface{}]interface{}, sep string) ([]string, error) {
@@ -150,6 +185,8 @@ func toSepMapParts(value map[interface{}]interface{}, sep string) ([]string, err
if sk, ok := k.(string); ok {
if sv, ok := v.(string); ok {
parts = append(parts, sk+sep+sv)
} else if sv, ok := v.(int); ok {
parts = append(parts, sk+sep+strconv.Itoa(sv))
} else if sv, ok := v.(int64); ok {
parts = append(parts, sk+sep+strconv.FormatInt(sv, 10))
} else if v == nil {
+19 -17
View File
@@ -1,6 +1,7 @@
package yaml
import (
"errors"
"fmt"
"sort"
)
@@ -12,29 +13,30 @@ type Ulimits struct {
}
// MarshalYAML implements the Marshaller interface.
func (u Ulimits) MarshalYAML() (tag string, value interface{}, err error) {
func (u Ulimits) MarshalYAML() (interface{}, error) {
ulimitMap := make(map[string]Ulimit)
for _, ulimit := range u.Elements {
ulimitMap[ulimit.Name] = ulimit
}
return "", ulimitMap, nil
return ulimitMap, nil
}
// UnmarshalYAML implements the Unmarshaller interface.
func (u *Ulimits) UnmarshalYAML(tag string, value interface{}) error {
func (u *Ulimits) UnmarshalYAML(unmarshal func(interface{}) error) error {
ulimits := make(map[string]Ulimit)
switch v := value.(type) {
case map[interface{}]interface{}:
for mapKey, mapValue := range v {
var mapType map[interface{}]interface{}
if err := unmarshal(&mapType); err == nil {
for mapKey, mapValue := range mapType {
name, ok := mapKey.(string)
if !ok {
return fmt.Errorf("Cannot unmarshal '%v' to type %T into a string value", name, name)
}
var soft, hard int64
switch mv := mapValue.(type) {
case int64:
soft = mv
hard = mv
case int:
soft = int64(mv)
hard = int64(mv)
case map[interface{}]interface{}:
if len(mv) != 2 {
return fmt.Errorf("Failed to unmarshal Ulimit: %#v", mapValue)
@@ -42,9 +44,9 @@ func (u *Ulimits) UnmarshalYAML(tag string, value interface{}) error {
for mkey, mvalue := range mv {
switch mkey {
case "soft":
soft = mvalue.(int64)
soft = int64(mvalue.(int))
case "hard":
hard = mvalue.(int64)
hard = int64(mvalue.(int))
default:
// FIXME(vdemeester) Should we ignore or fail ?
continue
@@ -69,10 +71,10 @@ func (u *Ulimits) UnmarshalYAML(tag string, value interface{}) error {
for _, key := range keys {
u.Elements = append(u.Elements, ulimits[key])
}
default:
return fmt.Errorf("Failed to unmarshal Ulimit: %#v", value)
return nil
}
return nil
return errors.New("Failed to unmarshal Ulimit")
}
// Ulimit represents ulimit information.
@@ -87,11 +89,11 @@ type ulimitValues struct {
}
// MarshalYAML implements the Marshaller interface.
func (u Ulimit) MarshalYAML() (tag string, value interface{}, err error) {
func (u Ulimit) MarshalYAML() (interface{}, error) {
if u.Soft == u.Hard {
return "", u.Soft, nil
return u.Soft, nil
}
return "", u.ulimitValues, err
return u.ulimitValues, nil
}
// NewUlimit creates a Ulimit based on the specified parts.
+83
View File
@@ -0,0 +1,83 @@
package yaml
import (
"errors"
"fmt"
"strings"
)
// Volumes represents a list of service volumes in compose file.
// It has several representation, hence this specific struct.
type Volumes struct {
Volumes []*Volume
}
// Volume represent a service volume
type Volume struct {
Source string `yaml:"-"`
Destination string `yaml:"-"`
AccessMode string `yaml:"-"`
}
// String implements the Stringer interface.
func (v *Volume) String() string {
var paths []string
if v.Source != "" {
paths = []string{v.Source, v.Destination}
} else {
paths = []string{v.Destination}
}
if v.AccessMode != "" {
paths = append(paths, v.AccessMode)
}
return strings.Join(paths, ":")
}
// MarshalYAML implements the Marshaller interface.
func (v Volumes) MarshalYAML() (interface{}, error) {
vs := []string{}
for _, volume := range v.Volumes {
vs = append(vs, volume.String())
}
return vs, nil
}
// UnmarshalYAML implements the Unmarshaller interface.
func (v *Volumes) UnmarshalYAML(unmarshal func(interface{}) error) error {
var sliceType []interface{}
if err := unmarshal(&sliceType); err == nil {
v.Volumes = []*Volume{}
for _, volume := range sliceType {
name, ok := volume.(string)
if !ok {
return fmt.Errorf("Cannot unmarshal '%v' to type %T into a string value", name, name)
}
elts := strings.SplitN(name, ":", 3)
var vol *Volume
switch {
case len(elts) == 1:
vol = &Volume{
Destination: elts[0],
}
case len(elts) == 2:
vol = &Volume{
Source: elts[0],
Destination: elts[1],
}
case len(elts) == 3:
vol = &Volume{
Source: elts[0],
Destination: elts[1],
AccessMode: elts[2],
}
default:
// FIXME
return fmt.Errorf("")
}
v.Volumes = append(v.Volumes, vol)
}
return nil
}
return errors.New("Failed to unmarshal Volumes")
}