project.go 6.7 KB

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