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