2015-08-04 21:45:38 +00:00
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
2016-06-14 00:53:36 +00:00
|
|
|
"fmt"
|
2019-02-21 09:00:45 +00:00
|
|
|
"strings"
|
2016-06-14 00:53:36 +00:00
|
|
|
|
2018-10-23 04:13:32 +00:00
|
|
|
"github.com/rancher/os/config"
|
|
|
|
"github.com/rancher/os/pkg/log"
|
2019-09-29 02:31:33 +00:00
|
|
|
"github.com/rancher/os/pkg/util/network"
|
2018-10-23 04:13:32 +00:00
|
|
|
|
2019-02-21 09:00:45 +00:00
|
|
|
"github.com/docker/docker/layer"
|
2016-05-24 00:21:28 +00:00
|
|
|
dockerclient "github.com/docker/engine-api/client"
|
|
|
|
"github.com/docker/engine-api/types"
|
|
|
|
composeConfig "github.com/docker/libcompose/config"
|
2015-08-04 21:45:38 +00:00
|
|
|
"github.com/docker/libcompose/docker"
|
|
|
|
"github.com/docker/libcompose/project"
|
2016-05-24 00:21:28 +00:00
|
|
|
"github.com/docker/libcompose/project/options"
|
|
|
|
"golang.org/x/net/context"
|
2015-08-04 21:45:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Service struct {
|
|
|
|
*docker.Service
|
|
|
|
deps map[string][]string
|
|
|
|
context *docker.Context
|
2015-08-26 09:48:19 +00:00
|
|
|
project *project.Project
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func NewService(factory *ServiceFactory, name string, serviceConfig *composeConfig.ServiceConfig, context *docker.Context, project *project.Project) *Service {
|
2015-08-04 21:45:38 +00:00
|
|
|
return &Service{
|
|
|
|
Service: docker.NewService(name, serviceConfig, context),
|
|
|
|
deps: factory.Deps,
|
|
|
|
context: context,
|
2015-08-26 09:48:19 +00:00
|
|
|
project: project,
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) DependentServices() []project.ServiceRelationship {
|
|
|
|
rels := s.Service.DependentServices()
|
|
|
|
for _, dep := range s.deps[s.Name()] {
|
2015-08-26 09:48:19 +00:00
|
|
|
rels = appendLink(rels, dep, true, s.project)
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.requiresSyslog() {
|
2015-08-26 09:48:19 +00:00
|
|
|
rels = appendLink(rels, "syslog", false, s.project)
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.requiresUserDocker() {
|
2016-11-09 19:08:30 +00:00
|
|
|
rels = appendLink(rels, "docker", false, s.project)
|
2015-08-04 21:45:38 +00:00
|
|
|
} else if s.missingImage() {
|
2015-08-26 09:48:19 +00:00
|
|
|
rels = appendLink(rels, "network", false, s.project)
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
return rels
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) missingImage() bool {
|
|
|
|
image := s.Config().Image
|
|
|
|
if image == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
client := s.context.ClientFactory.Create(s)
|
2019-09-29 09:44:00 +00:00
|
|
|
|
|
|
|
// If it is already built-in, we should use tag image
|
|
|
|
// use case: open-vmtools with another REGISTRY_DOMAIN setting
|
|
|
|
registryDomain := config.LoadConfig().Rancher.Environment["REGISTRY_DOMAIN"]
|
|
|
|
if registryDomain != "docker.io" && strings.Index(image, registryDomain) >= 0 {
|
|
|
|
orginImage := strings.SplitN(image, "/", 2)[1]
|
|
|
|
_, _, err := client.ImageInspectWithRaw(context.Background(), orginImage, false)
|
|
|
|
if err == nil {
|
|
|
|
log.Infof("Will tag image %s to %s", orginImage, image)
|
|
|
|
options := types.ImageTagOptions{
|
|
|
|
ImageID: orginImage,
|
|
|
|
RepositoryName: strings.SplitN(image, ":", 2)[0],
|
|
|
|
Tag: strings.SplitN(image, ":", 2)[1],
|
|
|
|
Force: false,
|
|
|
|
}
|
|
|
|
if err := client.ImageTag(context.Background(), options); err != nil {
|
|
|
|
log.Warnf("Failed to tag image from %s to %s: %v", orginImage, image, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-20 12:28:18 +00:00
|
|
|
_, _, err := client.ImageInspectWithRaw(context.Background(), image, false)
|
2016-05-24 00:21:28 +00:00
|
|
|
return err != nil
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) requiresSyslog() bool {
|
2016-05-24 00:21:28 +00:00
|
|
|
return s.Config().Logging.Driver == "syslog"
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) requiresUserDocker() bool {
|
2016-11-28 08:06:00 +00:00
|
|
|
return s.Config().Labels[config.ScopeLabel] != config.System
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 09:48:19 +00:00
|
|
|
func appendLink(deps []project.ServiceRelationship, name string, optional bool, p *project.Project) []project.ServiceRelationship {
|
2016-05-24 00:21:28 +00:00
|
|
|
if _, ok := p.ServiceConfigs.Get(name); !ok {
|
2015-08-26 09:48:19 +00:00
|
|
|
return deps
|
|
|
|
}
|
2015-11-26 12:41:42 +00:00
|
|
|
rel := project.NewServiceRelationship(name, project.RelTypeLink)
|
2015-08-04 21:45:38 +00:00
|
|
|
rel.Optional = optional
|
|
|
|
return append(deps, rel)
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func (s *Service) shouldRebuild(ctx context.Context) (bool, error) {
|
|
|
|
containers, err := s.Containers(ctx)
|
2015-11-26 12:41:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2016-06-02 01:41:55 +00:00
|
|
|
cfg := config.LoadConfig()
|
2015-11-26 12:41:42 +00:00
|
|
|
for _, c := range containers {
|
2016-05-24 00:21:28 +00:00
|
|
|
outOfSync, err := c.(*docker.Container).OutOfSync(ctx, s.Service.Config().Image)
|
2015-11-26 12:41:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
_, containerInfo, err := s.getContainer(ctx)
|
|
|
|
if err != nil {
|
2015-11-26 12:41:42 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
name := containerInfo.Name[1:]
|
|
|
|
|
2016-11-28 08:06:00 +00:00
|
|
|
origRebuildLabel := containerInfo.Config.Labels[config.RebuildLabel]
|
|
|
|
newRebuildLabel := s.Config().Labels[config.RebuildLabel]
|
2015-11-26 12:41:42 +00:00
|
|
|
rebuildLabelChanged := newRebuildLabel != origRebuildLabel
|
2016-11-23 10:49:35 +00:00
|
|
|
log.WithFields(log.Fields{
|
2015-11-26 12:41:42 +00:00
|
|
|
"origRebuildLabel": origRebuildLabel,
|
|
|
|
"newRebuildLabel": newRebuildLabel,
|
|
|
|
"rebuildLabelChanged": rebuildLabelChanged,
|
|
|
|
"outOfSync": outOfSync}).Debug("Rebuild values")
|
|
|
|
|
2016-06-10 04:31:07 +00:00
|
|
|
if newRebuildLabel == "always" {
|
|
|
|
return true, nil
|
|
|
|
}
|
2016-08-29 18:29:32 +00:00
|
|
|
if s.Name() == "console" && cfg.Rancher.ForceConsoleRebuild {
|
|
|
|
if err := config.Set("rancher.force_console_rebuild", false); err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
2016-04-18 20:29:24 +00:00
|
|
|
if outOfSync {
|
2016-06-10 04:31:07 +00:00
|
|
|
if s.Name() == "console" {
|
2016-11-28 08:06:00 +00:00
|
|
|
origConsoleLabel := containerInfo.Config.Labels[config.ConsoleLabel]
|
|
|
|
newConsoleLabel := s.Config().Labels[config.ConsoleLabel]
|
2016-06-10 04:31:07 +00:00
|
|
|
if newConsoleLabel != origConsoleLabel {
|
|
|
|
return true, nil
|
2016-04-18 20:29:24 +00:00
|
|
|
}
|
2016-06-10 04:31:07 +00:00
|
|
|
} else if rebuildLabelChanged || origRebuildLabel != "false" {
|
|
|
|
return true, nil
|
2016-04-18 20:29:24 +00:00
|
|
|
} else {
|
2016-11-23 10:49:35 +00:00
|
|
|
log.Warnf("%s needs rebuilding", name)
|
2016-04-18 20:29:24 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-26 12:41:42 +00:00
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func (s *Service) Up(ctx context.Context, options options.Up) error {
|
|
|
|
labels := s.Config().Labels
|
2015-08-04 21:45:38 +00:00
|
|
|
|
2019-09-29 02:31:33 +00:00
|
|
|
// wait for networking if necessary
|
|
|
|
if after := labels["io.rancher.os.after"]; after == "network" {
|
|
|
|
if err := network.AllDefaultGWOK(network.DefaultRoutesCheckTimeout); err != nil {
|
|
|
|
log.Warnf("Timeout to wait for the networking ready: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
if err := s.Service.Create(ctx, options.Create); err != nil {
|
2015-08-04 21:45:38 +00:00
|
|
|
return err
|
|
|
|
}
|
2016-05-24 00:21:28 +00:00
|
|
|
|
|
|
|
shouldRebuild, err := s.shouldRebuild(ctx)
|
2015-11-26 12:41:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if shouldRebuild {
|
2016-11-23 10:49:35 +00:00
|
|
|
log.Infof("Rebuilding %s", s.Name())
|
2016-05-24 00:21:28 +00:00
|
|
|
cs, err := s.Service.Containers(ctx)
|
2015-11-26 12:41:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, c := range cs {
|
2016-05-24 00:21:28 +00:00
|
|
|
if _, err := c.(*docker.Container).Recreate(ctx, s.Config().Image); err != nil {
|
2019-02-21 09:00:45 +00:00
|
|
|
// sometimes we can get ErrMountNameConflict when booting on RPi
|
|
|
|
// ignore this error so that ros can boot success, otherwise it will hang forever
|
|
|
|
if strings.Contains(err.Error(), layer.ErrMountNameConflict.Error()) {
|
|
|
|
log.Warn(err)
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
2015-11-26 12:41:42 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-14 00:53:36 +00:00
|
|
|
if err = s.rename(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-03 12:38:59 +00:00
|
|
|
}
|
2016-11-28 08:06:00 +00:00
|
|
|
if labels[config.CreateOnlyLabel] == "true" {
|
2015-12-03 12:38:59 +00:00
|
|
|
return s.checkReload(labels)
|
2015-11-26 12:41:42 +00:00
|
|
|
}
|
2016-05-24 00:21:28 +00:00
|
|
|
if err := s.Service.Up(ctx, options); err != nil {
|
2015-08-04 21:45:38 +00:00
|
|
|
return err
|
|
|
|
}
|
2016-11-28 08:06:00 +00:00
|
|
|
if labels[config.DetachLabel] == "false" {
|
2016-05-24 00:21:28 +00:00
|
|
|
if err := s.wait(ctx); err != nil {
|
2015-08-04 21:45:38 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.checkReload(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) checkReload(labels map[string]string) error {
|
2016-11-28 08:06:00 +00:00
|
|
|
if labels[config.ReloadConfigLabel] == "true" {
|
2015-08-04 21:45:38 +00:00
|
|
|
return project.ErrRestart
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func (s *Service) Create(ctx context.Context, options options.Create) error {
|
|
|
|
return s.Service.Create(ctx, options)
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func (s *Service) getContainer(ctx context.Context) (dockerclient.APIClient, types.ContainerJSON, error) {
|
|
|
|
containers, err := s.Service.Containers(ctx)
|
|
|
|
|
2015-08-04 21:45:38 +00:00
|
|
|
if err != nil {
|
2016-05-24 00:21:28 +00:00
|
|
|
return nil, types.ContainerJSON{}, err
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(containers) == 0 {
|
2016-06-14 00:53:36 +00:00
|
|
|
return nil, types.ContainerJSON{}, fmt.Errorf("No containers found for %s", s.Name())
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
2015-11-26 12:41:42 +00:00
|
|
|
id, err := containers[0].ID()
|
2015-08-04 21:45:38 +00:00
|
|
|
if err != nil {
|
2016-05-24 00:21:28 +00:00
|
|
|
return nil, types.ContainerJSON{}, err
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
client := s.context.ClientFactory.Create(s)
|
2016-05-24 00:21:28 +00:00
|
|
|
info, err := client.ContainerInspect(context.Background(), id)
|
2015-08-04 21:45:38 +00:00
|
|
|
return client, info, err
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func (s *Service) wait(ctx context.Context) error {
|
|
|
|
client, info, err := s.getContainer(ctx)
|
|
|
|
if err != nil {
|
2015-08-04 21:45:38 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
if _, err := client.ContainerWait(context.Background(), info.ID); err != nil {
|
2015-08-04 21:45:38 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-11-26 12:41:42 +00:00
|
|
|
return nil
|
2015-08-04 21:45:38 +00:00
|
|
|
}
|
2015-12-03 12:38:59 +00:00
|
|
|
|
2016-05-24 00:21:28 +00:00
|
|
|
func (s *Service) rename(ctx context.Context) error {
|
|
|
|
client, info, err := s.getContainer(ctx)
|
|
|
|
if err != nil {
|
2015-12-03 12:38:59 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(info.Name) > 0 && info.Name[1:] != s.Name() {
|
2016-11-23 10:49:35 +00:00
|
|
|
log.Debugf("Renaming container %s => %s", info.Name[1:], s.Name())
|
2016-05-24 00:21:28 +00:00
|
|
|
return client.ContainerRename(context.Background(), info.ID, s.Name())
|
2015-12-03 12:38:59 +00:00
|
|
|
}
|
2016-11-28 08:06:00 +00:00
|
|
|
return nil
|
2015-12-03 12:38:59 +00:00
|
|
|
}
|