service.go 6.1 KB

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