1
0
mirror of https://github.com/rancher/os.git synced 2025-09-26 13:13:02 +00:00
Files
os/vendor/github.com/docker/libcompose/docker/service.go
2015-12-04 20:19:31 +05:00

357 lines
8.3 KiB
Go

package docker
import (
"fmt"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/nat"
"github.com/docker/libcompose/project"
"github.com/docker/libcompose/utils"
)
// Service is a project.Service implementations.
type Service struct {
name string
serviceConfig *project.ServiceConfig
context *Context
}
// NewService creates a service
func NewService(name string, serviceConfig *project.ServiceConfig, context *Context) *Service {
return &Service{
name: name,
serviceConfig: serviceConfig,
context: context,
}
}
// Name returns the service name.
func (s *Service) Name() string {
return s.name
}
// Config returns the configuration of the service (project.ServiceConfig).
func (s *Service) Config() *project.ServiceConfig {
return s.serviceConfig
}
// DependentServices returns the dependent services (as an array of ServiceRelationship) of the service.
func (s *Service) DependentServices() []project.ServiceRelationship {
return project.DefaultDependentServices(s.context.Project, s)
}
// Create implements Service.Create.
func (s *Service) Create() error {
imageName, err := s.build()
if err != nil {
return err
}
_, err = s.createOne(imageName)
return err
}
func (s *Service) collectContainers() ([]*Container, error) {
client := s.context.ClientFactory.Create(s)
containers, err := GetContainersByFilter(client, SERVICE.Eq(s.name), PROJECT.Eq(s.context.Project.Name))
if err != nil {
return nil, err
}
result := []*Container{}
for _, container := range containers {
name := container.Labels[NAME.Str()]
result = append(result, NewContainer(client, name, s))
}
return result, nil
}
func (s *Service) createOne(imageName string) (*Container, error) {
containers, err := s.constructContainers(imageName, 1)
if err != nil {
return nil, err
}
return containers[0], err
}
// Build implements Service.Build. If an imageName is specified or if the context has
// no build to work with it will do nothing. Otherwise it will try to build
// the image and returns an error if any.
func (s *Service) Build() error {
_, err := s.build()
return err
}
func (s *Service) build() (string, error) {
if s.context.Builder == nil {
return s.Config().Image, nil
}
return s.context.Builder.Build(s.context.Project, s)
}
func (s *Service) constructContainers(imageName string, count int) ([]*Container, error) {
result, err := s.collectContainers()
if err != nil {
return nil, err
}
client := s.context.ClientFactory.Create(s)
var namer Namer
if s.serviceConfig.ContainerName != "" {
if count > 1 {
logrus.Warnf(`The "%s" service is using the custom container name "%s". Docker requires each container to have a unique name. Remove the custom name to scale the service.`, s.name, s.serviceConfig.ContainerName)
}
namer = NewSingleNamer(s.serviceConfig.ContainerName)
} else {
namer = NewNamer(client, s.context.Project.Name, s.name)
}
defer namer.Close()
for i := len(result); i < count; i++ {
containerName := namer.Next()
c := NewContainer(client, containerName, s)
dockerContainer, err := c.Create(imageName)
if err != nil {
return nil, err
}
logrus.Debugf("Created container %s: %v", dockerContainer.ID, dockerContainer.Names)
result = append(result, NewContainer(client, containerName, s))
}
return result, nil
}
// Up implements Service.Up. It builds the image if needed, creates a container
// and start it.
func (s *Service) Up() error {
imageName, err := s.build()
if err != nil {
return err
}
return s.up(imageName, true)
}
// Info implements Service.Info. It returns an project.InfoSet with the containers
// related to this service (can be multiple if using the scale command).
func (s *Service) Info(qFlag bool) (project.InfoSet, error) {
result := project.InfoSet{}
containers, err := s.collectContainers()
if err != nil {
return nil, err
}
for _, c := range containers {
info, err := c.Info(qFlag)
if err != nil {
return nil, err
}
result = append(result, info)
}
return result, nil
}
// Start implements Service.Start. It tries to start a container without creating it.
func (s *Service) Start() error {
return s.up("", false)
}
func (s *Service) up(imageName string, create bool) error {
containers, err := s.collectContainers()
if err != nil {
return err
}
logrus.Debugf("Found %d existing containers for service %s", len(containers), s.name)
if len(containers) == 0 && create {
c, err := s.createOne(imageName)
if err != nil {
return err
}
containers = []*Container{c}
}
return s.eachContainer(func(c *Container) error {
if create {
if err := s.recreateIfNeeded(imageName, c); err != nil {
return err
}
}
return c.Up(imageName)
})
}
func (s *Service) recreateIfNeeded(imageName string, c *Container) error {
if s.context.NoRecreate {
return nil
}
outOfSync, err := c.OutOfSync(imageName)
if err != nil {
return err
}
logrus.WithFields(logrus.Fields{
"outOfSync": outOfSync,
"ForceRecreate": s.context.ForceRecreate,
"NoRecreate": s.context.NoRecreate}).Debug("Going to decide if recreate is needed")
if s.context.ForceRecreate || outOfSync {
logrus.Infof("Recreating %s", s.name)
if _, err := c.Recreate(imageName); err != nil {
return err
}
}
return nil
}
func (s *Service) eachContainer(action func(*Container) error) error {
containers, err := s.collectContainers()
if err != nil {
return err
}
tasks := utils.InParallel{}
for _, container := range containers {
task := func(container *Container) func() error {
return func() error {
return action(container)
}
}(container)
tasks.Add(task)
}
return tasks.Wait()
}
// Down implements Service.Down. It stops any containers related to the service.
func (s *Service) Down() error {
return s.eachContainer(func(c *Container) error {
return c.Down()
})
}
// Restart implements Service.Restart. It restarts any containers related to the service.
func (s *Service) Restart() error {
return s.eachContainer(func(c *Container) error {
return c.Restart()
})
}
// Kill implements Service.Kill. It kills any containers related to the service.
func (s *Service) Kill() error {
return s.eachContainer(func(c *Container) error {
return c.Kill()
})
}
// Delete implements Service.Delete. It removes any containers related to the service.
func (s *Service) Delete() error {
return s.eachContainer(func(c *Container) error {
return c.Delete()
})
}
// Log implements Service.Log. It returns the docker logs for each container related to the service.
func (s *Service) Log() error {
return s.eachContainer(func(c *Container) error {
return c.Log()
})
}
// Scale implements Service.Scale. It creates or removes containers to have the specified number
// of related container to the service to run.
func (s *Service) Scale(scale int) error {
if s.specificiesHostPort() {
logrus.Warnf("The \"%s\" service specifies a port on the host. If multiple containers for this service are created on a single host, the port will clash.", s.Name())
}
foundCount := 0
err := s.eachContainer(func(c *Container) error {
foundCount++
if foundCount > scale {
err := c.Down()
if err != nil {
return err
}
return c.Delete()
}
return nil
})
if err != nil {
return err
}
if foundCount != scale {
imageName, err := s.build()
if err != nil {
return err
}
if _, err = s.constructContainers(imageName, scale); err != nil {
return err
}
}
return s.up("", false)
}
// Pull implements Service.Pull. It pulls or build the image of the service.
func (s *Service) Pull() error {
if s.Config().Image == "" {
return nil
}
return pullImage(s.context.ClientFactory.Create(s), s, s.Config().Image)
}
// Containers implements Service.Containers. It returns the list of containers
// that are related to the service.
func (s *Service) Containers() ([]project.Container, error) {
result := []project.Container{}
containers, err := s.collectContainers()
if err != nil {
return nil, err
}
for _, c := range containers {
result = append(result, c)
}
return result, nil
}
func (s *Service) specificiesHostPort() bool {
_, bindings, err := nat.ParsePortSpecs(s.Config().Ports)
if err != nil {
fmt.Println(err)
}
for _, portBindings := range bindings {
for _, portBinding := range portBindings {
if portBinding.HostPort != "" {
return true
}
}
}
return false
}