forked from LaconicNetwork/kompose
bump(github.com/docker/libcompose) v0.3.0
This commit is contained in:
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user