diff --git a/README.md b/README.md index 8406ebf551..85aeb85aa5 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ other discovery services. ## Advanced Scheduling -See [filters](scheduler/filter) and [strategies](scheduler/strategy) to learn +See [filters](filter) and [strategies](strategy) to learn more about advanced scheduling. ## TLS diff --git a/api/api.go b/api/api.go index 48ec5f1494..b0845d015c 100644 --- a/api/api.go +++ b/api/api.go @@ -16,8 +16,8 @@ import ( dockerfilters "github.com/docker/docker/pkg/parsers/filters" "github.com/docker/docker/pkg/units" "github.com/docker/swarm/cluster" + "github.com/docker/swarm/filter" "github.com/docker/swarm/scheduler" - "github.com/docker/swarm/scheduler/filter" "github.com/docker/swarm/version" "github.com/gorilla/mux" "github.com/samalba/dockerclient" @@ -27,7 +27,7 @@ const APIVERSION = "1.16" type context struct { cluster *cluster.Cluster - scheduler *scheduler.Scheduler + scheduler scheduler.Scheduler eventsHandler *eventsHandler debug bool tlsConfig *tls.Config diff --git a/api/api_test.go b/api/api_test.go index 96c0f6da75..770415c453 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" ) -func serveRequest(c *cluster.Cluster, s *scheduler.Scheduler, w http.ResponseWriter, req *http.Request) error { +func serveRequest(c *cluster.Cluster, s scheduler.Scheduler, w http.ResponseWriter, req *http.Request) error { context := &context{ cluster: c, scheduler: s, diff --git a/api/server.go b/api/server.go index b4c7778636..20a996f443 100644 --- a/api/server.go +++ b/api/server.go @@ -29,7 +29,7 @@ func newListener(proto, addr string, tlsConfig *tls.Config) (net.Listener, error return l, nil } -func ListenAndServe(c *cluster.Cluster, s *scheduler.Scheduler, hosts []string, enableCors bool, tlsConfig *tls.Config) error { +func ListenAndServe(c *cluster.Cluster, s scheduler.Scheduler, hosts []string, enableCors bool, tlsConfig *tls.Config) error { context := &context{ cluster: c, scheduler: s, diff --git a/scheduler/filter/README.md b/filter/README.md similarity index 100% rename from scheduler/filter/README.md rename to filter/README.md diff --git a/scheduler/filter/affinity.go b/filter/affinity.go similarity index 100% rename from scheduler/filter/affinity.go rename to filter/affinity.go diff --git a/scheduler/filter/affinity_test.go b/filter/affinity_test.go similarity index 100% rename from scheduler/filter/affinity_test.go rename to filter/affinity_test.go diff --git a/scheduler/filter/constraint.go b/filter/constraint.go similarity index 100% rename from scheduler/filter/constraint.go rename to filter/constraint.go diff --git a/scheduler/filter/constraint_test.go b/filter/constraint_test.go similarity index 100% rename from scheduler/filter/constraint_test.go rename to filter/constraint_test.go diff --git a/scheduler/filter/expr.go b/filter/expr.go similarity index 100% rename from scheduler/filter/expr.go rename to filter/expr.go diff --git a/scheduler/filter/expr_test.go b/filter/expr_test.go similarity index 100% rename from scheduler/filter/expr_test.go rename to filter/expr_test.go diff --git a/scheduler/filter/filter.go b/filter/filter.go similarity index 100% rename from scheduler/filter/filter.go rename to filter/filter.go diff --git a/scheduler/filter/health.go b/filter/health.go similarity index 100% rename from scheduler/filter/health.go rename to filter/health.go diff --git a/scheduler/filter/port.go b/filter/port.go similarity index 100% rename from scheduler/filter/port.go rename to filter/port.go diff --git a/scheduler/filter/port_test.go b/filter/port_test.go similarity index 100% rename from scheduler/filter/port_test.go rename to filter/port_test.go diff --git a/flags.go b/flags.go index 2ce6272914..091d79e2f3 100644 --- a/flags.go +++ b/flags.go @@ -94,4 +94,9 @@ var ( Usage: "filter to use [constraint, affinity, health, port, dependency]", Value: &flFilterValue, } + flScheduler = cli.StringFlag{ + Name: "scheduler, s", + Usage: "scheduler to use [builtin, mesos]", + Value: "builtin", + } ) diff --git a/manage.go b/manage.go index c03e60e344..d4b8a8729e 100644 --- a/manage.go +++ b/manage.go @@ -12,10 +12,10 @@ import ( "github.com/docker/swarm/api" "github.com/docker/swarm/cluster" "github.com/docker/swarm/discovery" + "github.com/docker/swarm/filter" "github.com/docker/swarm/scheduler" - "github.com/docker/swarm/scheduler/filter" - "github.com/docker/swarm/scheduler/strategy" "github.com/docker/swarm/state" + "github.com/docker/swarm/strategy" ) type logHandler struct { @@ -137,12 +137,16 @@ func manage(c *cli.Context) { go d.Watch(cluster.UpdateNodes) }() - sched := scheduler.NewScheduler( + sched, err := scheduler.New(c.String("scheduler"), cluster, s, fs, ) + if err != nil { + log.Fatal(err) + } + // see https://github.com/codegangsta/cli/issues/160 hosts := c.StringSlice("host") if c.IsSet("host") || c.IsSet("H") { diff --git a/scheduler/builtin/builtin.go b/scheduler/builtin/builtin.go new file mode 100644 index 0000000000..69327b29c4 --- /dev/null +++ b/scheduler/builtin/builtin.go @@ -0,0 +1,63 @@ +package builtin + +import ( + "sync" + + "github.com/docker/swarm/cluster" + "github.com/docker/swarm/filter" + "github.com/docker/swarm/strategy" + "github.com/samalba/dockerclient" +) + +type BuiltinScheduler struct { + sync.Mutex + + cluster *cluster.Cluster + strategy strategy.PlacementStrategy + filters []filter.Filter +} + +func (s *BuiltinScheduler) Initialize(cluster *cluster.Cluster, strategy strategy.PlacementStrategy, filters []filter.Filter) { + s.cluster = cluster + s.strategy = strategy + s.filters = filters +} + +// Find a nice home for our container. +func (s *BuiltinScheduler) selectNodeForContainer(config *dockerclient.ContainerConfig) (*cluster.Node, error) { + candidates := s.cluster.Nodes() + + accepted, err := filter.ApplyFilters(s.filters, config, candidates) + if err != nil { + return nil, err + } + + return s.strategy.PlaceContainer(config, accepted) +} + +// Schedule a brand new container into the cluster. +func (s *BuiltinScheduler) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) { + /*Disable for now + if config.Memory == 0 || config.CpuShares == 0 { + return nil, fmt.Errorf("Creating containers in clustering mode requires resource constraints (-c and -m) to be set") + } + */ + + s.Lock() + defer s.Unlock() + + node, err := s.selectNodeForContainer(config) + if err != nil { + return nil, err + } + return s.cluster.DeployContainer(node, config, name) +} + +// Remove a container from the cluster. Containers should always be destroyed +// through the scheduler to guarantee atomicity. +func (s *BuiltinScheduler) RemoveContainer(container *cluster.Container, force bool) error { + s.Lock() + defer s.Unlock() + + return s.cluster.DestroyContainer(container, force) +} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index cc3a872fb9..9c3f8a219c 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -1,65 +1,38 @@ package scheduler import ( - "sync" + "errors" + log "github.com/Sirupsen/logrus" "github.com/docker/swarm/cluster" - "github.com/docker/swarm/scheduler/filter" - "github.com/docker/swarm/scheduler/strategy" + "github.com/docker/swarm/filter" + "github.com/docker/swarm/scheduler/builtin" + "github.com/docker/swarm/strategy" "github.com/samalba/dockerclient" ) -type Scheduler struct { - sync.Mutex - - cluster *cluster.Cluster - strategy strategy.PlacementStrategy - filters []filter.Filter +type Scheduler interface { + Initialize(cluster *cluster.Cluster, strategy strategy.PlacementStrategy, filters []filter.Filter) + CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) + RemoveContainer(container *cluster.Container, force bool) error } -func NewScheduler(cluster *cluster.Cluster, strategy strategy.PlacementStrategy, filters []filter.Filter) *Scheduler { - return &Scheduler{ - cluster: cluster, - strategy: strategy, - filters: filters, +var ( + schedulers map[string]Scheduler + ErrNotSupported = errors.New("scheduler not supported") +) + +func init() { + schedulers = map[string]Scheduler{ + "builtin": &builtin.BuiltinScheduler{}, } } -// Find a nice home for our container. -func (s *Scheduler) selectNodeForContainer(config *dockerclient.ContainerConfig) (*cluster.Node, error) { - candidates := s.cluster.Nodes() - - accepted, err := filter.ApplyFilters(s.filters, config, candidates) - if err != nil { - return nil, err +func New(name string, cluster *cluster.Cluster, strategy strategy.PlacementStrategy, filters []filter.Filter) (Scheduler, error) { + if scheduler, exists := schedulers[name]; exists { + log.WithField("name", name).Debug("Initializing scheduler") + scheduler.Initialize(cluster, strategy, filters) + return scheduler, nil } - - return s.strategy.PlaceContainer(config, accepted) -} - -// Schedule a brand new container into the cluster. -func (s *Scheduler) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) { - /*Disable for now - if config.Memory == 0 || config.CpuShares == 0 { - return nil, fmt.Errorf("Creating containers in clustering mode requires resource constraints (-c and -m) to be set") - } - */ - - s.Lock() - defer s.Unlock() - - node, err := s.selectNodeForContainer(config) - if err != nil { - return nil, err - } - return s.cluster.DeployContainer(node, config, name) -} - -// Remove a container from the cluster. Containers should always be destroyed -// through the scheduler to guarantee atomicity. -func (s *Scheduler) RemoveContainer(container *cluster.Container, force bool) error { - s.Lock() - defer s.Unlock() - - return s.cluster.DestroyContainer(container, force) + return nil, ErrNotSupported } diff --git a/scheduler/strategy/README.md b/strategy/README.md similarity index 100% rename from scheduler/strategy/README.md rename to strategy/README.md diff --git a/scheduler/strategy/binpacking.go b/strategy/binpacking.go similarity index 100% rename from scheduler/strategy/binpacking.go rename to strategy/binpacking.go diff --git a/scheduler/strategy/binpacking_test.go b/strategy/binpacking_test.go similarity index 100% rename from scheduler/strategy/binpacking_test.go rename to strategy/binpacking_test.go diff --git a/scheduler/strategy/random.go b/strategy/random.go similarity index 100% rename from scheduler/strategy/random.go rename to strategy/random.go diff --git a/scheduler/strategy/strategy.go b/strategy/strategy.go similarity index 100% rename from scheduler/strategy/strategy.go rename to strategy/strategy.go