github.com/MOXA-ISD/edge-library-libcompose@v0.4.1-0.20200417083957-c90441e63650/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/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  	networkConfig := configWrapper.NetworkingConfig
    59  	if configWrapper.HostConfig.NetworkMode != "" && configWrapper.HostConfig.NetworkMode.IsUserDefined() {
    60  		if networkConfig == nil {
    61  			networkConfig = &network.NetworkingConfig{
    62  				EndpointsConfig: map[string]*network.EndpointSettings{
    63  					string(configWrapper.HostConfig.NetworkMode): {},
    64  				},
    65  			}
    66  		}
    67  		for key, value := range networkConfig.EndpointsConfig {
    68  
    69  			conf := value
    70  			if value.Aliases == nil {
    71  				value.Aliases = []string{}
    72  			}
    73  			value.Aliases = append(value.Aliases, s.name)
    74  			networkConfig.EndpointsConfig[key] = conf
    75  		}
    76  	}
    77  	logrus.Debugf("Creating container %s %#v", containerName, configWrapper)
    78  	// FIXME(vdemeester): long-term will be container.Create(…)
    79  	container, err := composecontainer.Create(ctx, client, containerName, configWrapper.Config, configWrapper.HostConfig, networkConfig)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	s.project.Notify(events.ContainerCreated, s.name, map[string]string{
    84  		"name": containerName,
    85  	})
    86  	return container, nil
    87  }
    88  
    89  func (s *Service) populateAdditionalHostConfig(hostConfig *containertypes.HostConfig) error {
    90  	links, err := s.getLinks()
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	for _, link := range s.DependentServices() {
    96  		if !s.project.ServiceConfigs.Has(link.Target) {
    97  			continue
    98  		}
    99  
   100  		service, err := s.project.CreateService(link.Target)
   101  		if err != nil {
   102  			return err
   103  		}
   104  
   105  		containers, err := service.Containers(context.Background())
   106  		if err != nil {
   107  			return err
   108  		}
   109  
   110  		if link.Type == project.RelTypeIpcNamespace {
   111  			hostConfig, err = addIpc(hostConfig, service, containers, s.serviceConfig.Ipc)
   112  		} else if link.Type == project.RelTypeNetNamespace {
   113  			hostConfig, err = addNetNs(hostConfig, service, containers, s.serviceConfig.NetworkMode)
   114  		}
   115  
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	hostConfig.Links = []string{}
   122  	for k, v := range links {
   123  		hostConfig.Links = append(hostConfig.Links, strings.Join([]string{v, k}, ":"))
   124  	}
   125  	for _, v := range s.serviceConfig.ExternalLinks {
   126  		hostConfig.Links = append(hostConfig.Links, v)
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  // FIXME(vdemeester) this is temporary
   133  func (s *Service) getLinks() (map[string]string, error) {
   134  	links := map[string]string{}
   135  	for _, link := range s.DependentServices() {
   136  		if !s.project.ServiceConfigs.Has(link.Target) {
   137  			continue
   138  		}
   139  
   140  		service, err := s.project.CreateService(link.Target)
   141  		if err != nil {
   142  			return nil, err
   143  		}
   144  
   145  		// FIXME(vdemeester) container should not know service
   146  		containers, err := service.Containers(context.Background())
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  
   151  		if link.Type == project.RelTypeLink {
   152  			addLinks(links, service, link, containers)
   153  		}
   154  
   155  		if err != nil {
   156  			return nil, err
   157  		}
   158  	}
   159  	return links, nil
   160  }
   161  
   162  func addLinks(links map[string]string, service project.Service, rel project.ServiceRelationship, containers []project.Container) {
   163  	for _, container := range containers {
   164  		if _, ok := links[rel.Alias]; !ok {
   165  			links[rel.Alias] = container.Name()
   166  		}
   167  
   168  		links[container.Name()] = container.Name()
   169  	}
   170  }
   171  
   172  func addIpc(config *containertypes.HostConfig, service project.Service, containers []project.Container, ipc string) (*containertypes.HostConfig, error) {
   173  	if len(containers) == 0 {
   174  		return nil, fmt.Errorf("Failed to find container for IPC %v", ipc)
   175  	}
   176  
   177  	id := containers[0].ID()
   178  	config.IpcMode = containertypes.IpcMode("container:" + id)
   179  	return config, nil
   180  }
   181  
   182  func addNetNs(config *containertypes.HostConfig, service project.Service, containers []project.Container, networkMode string) (*containertypes.HostConfig, error) {
   183  	if len(containers) == 0 {
   184  		return nil, fmt.Errorf("Failed to find container for networks ns %v", networkMode)
   185  	}
   186  
   187  	id := containers[0].ID()
   188  	config.NetworkMode = containertypes.NetworkMode("container:" + id)
   189  	return config, nil
   190  }
   191  
   192  func volumeBinds(volumes map[string]struct{}, container *types.ContainerJSON) []string {
   193  	result := make([]string, 0, len(container.Mounts))
   194  	for _, mount := range container.Mounts {
   195  		if _, ok := volumes[mount.Destination]; ok {
   196  			result = append(result, fmt.Sprint(mount.Source, ":", mount.Destination))
   197  		}
   198  	}
   199  	return result
   200  }