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 }