|  | @@ -50,6 +50,11 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	prepareNetworks(project)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	err = prepareVolumes(project)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	if err := s.ensureNetworks(ctx, project.Networks); err != nil {
 | 
	
		
			
				|  |  |  		return err
 | 
	
		
			
				|  |  |  	}
 | 
	
	
		
			
				|  | @@ -91,6 +96,29 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
 | 
	
		
			
				|  |  |  	})
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +func prepareVolumes(p *types.Project) error {
 | 
	
		
			
				|  |  | +	for i := range p.Services {
 | 
	
		
			
				|  |  | +		volumesFrom, dependServices, err := getVolumesFrom(p, p.Services[i].VolumesFrom)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		p.Services[i].VolumesFrom = volumesFrom
 | 
	
		
			
				|  |  | +		if len(dependServices) > 0 {
 | 
	
		
			
				|  |  | +			if p.Services[i].DependsOn == nil {
 | 
	
		
			
				|  |  | +				p.Services[i].DependsOn = make(types.DependsOnConfig, len(dependServices))
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			for _, service := range p.Services {
 | 
	
		
			
				|  |  | +				if contains(dependServices, service.Name) {
 | 
	
		
			
				|  |  | +					p.Services[i].DependsOn[service.Name] = types.ServiceDependency{
 | 
	
		
			
				|  |  | +						Condition: types.ServiceConditionStarted,
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  func prepareNetworks(project *types.Project) {
 | 
	
		
			
				|  |  |  	for k, network := range project.Networks {
 | 
	
		
			
				|  |  |  		network.Labels = network.Labels.Add(networkLabel, k)
 | 
	
	
		
			
				|  | @@ -134,7 +162,7 @@ func getImageName(service types.ServiceConfig, projectName string) string {
 | 
	
		
			
				|  |  |  func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig, number int, inherit *moby.Container,
 | 
	
		
			
				|  |  |  	autoRemove bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	hash, err := jsonHash(s)
 | 
	
		
			
				|  |  | +	hash, err := jsonHash(service)
 | 
	
		
			
				|  |  |  	if err != nil {
 | 
	
		
			
				|  |  |  		return nil, nil, nil, err
 | 
	
		
			
				|  |  |  	}
 | 
	
	
		
			
				|  | @@ -171,25 +199,11 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 | 
	
		
			
				|  |  |  		stdinOpen   = service.StdinOpen
 | 
	
		
			
				|  |  |  		attachStdin = false
 | 
	
		
			
				|  |  |  	)
 | 
	
		
			
				|  |  | -	image := getImageName(service, p.Name)
 | 
	
		
			
				|  |  | -	imgInspect, _, err := s.apiClient.ImageInspectWithRaw(ctx, image)
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		return nil, nil, nil, err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	mountOptions, err := buildContainerMountOptions(*p, service, imgInspect, inherit)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	volumeMounts, binds, mounts, err := s.buildContainerVolumes(ctx, *p, service, inherit)
 | 
	
		
			
				|  |  |  	if err != nil {
 | 
	
		
			
				|  |  |  		return nil, nil, nil, err
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	volumeMounts := map[string]struct{}{}
 | 
	
		
			
				|  |  | -	binds := []string{}
 | 
	
		
			
				|  |  | -	for _, m := range mountOptions {
 | 
	
		
			
				|  |  | -		if m.Type == mount.TypeVolume {
 | 
	
		
			
				|  |  | -			volumeMounts[m.Target] = struct{}{}
 | 
	
		
			
				|  |  | -			if m.Source != "" {
 | 
	
		
			
				|  |  | -				binds = append(binds, fmt.Sprintf("%s:%s:%s", m.Source, m.Target, "rw"))
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	containerConfig := container.Config{
 | 
	
		
			
				|  |  |  		Hostname:        service.Hostname,
 | 
	
	
		
			
				|  | @@ -203,7 +217,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 | 
	
		
			
				|  |  |  		AttachStderr:    true,
 | 
	
		
			
				|  |  |  		AttachStdout:    true,
 | 
	
		
			
				|  |  |  		Cmd:             runCmd,
 | 
	
		
			
				|  |  | -		Image:           image,
 | 
	
		
			
				|  |  | +		Image:           getImageName(service, p.Name),
 | 
	
		
			
				|  |  |  		WorkingDir:      service.WorkingDir,
 | 
	
		
			
				|  |  |  		Entrypoint:      entrypoint,
 | 
	
		
			
				|  |  |  		NetworkDisabled: service.NetworkMode == "disabled",
 | 
	
	
		
			
				|  | @@ -212,21 +226,11 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 | 
	
		
			
				|  |  |  		StopSignal:      service.StopSignal,
 | 
	
		
			
				|  |  |  		Env:             convert.ToMobyEnv(service.Environment),
 | 
	
		
			
				|  |  |  		Healthcheck:     convert.ToMobyHealthCheck(service.HealthCheck),
 | 
	
		
			
				|  |  | -		// Volumes:         // FIXME unclear to me the overlap with HostConfig.Mounts
 | 
	
		
			
				|  |  | -		Volumes:     volumeMounts,
 | 
	
		
			
				|  |  | +		Volumes:         volumeMounts,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		StopTimeout: convert.ToSeconds(service.StopGracePeriod),
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	// append secrets mounts
 | 
	
		
			
				|  |  | -	bindMounts, err := buildContainerSecretMounts(*p, service)
 | 
	
		
			
				|  |  | -	if err != nil {
 | 
	
		
			
				|  |  | -		return nil, nil, nil, err
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	for _, m := range mountOptions {
 | 
	
		
			
				|  |  | -		if m.Type == mount.TypeBind || m.Type == mount.TypeTmpfs {
 | 
	
		
			
				|  |  | -			bindMounts = append(bindMounts, m)
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  |  	portBindings := buildContainerPortBindingOptions(service)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	resources := getDeployResources(service)
 | 
	
	
		
			
				|  | @@ -234,7 +238,7 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 | 
	
		
			
				|  |  |  	hostConfig := container.HostConfig{
 | 
	
		
			
				|  |  |  		AutoRemove:     autoRemove,
 | 
	
		
			
				|  |  |  		Binds:          binds,
 | 
	
		
			
				|  |  | -		Mounts:         bindMounts,
 | 
	
		
			
				|  |  | +		Mounts:         mounts,
 | 
	
		
			
				|  |  |  		CapAdd:         strslice.StrSlice(service.CapAdd),
 | 
	
		
			
				|  |  |  		CapDrop:        strslice.StrSlice(service.CapDrop),
 | 
	
		
			
				|  |  |  		NetworkMode:    networkMode,
 | 
	
	
		
			
				|  | @@ -244,6 +248,8 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
 | 
	
		
			
				|  |  |  		Sysctls:      service.Sysctls,
 | 
	
		
			
				|  |  |  		PortBindings: portBindings,
 | 
	
		
			
				|  |  |  		Resources:    resources,
 | 
	
		
			
				|  |  | +		VolumeDriver: service.VolumeDriver,
 | 
	
		
			
				|  |  | +		VolumesFrom:  service.VolumesFrom,
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	networkConfig := buildDefaultNetworkConfig(service, networkMode, getContainerName(p.Name, service, number))
 | 
	
	
		
			
				|  | @@ -297,6 +303,69 @@ func buildContainerPortBindingOptions(s types.ServiceConfig) nat.PortMap {
 | 
	
		
			
				|  |  |  	return bindings
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +func getVolumesFrom(project *types.Project, volumesFrom []string) ([]string, []string, error) {
 | 
	
		
			
				|  |  | +	var volumes = []string{}
 | 
	
		
			
				|  |  | +	var services = []string{}
 | 
	
		
			
				|  |  | +	// parse volumes_from
 | 
	
		
			
				|  |  | +	if len(volumesFrom) == 0 {
 | 
	
		
			
				|  |  | +		return volumes, services, nil
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	for _, vol := range volumesFrom {
 | 
	
		
			
				|  |  | +		spec := strings.Split(vol, ":")
 | 
	
		
			
				|  |  | +		if spec[0] == "container" {
 | 
	
		
			
				|  |  | +			volumes = append(volumes, strings.Join(spec[1:], ":"))
 | 
	
		
			
				|  |  | +			continue
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		serviceName := spec[0]
 | 
	
		
			
				|  |  | +		services = append(services, serviceName)
 | 
	
		
			
				|  |  | +		service, err := project.GetService(serviceName)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return nil, nil, err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		firstContainer := getContainerName(project.Name, service, 1)
 | 
	
		
			
				|  |  | +		v := fmt.Sprintf("%s:%s", firstContainer, strings.Join(spec[1:], ":"))
 | 
	
		
			
				|  |  | +		volumes = append(volumes, v)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return volumes, services, nil
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig,
 | 
	
		
			
				|  |  | +	inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) {
 | 
	
		
			
				|  |  | +	var mounts = []mount.Mount{}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	image := getImageName(service, p.Name)
 | 
	
		
			
				|  |  | +	imgInspect, _, err := s.apiClient.ImageInspectWithRaw(ctx, image)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil, nil, nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	mountOptions, err := buildContainerMountOptions(p, service, imgInspect, inherit)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil, nil, nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// filter binds and volumes mount targets
 | 
	
		
			
				|  |  | +	volumeMounts := map[string]struct{}{}
 | 
	
		
			
				|  |  | +	binds := []string{}
 | 
	
		
			
				|  |  | +	for _, m := range mountOptions {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if m.Type == mount.TypeVolume {
 | 
	
		
			
				|  |  | +			volumeMounts[m.Target] = struct{}{}
 | 
	
		
			
				|  |  | +			if m.Source != "" {
 | 
	
		
			
				|  |  | +				binds = append(binds, fmt.Sprintf("%s:%s:rw", m.Source, m.Target))
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	for _, m := range mountOptions {
 | 
	
		
			
				|  |  | +		if m.Type == mount.TypeBind || m.Type == mount.TypeTmpfs {
 | 
	
		
			
				|  |  | +			mounts = append(mounts, m)
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return volumeMounts, binds, mounts, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
 | 
	
		
			
				|  |  |  	var mounts = map[string]mount.Mount{}
 | 
	
		
			
				|  |  |  	if inherit != nil {
 | 
	
	
		
			
				|  | @@ -318,7 +387,6 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	if img.ContainerConfig != nil {
 | 
	
		
			
				|  |  |  		for k := range img.ContainerConfig.Volumes {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  			mount, err := buildMount(p, types.ServiceVolumeConfig{
 | 
	
		
			
				|  |  |  				Type:   types.VolumeTypeVolume,
 | 
	
		
			
				|  |  |  				Target: k,
 | 
	
	
		
			
				|  | @@ -327,6 +395,7 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby
 | 
	
		
			
				|  |  |  				return nil, err
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			mounts[k] = mount
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	for _, v := range s.Volumes {
 | 
	
	
		
			
				|  | @@ -337,6 +406,17 @@ func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby
 | 
	
		
			
				|  |  |  		mounts[mount.Target] = mount
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	secrets, err := buildContainerSecretMounts(p, s)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil, err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	for _, s := range secrets {
 | 
	
		
			
				|  |  | +		if _, found := mounts[s.Target]; found {
 | 
	
		
			
				|  |  | +			continue
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		mounts[s.Target] = s
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	values := make([]mount.Mount, 0, len(mounts))
 | 
	
		
			
				|  |  |  	for _, v := range mounts {
 | 
	
		
			
				|  |  |  		values = append(values, v)
 | 
	
	
		
			
				|  | @@ -362,9 +442,10 @@ func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		mount, err := buildMount(p, types.ServiceVolumeConfig{
 | 
	
		
			
				|  |  | -			Type:   types.VolumeTypeBind,
 | 
	
		
			
				|  |  | -			Source: definedSecret.File,
 | 
	
		
			
				|  |  | -			Target: target,
 | 
	
		
			
				|  |  | +			Type:     types.VolumeTypeBind,
 | 
	
		
			
				|  |  | +			Source:   definedSecret.File,
 | 
	
		
			
				|  |  | +			Target:   target,
 | 
	
		
			
				|  |  | +			ReadOnly: true,
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
		
			
				|  |  |  			return nil, err
 |