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 }