project.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package compose
  2. import (
  3. "fmt"
  4. "golang.org/x/net/context"
  5. yaml "github.com/cloudfoundry-incubator/candiedyaml"
  6. dockerClient "github.com/docker/engine-api/client"
  7. "github.com/docker/libcompose/cli/logger"
  8. composeConfig "github.com/docker/libcompose/config"
  9. "github.com/docker/libcompose/docker"
  10. composeClient "github.com/docker/libcompose/docker/client"
  11. "github.com/docker/libcompose/project"
  12. "github.com/docker/libcompose/project/events"
  13. "github.com/docker/libcompose/project/options"
  14. "github.com/rancher/os/config"
  15. rosDocker "github.com/rancher/os/docker"
  16. "github.com/rancher/os/log"
  17. "github.com/rancher/os/util"
  18. "github.com/rancher/os/util/network"
  19. )
  20. func CreateService(cfg *config.CloudConfig, name string, serviceConfig *composeConfig.ServiceConfigV1) (project.Service, error) {
  21. if cfg == nil {
  22. cfg = config.LoadConfig()
  23. }
  24. p, err := CreateServiceSet("once", cfg, map[string]*composeConfig.ServiceConfigV1{
  25. name: serviceConfig,
  26. })
  27. if err != nil {
  28. return nil, err
  29. }
  30. return p.CreateService(name)
  31. }
  32. func CreateServiceSet(name string, cfg *config.CloudConfig, configs map[string]*composeConfig.ServiceConfigV1) (*project.Project, error) {
  33. p, err := newProject(name, cfg, nil, nil)
  34. if err != nil {
  35. return nil, err
  36. }
  37. addServices(p, map[interface{}]interface{}{}, configs)
  38. return p, nil
  39. }
  40. func RunServiceSet(name string, cfg *config.CloudConfig, configs map[string]*composeConfig.ServiceConfigV1) (*project.Project, error) {
  41. p, err := CreateServiceSet(name, cfg, configs)
  42. if err != nil {
  43. return nil, err
  44. }
  45. return p, p.Up(context.Background(), options.Up{
  46. Log: cfg.Rancher.Log,
  47. })
  48. }
  49. func GetProject(cfg *config.CloudConfig, networkingAvailable, loadConsole bool) (*project.Project, error) {
  50. return newCoreServiceProject(cfg, networkingAvailable, loadConsole)
  51. }
  52. func newProject(name string, cfg *config.CloudConfig, environmentLookup composeConfig.EnvironmentLookup, authLookup *rosDocker.ConfigAuthLookup) (*project.Project, error) {
  53. clientFactory, err := rosDocker.NewClientFactory(composeClient.Options{})
  54. if err != nil {
  55. return nil, err
  56. }
  57. if environmentLookup == nil {
  58. environmentLookup = rosDocker.NewConfigEnvironment(cfg)
  59. }
  60. if authLookup == nil {
  61. authLookup = rosDocker.NewConfigAuthLookup(cfg)
  62. }
  63. serviceFactory := &rosDocker.ServiceFactory{
  64. Deps: map[string][]string{},
  65. }
  66. context := &docker.Context{
  67. ClientFactory: clientFactory,
  68. AuthLookup: authLookup,
  69. Context: project.Context{
  70. ProjectName: name,
  71. EnvironmentLookup: environmentLookup,
  72. ServiceFactory: serviceFactory,
  73. LoggerFactory: logger.NewColorLoggerFactory(),
  74. },
  75. }
  76. serviceFactory.Context = context
  77. authLookup.SetContext(context)
  78. return docker.NewProject(context, &composeConfig.ParseOptions{
  79. Interpolate: true,
  80. Validate: false,
  81. Preprocess: preprocessServiceMap,
  82. })
  83. }
  84. func preprocessServiceMap(serviceMap composeConfig.RawServiceMap) (composeConfig.RawServiceMap, error) {
  85. newServiceMap := make(composeConfig.RawServiceMap)
  86. for k, v := range serviceMap {
  87. newServiceMap[k] = make(composeConfig.RawService)
  88. for k2, v2 := range v {
  89. if k2 == "environment" || k2 == "labels" {
  90. newServiceMap[k][k2] = preprocess(v2, true)
  91. } else {
  92. newServiceMap[k][k2] = preprocess(v2, false)
  93. }
  94. }
  95. }
  96. return newServiceMap, nil
  97. }
  98. func preprocess(item interface{}, replaceTypes bool) interface{} {
  99. switch typedDatas := item.(type) {
  100. case map[interface{}]interface{}:
  101. newMap := make(map[interface{}]interface{})
  102. for key, value := range typedDatas {
  103. newMap[key] = preprocess(value, replaceTypes)
  104. }
  105. return newMap
  106. case []interface{}:
  107. // newArray := make([]interface{}, 0) will cause golint to complain
  108. var newArray []interface{}
  109. newArray = make([]interface{}, 0)
  110. for _, value := range typedDatas {
  111. newArray = append(newArray, preprocess(value, replaceTypes))
  112. }
  113. return newArray
  114. default:
  115. if replaceTypes {
  116. return fmt.Sprint(item)
  117. }
  118. return item
  119. }
  120. }
  121. func addServices(p *project.Project, enabled map[interface{}]interface{}, configs map[string]*composeConfig.ServiceConfigV1) map[interface{}]interface{} {
  122. serviceConfigsV2, _ := composeConfig.ConvertServices(configs)
  123. // Note: we ignore errors while loading services
  124. unchanged := true
  125. for name, serviceConfig := range serviceConfigsV2 {
  126. hash := composeConfig.GetServiceHash(name, serviceConfig)
  127. if enabled[name] == hash {
  128. continue
  129. }
  130. if err := p.AddConfig(name, serviceConfig); err != nil {
  131. log.Infof("Failed loading service %s", name)
  132. continue
  133. }
  134. if unchanged {
  135. enabled = util.MapCopy(enabled)
  136. unchanged = false
  137. }
  138. enabled[name] = hash
  139. }
  140. return enabled
  141. }
  142. func adjustContainerNames(m map[interface{}]interface{}) map[interface{}]interface{} {
  143. for k, v := range m {
  144. if k, ok := k.(string); ok {
  145. if v, ok := v.(map[interface{}]interface{}); ok {
  146. if _, ok := v["container_name"]; !ok {
  147. v["container_name"] = k
  148. }
  149. }
  150. }
  151. }
  152. return m
  153. }
  154. func newCoreServiceProject(cfg *config.CloudConfig, useNetwork, loadConsole bool) (*project.Project, error) {
  155. environmentLookup := rosDocker.NewConfigEnvironment(cfg)
  156. authLookup := rosDocker.NewConfigAuthLookup(cfg)
  157. p, err := newProject("os", cfg, environmentLookup, authLookup)
  158. if err != nil {
  159. return nil, err
  160. }
  161. projectEvents := make(chan events.Event)
  162. p.AddListener(project.NewDefaultListener(p))
  163. p.AddListener(projectEvents)
  164. p.ReloadCallback = projectReload(p, &useNetwork, loadConsole, environmentLookup, authLookup)
  165. go func() {
  166. for event := range projectEvents {
  167. if event.EventType == events.ContainerStarted && event.ServiceName == "network" {
  168. useNetwork = true
  169. }
  170. }
  171. }()
  172. err = p.ReloadCallback()
  173. if err != nil {
  174. log.Errorf("Failed to reload os: %v", err)
  175. return nil, err
  176. }
  177. return p, nil
  178. }
  179. func StageServices(cfg *config.CloudConfig, services ...string) error {
  180. p, err := newProject("stage-services", cfg, nil, nil)
  181. if err != nil {
  182. return err
  183. }
  184. for _, service := range services {
  185. bytes, err := network.LoadServiceResource(service, true, cfg)
  186. if err != nil {
  187. return fmt.Errorf("Failed to load %s : %v", service, err)
  188. }
  189. m := map[interface{}]interface{}{}
  190. if err := yaml.Unmarshal(bytes, &m); err != nil {
  191. return fmt.Errorf("Failed to parse YAML configuration: %s : %v", service, err)
  192. }
  193. bytes, err = yaml.Marshal(m)
  194. if err != nil {
  195. return fmt.Errorf("Failed to marshal YAML configuration: %s : %v", service, err)
  196. }
  197. err = p.Load(bytes)
  198. if err != nil {
  199. return fmt.Errorf("Failed to load %s : %v", service, err)
  200. }
  201. }
  202. // Reduce service configurations to just image and labels
  203. needToPull := false
  204. var client, userClient, systemClient dockerClient.APIClient
  205. for _, serviceName := range p.ServiceConfigs.Keys() {
  206. serviceConfig, _ := p.ServiceConfigs.Get(serviceName)
  207. // test to see if we need to Pull
  208. if serviceConfig.Labels[config.ScopeLabel] != config.System {
  209. if userClient == nil {
  210. userClient, err = rosDocker.NewDefaultClient()
  211. if err != nil {
  212. log.Error(err)
  213. }
  214. }
  215. client = userClient
  216. } else {
  217. if systemClient == nil {
  218. systemClient, err = rosDocker.NewSystemClient()
  219. if err != nil {
  220. log.Error(err)
  221. }
  222. client = systemClient
  223. }
  224. }
  225. if client != nil {
  226. _, _, err := client.ImageInspectWithRaw(context.Background(), serviceConfig.Image, false)
  227. if err == nil {
  228. log.Infof("Service %s using local image %s", serviceName, serviceConfig.Image)
  229. continue
  230. }
  231. }
  232. needToPull = true
  233. p.ServiceConfigs.Add(serviceName, &composeConfig.ServiceConfig{
  234. Image: serviceConfig.Image,
  235. Labels: serviceConfig.Labels,
  236. })
  237. }
  238. if needToPull {
  239. return p.Pull(context.Background())
  240. }
  241. return nil
  242. }