github.com/bdwilliams/libcompose@v0.3.1-0.20160826154243-d81a9bdacff0/docker/service/service_create.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"golang.org/x/net/context"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/engine-api/types"
    12  	containertypes "github.com/docker/engine-api/types/container"
    13  	"github.com/docker/libcompose/config"
    14  	composecontainer "github.com/docker/libcompose/docker/container"
    15  	"github.com/docker/libcompose/labels"
    16  	"github.com/docker/libcompose/project"
    17  	"github.com/docker/libcompose/project/events"
    18  	util "github.com/docker/libcompose/utils"
    19  )
    20  
    21  func (s *Service) createContainer(ctx context.Context, namer Namer, oldContainer string, configOverride *config.ServiceConfig, oneOff bool) (*composecontainer.Container, error) {
    22  	serviceConfig := s.serviceConfig
    23  	if configOverride != nil {
    24  		serviceConfig.Command = configOverride.Command
    25  		serviceConfig.Tty = configOverride.Tty
    26  		serviceConfig.StdinOpen = configOverride.StdinOpen
    27  	}
    28  	configWrapper, err := ConvertToAPI(serviceConfig, s.context.Context, s.clientFactory)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	configWrapper.Config.Image = s.imageName()
    33  
    34  	containerName, containerNumber := namer.Next()
    35  
    36  	configWrapper.Config.Labels[labels.SERVICE.Str()] = s.name
    37  	configWrapper.Config.Labels[labels.PROJECT.Str()] = s.project.Name
    38  	configWrapper.Config.Labels[labels.HASH.Str()] = config.GetServiceHash(s.name, serviceConfig)
    39  	configWrapper.Config.Labels[labels.ONEOFF.Str()] = strings.Title(strconv.FormatBool(oneOff))
    40  	configWrapper.Config.Labels[labels.NUMBER.Str()] = fmt.Sprintf("%d", containerNumber)
    41  	configWrapper.Config.Labels[labels.VERSION.Str()] = project.ComposeVersion
    42  
    43  	err = s.populateAdditionalHostConfig(configWrapper.HostConfig)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	// FIXME(vdemeester): oldContainer should be a Container instead of a string
    49  	client := s.clientFactory.Create(s)
    50  	if oldContainer != "" {
    51  		info, err := client.ContainerInspect(ctx, oldContainer)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		configWrapper.HostConfig.Binds = util.Merge(configWrapper.HostConfig.Binds, volumeBinds(configWrapper.Config.Volumes, &info))
    56  	}
    57  
    58  	logrus.Debugf("Creating container %s %#v", containerName, configWrapper)
    59  	// FIXME(vdemeester): long-term will be container.Create(…)
    60  	container, err := composecontainer.Create(ctx, client, containerName, configWrapper.Config, configWrapper.HostConfig, configWrapper.NetworkingConfig)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	s.project.Notify(events.ContainerCreated, s.name, map[string]string{
    65  		"name": containerName,
    66  	})
    67  	return container, nil
    68  }
    69  
    70  func (s *Service) populateAdditionalHostConfig(hostConfig *containertypes.HostConfig) error {
    71  	links, err := s.getLinks()
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	for _, link := range s.DependentServices() {
    77  		if !s.project.ServiceConfigs.Has(link.Target) {
    78  			continue
    79  		}
    80  
    81  		service, err := s.project.CreateService(link.Target)
    82  		if err != nil {
    83  			return err
    84  		}
    85  
    86  		containers, err := service.Containers(context.Background())
    87  		if err != nil {
    88  			return err
    89  		}
    90  
    91  		if link.Type == project.RelTypeIpcNamespace {
    92  			hostConfig, err = addIpc(hostConfig, service, containers, s.serviceConfig.Ipc)
    93  		} else if link.Type == project.RelTypeNetNamespace {
    94  			hostConfig, err = addNetNs(hostConfig, service, containers, s.serviceConfig.NetworkMode)
    95  		}
    96  
    97  		if err != nil {
    98  			return err
    99  		}
   100  	}
   101  
   102  	hostConfig.Links = []string{}
   103  	for k, v := range links {
   104  		hostConfig.Links = append(hostConfig.Links, strings.Join([]string{v, k}, ":"))
   105  	}
   106  	for _, v := range s.serviceConfig.ExternalLinks {
   107  		hostConfig.Links = append(hostConfig.Links, v)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // FIXME(vdemeester) this is temporary
   114  func (s *Service) getLinks() (map[string]string, error) {
   115  	links := map[string]string{}
   116  	for _, link := range s.DependentServices() {
   117  		if !s.project.ServiceConfigs.Has(link.Target) {
   118  			continue
   119  		}
   120  
   121  		service, err := s.project.CreateService(link.Target)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  
   126  		// FIXME(vdemeester) container should not know service
   127  		containers, err := service.Containers(context.Background())
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  
   132  		if link.Type == project.RelTypeLink {
   133  			addLinks(links, service, link, containers)
   134  		}
   135  
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  	}
   140  	return links, nil
   141  }
   142  
   143  func addLinks(links map[string]string, service project.Service, rel project.ServiceRelationship, containers []project.Container) {
   144  	for _, container := range containers {
   145  		if _, ok := links[rel.Alias]; !ok {
   146  			links[rel.Alias] = container.Name()
   147  		}
   148  
   149  		links[container.Name()] = container.Name()
   150  	}
   151  }
   152  
   153  func addIpc(config *containertypes.HostConfig, service project.Service, containers []project.Container, ipc string) (*containertypes.HostConfig, error) {
   154  	if len(containers) == 0 {
   155  		return nil, fmt.Errorf("Failed to find container for IPC %v", ipc)
   156  	}
   157  
   158  	id, err := containers[0].ID()
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	config.IpcMode = containertypes.IpcMode("container:" + id)
   164  	return config, nil
   165  }
   166  
   167  func addNetNs(config *containertypes.HostConfig, service project.Service, containers []project.Container, networkMode string) (*containertypes.HostConfig, error) {
   168  	if len(containers) == 0 {
   169  		return nil, fmt.Errorf("Failed to find container for networks ns %v", networkMode)
   170  	}
   171  
   172  	id, err := containers[0].ID()
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	config.NetworkMode = containertypes.NetworkMode("container:" + id)
   178  	return config, nil
   179  }
   180  
   181  func volumeBinds(volumes map[string]struct{}, container *types.ContainerJSON) []string {
   182  	result := make([]string, 0, len(container.Mounts))
   183  	for _, mount := range container.Mounts {
   184  		if _, ok := volumes[mount.Destination]; ok {
   185  			result = append(result, fmt.Sprint(mount.Source, ":", mount.Destination))
   186  		}
   187  	}
   188  	return result
   189  }