|
@@ -21,7 +21,7 @@ import (
|
|
|
"context"
|
|
"context"
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
- "io/ioutil"
|
|
|
|
|
|
|
+ "os"
|
|
|
"path"
|
|
"path"
|
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
"strconv"
|
|
"strconv"
|
|
@@ -31,6 +31,7 @@ import (
|
|
|
moby "github.com/docker/docker/api/types"
|
|
moby "github.com/docker/docker/api/types"
|
|
|
"github.com/docker/docker/api/types/blkiodev"
|
|
"github.com/docker/docker/api/types/blkiodev"
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
|
+ "github.com/docker/docker/api/types/filters"
|
|
|
"github.com/docker/docker/api/types/mount"
|
|
"github.com/docker/docker/api/types/mount"
|
|
|
"github.com/docker/docker/api/types/network"
|
|
"github.com/docker/docker/api/types/network"
|
|
|
"github.com/docker/docker/api/types/strslice"
|
|
"github.com/docker/docker/api/types/strslice"
|
|
@@ -173,13 +174,21 @@ func prepareServicesDependsOn(p *types.Project) error {
|
|
|
dependencies = append(dependencies, spec[0])
|
|
dependencies = append(dependencies, spec[0])
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ for _, link := range service.Links {
|
|
|
|
|
+ dependencies = append(dependencies, strings.Split(link, ":")[0])
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if len(dependencies) == 0 {
|
|
if len(dependencies) == 0 {
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
if service.DependsOn == nil {
|
|
if service.DependsOn == nil {
|
|
|
service.DependsOn = make(types.DependsOnConfig)
|
|
service.DependsOn = make(types.DependsOnConfig)
|
|
|
}
|
|
}
|
|
|
- deps, err := p.GetServices(dependencies...)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Verify dependencies exist in the project, whether disabled or not
|
|
|
|
|
+ projAllServices := types.Project{}
|
|
|
|
|
+ projAllServices.Services = p.AllServices()
|
|
|
|
|
+ deps, err := projAllServices.GetServices(dependencies...)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
@@ -255,7 +264,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
|
|
|
return nil, nil, nil, err
|
|
return nil, nil, nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- proxyConfig := types.MappingWithEquals(s.configFile.ParseProxyConfig(s.apiClient.DaemonHost(), nil))
|
|
|
|
|
|
|
+ proxyConfig := types.MappingWithEquals(s.configFile().ParseProxyConfig(s.apiClient().DaemonHost(), nil))
|
|
|
env := proxyConfig.OverrideBy(service.Environment)
|
|
env := proxyConfig.OverrideBy(service.Environment)
|
|
|
|
|
|
|
|
containerConfig := container.Config{
|
|
containerConfig := container.Config{
|
|
@@ -347,6 +356,11 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
|
|
|
volumesFrom = append(volumesFrom, v[len("container:"):])
|
|
volumesFrom = append(volumesFrom, v[len("container:"):])
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ links, err := s.getLinks(ctx, p.Name, service, number)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, nil, nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
securityOpts, err := parseSecurityOpts(p, service.SecurityOpt)
|
|
securityOpts, err := parseSecurityOpts(p, service.SecurityOpt)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, nil, nil, err
|
|
return nil, nil, nil, err
|
|
@@ -371,7 +385,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
|
|
|
DNS: service.DNS,
|
|
DNS: service.DNS,
|
|
|
DNSSearch: service.DNSSearch,
|
|
DNSSearch: service.DNSSearch,
|
|
|
DNSOptions: service.DNSOpts,
|
|
DNSOptions: service.DNSOpts,
|
|
|
- ExtraHosts: service.ExtraHosts,
|
|
|
|
|
|
|
+ ExtraHosts: service.ExtraHosts.AsList(),
|
|
|
SecurityOpt: securityOpts,
|
|
SecurityOpt: securityOpts,
|
|
|
UsernsMode: container.UsernsMode(service.UserNSMode),
|
|
UsernsMode: container.UsernsMode(service.UserNSMode),
|
|
|
Privileged: service.Privileged,
|
|
Privileged: service.Privileged,
|
|
@@ -381,6 +395,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
|
|
|
Runtime: service.Runtime,
|
|
Runtime: service.Runtime,
|
|
|
LogConfig: logConfig,
|
|
LogConfig: logConfig,
|
|
|
GroupAdd: service.GroupAdd,
|
|
GroupAdd: service.GroupAdd,
|
|
|
|
|
+ Links: links,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return &containerConfig, &hostConfig, networkConfig, nil
|
|
return &containerConfig, &hostConfig, networkConfig, nil
|
|
@@ -399,7 +414,7 @@ func parseSecurityOpts(p *types.Project, securityOpts []string) ([]string, error
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if con[0] == "seccomp" && con[1] != "unconfined" {
|
|
if con[0] == "seccomp" && con[1] != "unconfined" {
|
|
|
- f, err := ioutil.ReadFile(p.RelativePath(con[1]))
|
|
|
|
|
|
|
+ f, err := os.ReadFile(p.RelativePath(con[1]))
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
|
|
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
|
|
|
}
|
|
}
|
|
@@ -500,6 +515,7 @@ func getDeployResources(s types.ServiceConfig) container.Resources {
|
|
|
CPUShares: s.CPUShares,
|
|
CPUShares: s.CPUShares,
|
|
|
CPUPercent: int64(s.CPUS * 100),
|
|
CPUPercent: int64(s.CPUS * 100),
|
|
|
CpusetCpus: s.CPUSet,
|
|
CpusetCpus: s.CPUSet,
|
|
|
|
|
+ DeviceCgroupRules: s.DeviceCgroupRules,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if s.PidsLimit != 0 {
|
|
if s.PidsLimit != 0 {
|
|
@@ -579,8 +595,12 @@ func setLimits(limits *types.Resource, resources *container.Resources) {
|
|
|
resources.Memory = int64(limits.MemoryBytes)
|
|
resources.Memory = int64(limits.MemoryBytes)
|
|
|
}
|
|
}
|
|
|
if limits.NanoCPUs != "" {
|
|
if limits.NanoCPUs != "" {
|
|
|
- i, _ := strconv.ParseInt(limits.NanoCPUs, 10, 64)
|
|
|
|
|
- resources.NanoCPUs = i
|
|
|
|
|
|
|
+ if f, err := strconv.ParseFloat(limits.NanoCPUs, 64); err == nil {
|
|
|
|
|
+ resources.NanoCPUs = int64(f * 1e9)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if limits.PIds > 0 {
|
|
|
|
|
+ resources.PidsLimit = &limits.PIds
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -693,7 +713,7 @@ func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Proj
|
|
|
var mounts = []mount.Mount{}
|
|
var mounts = []mount.Mount{}
|
|
|
|
|
|
|
|
image := getImageName(service, p.Name)
|
|
image := getImageName(service, p.Name)
|
|
|
- imgInspect, _, err := s.apiClient.ImageInspectWithRaw(ctx, image)
|
|
|
|
|
|
|
+ imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, nil, nil, err
|
|
return nil, nil, nil, err
|
|
|
}
|
|
}
|
|
@@ -708,12 +728,20 @@ func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Proj
|
|
|
MOUNTS:
|
|
MOUNTS:
|
|
|
for _, m := range mountOptions {
|
|
for _, m := range mountOptions {
|
|
|
volumeMounts[m.Target] = struct{}{}
|
|
volumeMounts[m.Target] = struct{}{}
|
|
|
- // `Bind` API is used when host path need to be created if missing, `Mount` is preferred otherwise
|
|
|
|
|
if m.Type == mount.TypeBind || m.Type == mount.TypeNamedPipe {
|
|
if m.Type == mount.TypeBind || m.Type == mount.TypeNamedPipe {
|
|
|
|
|
+ // `Mount` is preferred but does not offer option to created host path if missing
|
|
|
|
|
+ // so `Bind` API is used here with raw volume string
|
|
|
|
|
+ // see https://github.com/moby/moby/issues/43483
|
|
|
for _, v := range service.Volumes {
|
|
for _, v := range service.Volumes {
|
|
|
- if v.Target == m.Target && v.Bind != nil && v.Bind.CreateHostPath {
|
|
|
|
|
- binds = append(binds, fmt.Sprintf("%s:%s:%s", m.Source, m.Target, getBindMode(v.Bind, m.ReadOnly)))
|
|
|
|
|
- continue MOUNTS
|
|
|
|
|
|
|
+ if v.Target == m.Target {
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case string(m.Type) != v.Type:
|
|
|
|
|
+ v.Source = m.Source
|
|
|
|
|
+ fallthrough
|
|
|
|
|
+ case v.Bind != nil && v.Bind.CreateHostPath:
|
|
|
|
|
+ binds = append(binds, v.String())
|
|
|
|
|
+ continue MOUNTS
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -722,23 +750,6 @@ MOUNTS:
|
|
|
return volumeMounts, binds, mounts, nil
|
|
return volumeMounts, binds, mounts, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func getBindMode(bind *types.ServiceVolumeBind, readOnly bool) string {
|
|
|
|
|
- mode := "rw"
|
|
|
|
|
-
|
|
|
|
|
- if readOnly {
|
|
|
|
|
- mode = "ro"
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- switch bind.SELinux {
|
|
|
|
|
- case types.SELinuxShared:
|
|
|
|
|
- mode += ",z"
|
|
|
|
|
- case types.SELinuxPrivate:
|
|
|
|
|
- mode += ",Z"
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return mode
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
|
|
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
|
|
|
var mounts = map[string]mount.Mount{}
|
|
var mounts = map[string]mount.Mount{}
|
|
|
if inherit != nil {
|
|
if inherit != nil {
|
|
@@ -878,6 +889,10 @@ func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount
|
|
|
return nil, fmt.Errorf("unsupported external secret %s", definedSecret.Name)
|
|
return nil, fmt.Errorf("unsupported external secret %s", definedSecret.Name)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if definedSecret.Environment != "" {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
mount, err := buildMount(p, types.ServiceVolumeConfig{
|
|
mount, err := buildMount(p, types.ServiceVolumeConfig{
|
|
|
Type: types.VolumeTypeBind,
|
|
Type: types.VolumeTypeBind,
|
|
|
Source: definedSecret.File,
|
|
Source: definedSecret.File,
|
|
@@ -921,10 +936,14 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- bind, vol, tmpfs := buildMountOptions(volume)
|
|
|
|
|
|
|
+ bind, vol, tmpfs := buildMountOptions(project, volume)
|
|
|
|
|
|
|
|
volume.Target = path.Clean(volume.Target)
|
|
volume.Target = path.Clean(volume.Target)
|
|
|
|
|
|
|
|
|
|
+ if bind != nil {
|
|
|
|
|
+ volume.Type = types.VolumeTypeBind
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return mount.Mount{
|
|
return mount.Mount{
|
|
|
Type: mount.Type(volume.Type),
|
|
Type: mount.Type(volume.Type),
|
|
|
Source: source,
|
|
Source: source,
|
|
@@ -937,7 +956,7 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
|
|
|
}, nil
|
|
}, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) {
|
|
|
|
|
|
|
+func buildMountOptions(project types.Project, volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) {
|
|
|
switch volume.Type {
|
|
switch volume.Type {
|
|
|
case "bind":
|
|
case "bind":
|
|
|
if volume.Volume != nil {
|
|
if volume.Volume != nil {
|
|
@@ -954,6 +973,11 @@ func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *m
|
|
|
if volume.Tmpfs != nil {
|
|
if volume.Tmpfs != nil {
|
|
|
logrus.Warnf("mount of type `volume` should not define `tmpfs` option")
|
|
logrus.Warnf("mount of type `volume` should not define `tmpfs` option")
|
|
|
}
|
|
}
|
|
|
|
|
+ if v, ok := project.Volumes[volume.Source]; ok && v.DriverOpts["o"] == types.VolumeTypeBind {
|
|
|
|
|
+ return buildBindOption(&types.ServiceVolumeBind{
|
|
|
|
|
+ CreateHostPath: true,
|
|
|
|
|
+ }), nil, nil
|
|
|
|
|
+ }
|
|
|
return nil, buildVolumeOptions(volume.Volume), nil
|
|
return nil, buildVolumeOptions(volume.Volume), nil
|
|
|
case "tmpfs":
|
|
case "tmpfs":
|
|
|
if volume.Bind != nil {
|
|
if volume.Bind != nil {
|
|
@@ -1007,92 +1031,88 @@ func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (s *composeService) ensureNetwork(ctx context.Context, n types.NetworkConfig) error {
|
|
func (s *composeService) ensureNetwork(ctx context.Context, n types.NetworkConfig) error {
|
|
|
- _, err := s.apiClient.NetworkInspect(ctx, n.Name, moby.NetworkInspectOptions{})
|
|
|
|
|
|
|
+ // NetworkInspect will match on ID prefix, so NetworkList with a name
|
|
|
|
|
+ // filter is used to look for an exact match to prevent e.g. a network
|
|
|
|
|
+ // named `db` from getting erroneously matched to a network with an ID
|
|
|
|
|
+ // like `db9086999caf`
|
|
|
|
|
+ networks, err := s.apiClient().NetworkList(ctx, moby.NetworkListOptions{
|
|
|
|
|
+ Filters: filters.NewArgs(filters.Arg("name", n.Name)),
|
|
|
|
|
+ })
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- if errdefs.IsNotFound(err) {
|
|
|
|
|
- if n.External.External {
|
|
|
|
|
- if n.Driver == "overlay" {
|
|
|
|
|
- // Swarm nodes do not register overlay networks that were
|
|
|
|
|
- // created on a different node unless they're in use.
|
|
|
|
|
- // Here we assume `driver` is relevant for a network we don't manage
|
|
|
|
|
- // which is a non-sense, but this is our legacy ¯\(ツ)/¯
|
|
|
|
|
- // networkAttach will later fail anyway if network actually doesn't exists
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
- return fmt.Errorf("network %s declared as external, but could not be found", n.Name)
|
|
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(networks) == 0 {
|
|
|
|
|
+ if n.External.External {
|
|
|
|
|
+ if n.Driver == "overlay" {
|
|
|
|
|
+ // Swarm nodes do not register overlay networks that were
|
|
|
|
|
+ // created on a different node unless they're in use.
|
|
|
|
|
+ // Here we assume `driver` is relevant for a network we don't manage
|
|
|
|
|
+ // which is a non-sense, but this is our legacy ¯\(ツ)/¯
|
|
|
|
|
+ // networkAttach will later fail anyway if network actually doesn't exists
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
- var ipam *network.IPAM
|
|
|
|
|
- if n.Ipam.Config != nil {
|
|
|
|
|
- var config []network.IPAMConfig
|
|
|
|
|
- for _, pool := range n.Ipam.Config {
|
|
|
|
|
- config = append(config, network.IPAMConfig{
|
|
|
|
|
- Subnet: pool.Subnet,
|
|
|
|
|
- IPRange: pool.IPRange,
|
|
|
|
|
- Gateway: pool.Gateway,
|
|
|
|
|
- AuxAddress: pool.AuxiliaryAddresses,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- ipam = &network.IPAM{
|
|
|
|
|
- Driver: n.Ipam.Driver,
|
|
|
|
|
- Config: config,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return fmt.Errorf("network %s declared as external, but could not be found", n.Name)
|
|
|
|
|
+ }
|
|
|
|
|
+ var ipam *network.IPAM
|
|
|
|
|
+ if n.Ipam.Config != nil {
|
|
|
|
|
+ var config []network.IPAMConfig
|
|
|
|
|
+ for _, pool := range n.Ipam.Config {
|
|
|
|
|
+ config = append(config, network.IPAMConfig{
|
|
|
|
|
+ Subnet: pool.Subnet,
|
|
|
|
|
+ IPRange: pool.IPRange,
|
|
|
|
|
+ Gateway: pool.Gateway,
|
|
|
|
|
+ AuxAddress: pool.AuxiliaryAddresses,
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
- createOpts := moby.NetworkCreate{
|
|
|
|
|
- // TODO NameSpace Labels
|
|
|
|
|
- Labels: n.Labels,
|
|
|
|
|
- Driver: n.Driver,
|
|
|
|
|
- Options: n.DriverOpts,
|
|
|
|
|
- Internal: n.Internal,
|
|
|
|
|
- Attachable: n.Attachable,
|
|
|
|
|
- IPAM: ipam,
|
|
|
|
|
- EnableIPv6: n.EnableIPv6,
|
|
|
|
|
|
|
+ ipam = &network.IPAM{
|
|
|
|
|
+ Driver: n.Ipam.Driver,
|
|
|
|
|
+ Config: config,
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
+ createOpts := moby.NetworkCreate{
|
|
|
|
|
+ CheckDuplicate: true,
|
|
|
|
|
+ // TODO NameSpace Labels
|
|
|
|
|
+ Labels: n.Labels,
|
|
|
|
|
+ Driver: n.Driver,
|
|
|
|
|
+ Options: n.DriverOpts,
|
|
|
|
|
+ Internal: n.Internal,
|
|
|
|
|
+ Attachable: n.Attachable,
|
|
|
|
|
+ IPAM: ipam,
|
|
|
|
|
+ EnableIPv6: n.EnableIPv6,
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
|
|
|
|
|
- createOpts.IPAM = &network.IPAM{}
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
|
|
|
|
|
+ createOpts.IPAM = &network.IPAM{}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if n.Ipam.Driver != "" {
|
|
|
|
|
- createOpts.IPAM.Driver = n.Ipam.Driver
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if n.Ipam.Driver != "" {
|
|
|
|
|
+ createOpts.IPAM.Driver = n.Ipam.Driver
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- for _, ipamConfig := range n.Ipam.Config {
|
|
|
|
|
- config := network.IPAMConfig{
|
|
|
|
|
- Subnet: ipamConfig.Subnet,
|
|
|
|
|
- }
|
|
|
|
|
- createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
|
|
|
|
|
|
|
+ for _, ipamConfig := range n.Ipam.Config {
|
|
|
|
|
+ config := network.IPAMConfig{
|
|
|
|
|
+ Subnet: ipamConfig.Subnet,
|
|
|
|
|
+ IPRange: ipamConfig.IPRange,
|
|
|
|
|
+ Gateway: ipamConfig.Gateway,
|
|
|
|
|
+ AuxAddress: ipamConfig.AuxiliaryAddresses,
|
|
|
}
|
|
}
|
|
|
- networkEventName := fmt.Sprintf("Network %s", n.Name)
|
|
|
|
|
- w := progress.ContextWriter(ctx)
|
|
|
|
|
- w.Event(progress.CreatingEvent(networkEventName))
|
|
|
|
|
- if _, err := s.apiClient.NetworkCreate(ctx, n.Name, createOpts); err != nil {
|
|
|
|
|
- w.Event(progress.ErrorEvent(networkEventName))
|
|
|
|
|
- return errors.Wrapf(err, "failed to create network %s", n.Name)
|
|
|
|
|
- }
|
|
|
|
|
- w.Event(progress.CreatedEvent(networkEventName))
|
|
|
|
|
- return nil
|
|
|
|
|
|
|
+ createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
|
|
|
}
|
|
}
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
- return nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (s *composeService) removeNetwork(ctx context.Context, networkID string, networkName string) error {
|
|
|
|
|
- w := progress.ContextWriter(ctx)
|
|
|
|
|
- eventName := fmt.Sprintf("Network %s", networkName)
|
|
|
|
|
- w.Event(progress.RemovingEvent(eventName))
|
|
|
|
|
-
|
|
|
|
|
- if err := s.apiClient.NetworkRemove(ctx, networkID); err != nil {
|
|
|
|
|
- w.Event(progress.ErrorEvent(eventName))
|
|
|
|
|
- return errors.Wrapf(err, fmt.Sprintf("failed to remove network %s", networkID))
|
|
|
|
|
|
|
+ networkEventName := fmt.Sprintf("Network %s", n.Name)
|
|
|
|
|
+ w := progress.ContextWriter(ctx)
|
|
|
|
|
+ w.Event(progress.CreatingEvent(networkEventName))
|
|
|
|
|
+ if _, err := s.apiClient().NetworkCreate(ctx, n.Name, createOpts); err != nil {
|
|
|
|
|
+ w.Event(progress.ErrorEvent(networkEventName))
|
|
|
|
|
+ return errors.Wrapf(err, "failed to create network %s", n.Name)
|
|
|
|
|
+ }
|
|
|
|
|
+ w.Event(progress.CreatedEvent(networkEventName))
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- w.Event(progress.RemovedEvent(eventName))
|
|
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project string) error {
|
|
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project string) error {
|
|
|
- inspected, err := s.apiClient.VolumeInspect(ctx, volume.Name)
|
|
|
|
|
|
|
+ inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
if !errdefs.IsNotFound(err) {
|
|
if !errdefs.IsNotFound(err) {
|
|
|
return err
|
|
return err
|
|
@@ -1123,7 +1143,7 @@ func (s *composeService) createVolume(ctx context.Context, volume types.VolumeCo
|
|
|
eventName := fmt.Sprintf("Volume %q", volume.Name)
|
|
eventName := fmt.Sprintf("Volume %q", volume.Name)
|
|
|
w := progress.ContextWriter(ctx)
|
|
w := progress.ContextWriter(ctx)
|
|
|
w.Event(progress.CreatingEvent(eventName))
|
|
w.Event(progress.CreatingEvent(eventName))
|
|
|
- _, err := s.apiClient.VolumeCreate(ctx, volume_api.VolumeCreateBody{
|
|
|
|
|
|
|
+ _, err := s.apiClient().VolumeCreate(ctx, volume_api.VolumeCreateBody{
|
|
|
Labels: volume.Labels,
|
|
Labels: volume.Labels,
|
|
|
Name: volume.Name,
|
|
Name: volume.Name,
|
|
|
Driver: volume.Driver,
|
|
Driver: volume.Driver,
|