service.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package docker
  2. import (
  3. "fmt"
  4. dockerclient "github.com/docker/engine-api/client"
  5. "github.com/docker/engine-api/types"
  6. composeConfig "github.com/docker/libcompose/config"
  7. "github.com/docker/libcompose/docker"
  8. "github.com/docker/libcompose/project"
  9. "github.com/docker/libcompose/project/options"
  10. "github.com/rancher/os/config"
  11. "github.com/rancher/os/log"
  12. "golang.org/x/net/context"
  13. )
  14. type Service struct {
  15. *docker.Service
  16. deps map[string][]string
  17. context *docker.Context
  18. project *project.Project
  19. }
  20. func NewService(factory *ServiceFactory, name string, serviceConfig *composeConfig.ServiceConfig, context *docker.Context, project *project.Project) *Service {
  21. return &Service{
  22. Service: docker.NewService(name, serviceConfig, context),
  23. deps: factory.Deps,
  24. context: context,
  25. project: project,
  26. }
  27. }
  28. func (s *Service) DependentServices() []project.ServiceRelationship {
  29. rels := s.Service.DependentServices()
  30. for _, dep := range s.deps[s.Name()] {
  31. rels = appendLink(rels, dep, true, s.project)
  32. }
  33. if s.requiresSyslog() {
  34. rels = appendLink(rels, "syslog", false, s.project)
  35. }
  36. if s.requiresUserDocker() {
  37. rels = appendLink(rels, "docker", false, s.project)
  38. } else if s.missingImage() {
  39. rels = appendLink(rels, "network", false, s.project)
  40. }
  41. return rels
  42. }
  43. func (s *Service) missingImage() bool {
  44. image := s.Config().Image
  45. if image == "" {
  46. return false
  47. }
  48. client := s.context.ClientFactory.Create(s)
  49. _, _, err := client.ImageInspectWithRaw(context.Background(), image, false)
  50. if err != nil {
  51. log.Errorf("Missing the image: %v", err)
  52. }
  53. return err != nil
  54. }
  55. func (s *Service) requiresSyslog() bool {
  56. return s.Config().Logging.Driver == "syslog"
  57. }
  58. func (s *Service) requiresUserDocker() bool {
  59. return s.Config().Labels[config.ScopeLabel] != config.System
  60. }
  61. func appendLink(deps []project.ServiceRelationship, name string, optional bool, p *project.Project) []project.ServiceRelationship {
  62. if _, ok := p.ServiceConfigs.Get(name); !ok {
  63. return deps
  64. }
  65. rel := project.NewServiceRelationship(name, project.RelTypeLink)
  66. rel.Optional = optional
  67. return append(deps, rel)
  68. }
  69. func (s *Service) shouldRebuild(ctx context.Context) (bool, error) {
  70. containers, err := s.Containers(ctx)
  71. if err != nil {
  72. return false, err
  73. }
  74. cfg := config.LoadConfig()
  75. for _, c := range containers {
  76. outOfSync, err := c.(*docker.Container).OutOfSync(ctx, s.Service.Config().Image)
  77. if err != nil {
  78. return false, err
  79. }
  80. _, containerInfo, err := s.getContainer(ctx)
  81. if err != nil {
  82. return false, err
  83. }
  84. name := containerInfo.Name[1:]
  85. origRebuildLabel := containerInfo.Config.Labels[config.RebuildLabel]
  86. newRebuildLabel := s.Config().Labels[config.RebuildLabel]
  87. rebuildLabelChanged := newRebuildLabel != origRebuildLabel
  88. log.WithFields(log.Fields{
  89. "origRebuildLabel": origRebuildLabel,
  90. "newRebuildLabel": newRebuildLabel,
  91. "rebuildLabelChanged": rebuildLabelChanged,
  92. "outOfSync": outOfSync}).Debug("Rebuild values")
  93. if newRebuildLabel == "always" {
  94. return true, nil
  95. }
  96. if s.Name() == "console" && cfg.Rancher.ForceConsoleRebuild {
  97. if err := config.Set("rancher.force_console_rebuild", false); err != nil {
  98. return false, err
  99. }
  100. return true, nil
  101. }
  102. if outOfSync {
  103. if s.Name() == "console" {
  104. origConsoleLabel := containerInfo.Config.Labels[config.ConsoleLabel]
  105. newConsoleLabel := s.Config().Labels[config.ConsoleLabel]
  106. if newConsoleLabel != origConsoleLabel {
  107. return true, nil
  108. }
  109. } else if rebuildLabelChanged || origRebuildLabel != "false" {
  110. return true, nil
  111. } else {
  112. log.Warnf("%s needs rebuilding", name)
  113. }
  114. }
  115. }
  116. return false, nil
  117. }
  118. func (s *Service) Up(ctx context.Context, options options.Up) error {
  119. labels := s.Config().Labels
  120. if err := s.Service.Create(ctx, options.Create); err != nil {
  121. return err
  122. }
  123. shouldRebuild, err := s.shouldRebuild(ctx)
  124. if err != nil {
  125. return err
  126. }
  127. if shouldRebuild {
  128. log.Infof("Rebuilding %s", s.Name())
  129. cs, err := s.Service.Containers(ctx)
  130. if err != nil {
  131. return err
  132. }
  133. for _, c := range cs {
  134. if _, err := c.(*docker.Container).Recreate(ctx, s.Config().Image); err != nil {
  135. return err
  136. }
  137. }
  138. if err = s.rename(ctx); err != nil {
  139. return err
  140. }
  141. }
  142. if labels[config.CreateOnlyLabel] == "true" {
  143. return s.checkReload(labels)
  144. }
  145. if err := s.Service.Up(ctx, options); err != nil {
  146. return err
  147. }
  148. if labels[config.DetachLabel] == "false" {
  149. if err := s.wait(ctx); err != nil {
  150. return err
  151. }
  152. }
  153. return s.checkReload(labels)
  154. }
  155. func (s *Service) checkReload(labels map[string]string) error {
  156. if labels[config.ReloadConfigLabel] == "true" {
  157. return project.ErrRestart
  158. }
  159. return nil
  160. }
  161. func (s *Service) Create(ctx context.Context, options options.Create) error {
  162. return s.Service.Create(ctx, options)
  163. }
  164. func (s *Service) getContainer(ctx context.Context) (dockerclient.APIClient, types.ContainerJSON, error) {
  165. containers, err := s.Service.Containers(ctx)
  166. if err != nil {
  167. return nil, types.ContainerJSON{}, err
  168. }
  169. if len(containers) == 0 {
  170. return nil, types.ContainerJSON{}, fmt.Errorf("No containers found for %s", s.Name())
  171. }
  172. id, err := containers[0].ID()
  173. if err != nil {
  174. return nil, types.ContainerJSON{}, err
  175. }
  176. client := s.context.ClientFactory.Create(s)
  177. info, err := client.ContainerInspect(context.Background(), id)
  178. return client, info, err
  179. }
  180. func (s *Service) wait(ctx context.Context) error {
  181. client, info, err := s.getContainer(ctx)
  182. if err != nil {
  183. return err
  184. }
  185. if _, err := client.ContainerWait(context.Background(), info.ID); err != nil {
  186. return err
  187. }
  188. return nil
  189. }
  190. func (s *Service) rename(ctx context.Context) error {
  191. client, info, err := s.getContainer(ctx)
  192. if err != nil {
  193. return err
  194. }
  195. if len(info.Name) > 0 && info.Name[1:] != s.Name() {
  196. log.Debugf("Renaming container %s => %s", info.Name[1:], s.Name())
  197. return client.ContainerRename(context.Background(), info.ID, s.Name())
  198. }
  199. return nil
  200. }