mirror of https://github.com/docker/docs.git
Merge pull request #673 from vieux/labels_storage
store constraints and affinities as labels
This commit is contained in:
commit
b39e023e03
|
@ -209,7 +209,7 @@ func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
container, err := c.cluster.CreateContainer(&config, name)
|
||||
container, err := c.cluster.CreateContainer(cluster.BuildContainerConfig(&config), name)
|
||||
if err != nil {
|
||||
httpError(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
// Cluster is exported
|
||||
type Cluster interface {
|
||||
// Create a container
|
||||
CreateContainer(config *dockerclient.ContainerConfig, name string) (*Container, error)
|
||||
CreateContainer(config *ContainerConfig, name string) (*Container, error)
|
||||
|
||||
// Remove a container
|
||||
RemoveContainer(container *Container, force bool) error
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
const namespace = "com.docker.swarm"
|
||||
|
||||
// ContainerConfig is exported
|
||||
// TODO store affinities and constraints in their own fields
|
||||
type ContainerConfig struct {
|
||||
dockerclient.ContainerConfig
|
||||
}
|
||||
|
||||
func parseEnv(e string) (bool, string, string) {
|
||||
parts := strings.SplitN(e, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
return true, parts[0], parts[1]
|
||||
}
|
||||
return false, "", ""
|
||||
}
|
||||
|
||||
// BuildContainerConfig creates a cluster.ContainerConfig from a dockerclient.ContainerConfig
|
||||
func BuildContainerConfig(c *dockerclient.ContainerConfig) *ContainerConfig {
|
||||
var (
|
||||
affinities []string
|
||||
constraints []string
|
||||
env []string
|
||||
)
|
||||
|
||||
// only for tests
|
||||
if c.Labels == nil {
|
||||
c.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
// parse affinities from labels (ex. docker run --label 'com.docker.swarm.affinities=["container==redis","image==nginx"]')
|
||||
if labels, ok := c.Labels[namespace+".affinities"]; ok {
|
||||
json.Unmarshal([]byte(labels), &affinities)
|
||||
}
|
||||
|
||||
// parse contraints from labels (ex. docker run --label 'com.docker.swarm.constraints=["region==us-east","storage==ssd"]')
|
||||
if labels, ok := c.Labels[namespace+".constraints"]; ok {
|
||||
json.Unmarshal([]byte(labels), &constraints)
|
||||
}
|
||||
|
||||
// parse affinities/contraints from env (ex. docker run -e affinity:container==redis -e affinity:image==nginx -e constraint:region==us-east -e constraint:storage==ssd)
|
||||
for _, e := range c.Env {
|
||||
if ok, key, value := parseEnv(e); ok && key == "affinity" {
|
||||
affinities = append(affinities, value)
|
||||
} else if ok && key == "constraint" {
|
||||
constraints = append(constraints, value)
|
||||
} else {
|
||||
env = append(env, e)
|
||||
}
|
||||
}
|
||||
|
||||
// remove affinities/contraints from env
|
||||
c.Env = env
|
||||
|
||||
// store affinities in labels
|
||||
if len(affinities) > 0 {
|
||||
if labels, err := json.Marshal(affinities); err == nil {
|
||||
c.Labels[namespace+".affinities"] = string(labels)
|
||||
}
|
||||
}
|
||||
|
||||
// store contraints in labels
|
||||
if len(constraints) > 0 {
|
||||
if labels, err := json.Marshal(constraints); err == nil {
|
||||
c.Labels[namespace+".constraints"] = string(labels)
|
||||
}
|
||||
}
|
||||
|
||||
return &ContainerConfig{*c}
|
||||
}
|
||||
|
||||
func (c *ContainerConfig) extractExprs(key string) []string {
|
||||
var exprs []string
|
||||
|
||||
if labels, ok := c.Labels[namespace+"."+key]; ok {
|
||||
json.Unmarshal([]byte(labels), &exprs)
|
||||
}
|
||||
|
||||
return exprs
|
||||
}
|
||||
|
||||
// Affinities returns all the affinities from the ContainerConfig
|
||||
func (c *ContainerConfig) Affinities() []string {
|
||||
return c.extractExprs("affinities")
|
||||
}
|
||||
|
||||
// Constraints returns all the constraints from the ContainerConfig
|
||||
func (c *ContainerConfig) Constraints() []string {
|
||||
return c.extractExprs("constraints")
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/samalba/dockerclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuildContainerConfig(t *testing.T) {
|
||||
config := BuildContainerConfig(&dockerclient.ContainerConfig{})
|
||||
assert.Equal(t, len(config.Env), 0)
|
||||
assert.Equal(t, len(config.Labels), 0)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"test=true"}})
|
||||
assert.Equal(t, len(config.Env), 1)
|
||||
assert.Equal(t, len(config.Labels), 0)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:test==true"}})
|
||||
assert.Equal(t, len(config.Env), 0)
|
||||
assert.Equal(t, len(config.Labels), 1)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==test"}})
|
||||
assert.Equal(t, len(config.Env), 0)
|
||||
assert.Equal(t, len(config.Labels), 1)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"test=true", "constraint:test==true", "affinity:container==test"}})
|
||||
assert.Equal(t, len(config.Env), 1)
|
||||
assert.Equal(t, len(config.Labels), 2)
|
||||
}
|
||||
|
||||
func TestConstraints(t *testing.T) {
|
||||
config := BuildContainerConfig(&dockerclient.ContainerConfig{})
|
||||
assert.Equal(t, len(config.Constraints()), 0)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:test==true"}})
|
||||
assert.Equal(t, len(config.Constraints()), 1)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"test=true", "constraint:test==true", "affinity:container==test"}})
|
||||
assert.Equal(t, len(config.Constraints()), 1)
|
||||
}
|
||||
|
||||
func TestAffinities(t *testing.T) {
|
||||
config := BuildContainerConfig(&dockerclient.ContainerConfig{})
|
||||
assert.Equal(t, len(config.Affinities()), 0)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==test"}})
|
||||
assert.Equal(t, len(config.Affinities()), 1)
|
||||
|
||||
config = BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"test=true", "constraint:test==true", "affinity:container==test"}})
|
||||
assert.Equal(t, len(config.Affinities()), 1)
|
||||
}
|
|
@ -349,7 +349,7 @@ func (e *Engine) TotalCpus() int64 {
|
|||
}
|
||||
|
||||
// Create a new container
|
||||
func (e *Engine) Create(config *dockerclient.ContainerConfig, name string, pullImage bool) (*Container, error) {
|
||||
func (e *Engine) Create(config *ContainerConfig, name string, pullImage bool) (*Container, error) {
|
||||
var (
|
||||
err error
|
||||
id string
|
||||
|
@ -361,7 +361,7 @@ func (e *Engine) Create(config *dockerclient.ContainerConfig, name string, pullI
|
|||
// nb of CPUs -> real CpuShares
|
||||
newConfig.CpuShares = config.CpuShares * 1024 / e.Cpus
|
||||
|
||||
if id, err = client.CreateContainer(&newConfig, name); err != nil {
|
||||
if id, err = client.CreateContainer(&newConfig.ContainerConfig, name); err != nil {
|
||||
// If the error is other than not found, abort immediately.
|
||||
if err != dockerclient.ErrNotFound || !pullImage {
|
||||
return nil, err
|
||||
|
@ -371,7 +371,7 @@ func (e *Engine) Create(config *dockerclient.ContainerConfig, name string, pullI
|
|||
return nil, err
|
||||
}
|
||||
// ...And try agaie.
|
||||
if id, err = client.CreateContainer(&newConfig, name); err != nil {
|
||||
if id, err = client.CreateContainer(&newConfig.ContainerConfig, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,12 +169,12 @@ func TestEngineContainerLookup(t *testing.T) {
|
|||
|
||||
func TestCreateContainer(t *testing.T) {
|
||||
var (
|
||||
config = &dockerclient.ContainerConfig{
|
||||
config = &ContainerConfig{dockerclient.ContainerConfig{
|
||||
Image: "busybox",
|
||||
CpuShares: 1,
|
||||
Cmd: []string{"date"},
|
||||
Tty: false,
|
||||
}
|
||||
}}
|
||||
engine = NewEngine("test", 0)
|
||||
client = mockclient.NewMockClient()
|
||||
)
|
||||
|
@ -186,7 +186,7 @@ func TestCreateContainer(t *testing.T) {
|
|||
assert.NoError(t, engine.connectClient(client))
|
||||
assert.True(t, engine.isConnected())
|
||||
|
||||
mockConfig := *config
|
||||
mockConfig := config.ContainerConfig
|
||||
mockConfig.CpuShares = config.CpuShares * 1024 / mockInfo.NCPU
|
||||
|
||||
// Everything is ok
|
||||
|
@ -195,7 +195,7 @@ func TestCreateContainer(t *testing.T) {
|
|||
client.On("CreateContainer", &mockConfig, name).Return(id, nil).Once()
|
||||
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
|
||||
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
|
||||
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: config}, nil).Once()
|
||||
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
|
||||
container, err := engine.Create(config, name, false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, container.Id, id)
|
||||
|
@ -218,7 +218,7 @@ func TestCreateContainer(t *testing.T) {
|
|||
client.On("CreateContainer", &mockConfig, name).Return(id, nil).Once()
|
||||
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
|
||||
client.On("ListImages").Return([]*dockerclient.Image{}, nil).Once()
|
||||
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: config}, nil).Once()
|
||||
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
|
||||
container, err = engine.Create(config, name, true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, container.Id, id)
|
||||
|
|
|
@ -81,7 +81,7 @@ func (c *Cluster) RegisterEventHandler(h cluster.EventHandler) error {
|
|||
}
|
||||
|
||||
// CreateContainer aka schedule a brand new container into the cluster.
|
||||
func (c *Cluster) CreateContainer(config *dockerclient.ContainerConfig, name string) (*cluster.Container, error) {
|
||||
func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) (*cluster.Container, error) {
|
||||
c.scheduler.Lock()
|
||||
defer c.scheduler.Unlock()
|
||||
|
||||
|
@ -376,7 +376,7 @@ func (c *Cluster) Info() [][2]string {
|
|||
|
||||
// RANDOMENGINE returns a random engine.
|
||||
func (c *Cluster) RANDOMENGINE() (*cluster.Engine, error) {
|
||||
n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), &dockerclient.ContainerConfig{})
|
||||
n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), &cluster.ContainerConfig{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// AffinityFilter selects only nodes based on other containers on the node.
|
||||
|
@ -19,8 +19,8 @@ func (f *AffinityFilter) Name() string {
|
|||
}
|
||||
|
||||
// Filter is exported
|
||||
func (f *AffinityFilter) Filter(config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
affinities, err := parseExprs("affinity", config.Env)
|
||||
func (f *AffinityFilter) Filter(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
affinities, err := parseExprs(config.Affinities())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -62,187 +62,133 @@ func TestAffinityFilter(t *testing.T) {
|
|||
)
|
||||
|
||||
// Without constraints we should get the unfiltered list of nodes back.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{}, nodes)
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodes)
|
||||
|
||||
// Set a constraint that cannot be fulfilled and expect an error back.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container==does_not_exsits"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==does_not_exsits"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Set a constraint that can only be filled by a single node.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container==container-n0*"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==container-n0*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// This constraint can only be fulfilled by a subset of nodes.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container==container-*"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==container-*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
assert.NotContains(t, result, nodes[2])
|
||||
|
||||
// Validate by id.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container==container-n0-0-id"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==container-n0-0-id"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Validate by id.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container!=container-n0-0-id"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container!=container-n0-0-id"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
assert.NotContains(t, result, nodes[0])
|
||||
|
||||
// Validate by id.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container!=container-n0-1-id"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container!=container-n0-1-id"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
assert.NotContains(t, result, nodes[0])
|
||||
|
||||
// Validate by name.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container==container-n1-0-name"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==container-n1-0-name"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
// Validate by name.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container!=container-n1-0-name"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container!=container-n1-0-name"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
assert.NotContains(t, result, nodes[1])
|
||||
|
||||
// Validate by name.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:container!=container-n1-1-name"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container!=container-n1-1-name"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
assert.NotContains(t, result, nodes[1])
|
||||
|
||||
// Validate images by id
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==image-0-id"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==image-0-id"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Validate images by name
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==image-0:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==image-0:tag3"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
// Validate images by name
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image!=image-0:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image!=image-0:tag3"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Validate images by name
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==image-1"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==image-1"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
// Validate images by name
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image!=image-1"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image!=image-1"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Ensure that constraints can be chained.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{
|
||||
"affinity:container!=container-n0-1-id",
|
||||
"affinity:container!=container-n1-1-id",
|
||||
},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container!=container-n0-1-id", "affinity:container!=container-n1-1-id"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[2])
|
||||
|
||||
// Ensure that constraints can be chained.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{
|
||||
"affinity:container==container-n0-1-id",
|
||||
"affinity:container==container-n1-1-id",
|
||||
},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:container==container-n0-1-id", "affinity:container==container-n1-1-id"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
|
||||
//Tests for Soft affinity
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~image-0:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==~image-0:tag3"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~ima~ge-0:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==~ima~ge-0:tag3"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~image-1:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==~image-1:tag3"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~image-*"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==~image-*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image!=~image-*"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image!=~image-*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[2])
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~/image-\\d*/"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image==~/image-\\d*/"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Not support = any more
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image=image-0:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image=image-0:tag3"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
// Not support =! any more
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image=!image-0:tag3"},
|
||||
}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"affinity:image=!image-0:tag3"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// ConstraintFilter selects only nodes that match certain labels.
|
||||
|
@ -18,8 +18,8 @@ func (f *ConstraintFilter) Name() string {
|
|||
}
|
||||
|
||||
// Filter is exported
|
||||
func (f *ConstraintFilter) Filter(config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
constraints, err := parseExprs("constraint", config.Env)
|
||||
func (f *ConstraintFilter) Filter(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
constraints, err := parseExprs(config.Constraints())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package filter
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -60,54 +61,54 @@ func TestConstrainteFilter(t *testing.T) {
|
|||
)
|
||||
|
||||
// Without constraints we should get the unfiltered list of nodes back.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{}, nodes)
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodes)
|
||||
|
||||
// Set a constraint that cannot be fullfilled and expect an error back.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:does_not_exist==true"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:does_not_exist==true"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Set a contraint that can only be filled by a single node.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name==node1"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name==node1"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
// This constraint can only be fullfilled by a subset of nodes.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:group==1"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:group==1"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
assert.NotContains(t, result, nodes[2])
|
||||
|
||||
// Validate node pinning by id.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:node==node-2-id"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:node==node-2-id"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[2])
|
||||
|
||||
// Validate node pinning by name.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:node==node-1-name"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:node==node-1-name"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
// Make sure constraints are evaluated as logical ANDs.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name==node0", "constraint:group==1"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name==node0", "constraint:group==1"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Check matching
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region==us"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region==us"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region==us*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region==us*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region==*us*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region==*us*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
}
|
||||
|
@ -121,22 +122,22 @@ func TestConstraintNotExpr(t *testing.T) {
|
|||
)
|
||||
|
||||
// Check not (!) expression
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name!=node0"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name!=node0"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
|
||||
// Check not does_not_exist. All should be found
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name!=does_not_exist"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name!=does_not_exist"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 4)
|
||||
|
||||
// Check name must not start with n
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name!=n*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name!=n*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
|
||||
// Check not with globber pattern
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region!=us*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region!=us*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
}
|
||||
|
@ -150,22 +151,22 @@ func TestConstraintRegExp(t *testing.T) {
|
|||
)
|
||||
|
||||
// Check with regular expression /node\d/ matches node{0..2}
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/node\d/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/node\d/`}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
|
||||
// Check with regular expression /node\d/ matches node{0..2}
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/node[12]/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/node[12]/`}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Check with regular expression ! and regexp /node[12]/ matches node[0] and node[3]
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name!=/node[12]/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name!=/node[12]/`}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Validate node pinning by ! and regexp.
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:node!=/node-[01]-id/"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:node!=/node-[01]-id/"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
}
|
||||
|
@ -186,19 +187,19 @@ func TestFilterRegExpCaseInsensitive(t *testing.T) {
|
|||
}
|
||||
|
||||
// Case-sensitive, so not match
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/abcdef/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/abcdef/`}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
// Match with case-insensitive
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/(?i)abcdef/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name==/(?i)abcdef/`}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[3])
|
||||
assert.Equal(t, result[0].Labels["name"], "aBcDeF")
|
||||
|
||||
// Test ! filter combined with case insensitive
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name!=/(?i)abc*/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name!=/(?i)abc*/`}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
}
|
||||
|
@ -212,17 +213,17 @@ func TestFilterEquals(t *testing.T) {
|
|||
)
|
||||
|
||||
// Check == comparison
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name==node0"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name==node0"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
|
||||
// Test == with glob
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region==us*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region==us*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Validate node name with ==
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:node==node-1-name"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:node==node-1-name"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
@ -236,11 +237,11 @@ func TestUnsupportedOperators(t *testing.T) {
|
|||
err error
|
||||
)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name=node0"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name=node0"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:name=!node0"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:name=!node0"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
}
|
||||
|
@ -253,26 +254,26 @@ func TestFilterSoftConstraint(t *testing.T) {
|
|||
err error
|
||||
)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:node==~node-1-name"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:node==~node-1-name"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{`constraint:name!=~/(?i)abc*/`}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{`constraint:name!=~/(?i)abc*/`}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 4)
|
||||
|
||||
// Check not with globber pattern
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region!=~us*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region!=~us*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region!=~can*"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region!=~can*"}}), nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 4)
|
||||
|
||||
// Check matching
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{Env: []string{"constraint:region==~us~"}}, nodes)
|
||||
result, err = f.Filter(cluster.BuildContainerConfig(&dockerclient.ContainerConfig{Env: []string{"constraint:region==~us~"}}), nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// DependencyFilter co-schedules dependent containers on the same node.
|
||||
|
@ -18,7 +18,7 @@ func (f *DependencyFilter) Name() string {
|
|||
}
|
||||
|
||||
// Filter is exported
|
||||
func (f *DependencyFilter) Filter(config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
func (f *DependencyFilter) Filter(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
if len(nodes) == 0 {
|
||||
return nodes, nil
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func (f *DependencyFilter) Filter(config *dockerclient.ContainerConfig, nodes []
|
|||
}
|
||||
|
||||
// Get a string representation of the dependencies found in the container config.
|
||||
func (f *DependencyFilter) String(config *dockerclient.ContainerConfig) string {
|
||||
func (f *DependencyFilter) String(config *cluster.ContainerConfig) string {
|
||||
dependencies := []string{}
|
||||
for _, volume := range config.HostConfig.VolumesFrom {
|
||||
dependencies = append(dependencies, fmt.Sprintf("--volumes-from=%s", volume))
|
||||
|
|
|
@ -36,46 +36,46 @@ func TestDependencyFilterSimple(t *testing.T) {
|
|||
}
|
||||
result []*node.Node
|
||||
err error
|
||||
config *dockerclient.ContainerConfig
|
||||
config *cluster.ContainerConfig
|
||||
)
|
||||
|
||||
// No dependencies - make sure we don't filter anything out.
|
||||
config = &dockerclient.ContainerConfig{}
|
||||
config = &cluster.ContainerConfig{}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodes)
|
||||
|
||||
// volumes-from.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c0"},
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// link.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
Links: []string{"c1:foobar"},
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[1])
|
||||
|
||||
// net.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
NetworkMode: "container:c2",
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[2])
|
||||
|
||||
// net not prefixed by "container:" should be ignored.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
NetworkMode: "bridge",
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodes)
|
||||
|
@ -113,40 +113,40 @@ func TestDependencyFilterMulti(t *testing.T) {
|
|||
}
|
||||
result []*node.Node
|
||||
err error
|
||||
config *dockerclient.ContainerConfig
|
||||
config *cluster.ContainerConfig
|
||||
)
|
||||
|
||||
// Depend on c0 which is on nodes[0]
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c0"},
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Depend on c1 which is on nodes[0]
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c1"},
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Depend on c0 AND c1 which are both on nodes[0]
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c0", "c1"},
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Depend on c0 AND c2 which are on different nodes.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c0", "c2"},
|
||||
}}
|
||||
}}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
@ -183,30 +183,30 @@ func TestDependencyFilterChaining(t *testing.T) {
|
|||
}
|
||||
result []*node.Node
|
||||
err error
|
||||
config *dockerclient.ContainerConfig
|
||||
config *cluster.ContainerConfig
|
||||
)
|
||||
|
||||
// Different dependencies on c0 and c1
|
||||
config = &dockerclient.ContainerConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{
|
||||
HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c0"},
|
||||
Links: []string{"c1"},
|
||||
NetworkMode: "container:c1",
|
||||
},
|
||||
}
|
||||
}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[0])
|
||||
|
||||
// Different dependencies on c0 and c2
|
||||
config = &dockerclient.ContainerConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{
|
||||
HostConfig: dockerclient.HostConfig{
|
||||
VolumesFrom: []string{"c0"},
|
||||
Links: []string{"c2"},
|
||||
NetworkMode: "container:c1",
|
||||
},
|
||||
}
|
||||
}}
|
||||
result, err = f.Filter(config, nodes)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
|
|
@ -25,52 +25,49 @@ type expr struct {
|
|||
isSoft bool
|
||||
}
|
||||
|
||||
func parseExprs(key string, env []string) ([]expr, error) {
|
||||
func parseExprs(env []string) ([]expr, error) {
|
||||
exprs := []expr{}
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, key+":") {
|
||||
entry := strings.TrimPrefix(e, key+":")
|
||||
found := false
|
||||
for i, op := range OPERATORS {
|
||||
if strings.Contains(entry, op) {
|
||||
// split with the op
|
||||
parts := strings.SplitN(entry, op, 2)
|
||||
found := false
|
||||
for i, op := range OPERATORS {
|
||||
if strings.Contains(e, op) {
|
||||
// split with the op
|
||||
parts := strings.SplitN(e, op, 2)
|
||||
|
||||
// validate key
|
||||
// allow alpha-numeric
|
||||
matched, err := regexp.MatchString(`^(?i)[a-z_][a-z0-9\-_.]+$`, parts[0])
|
||||
// validate key
|
||||
// allow alpha-numeric
|
||||
matched, err := regexp.MatchString(`^(?i)[a-z_][a-z0-9\-_.]+$`, parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if matched == false {
|
||||
return nil, fmt.Errorf("Key '%s' is invalid", parts[0])
|
||||
}
|
||||
|
||||
if len(parts) == 2 {
|
||||
|
||||
// validate value
|
||||
// allow leading = in case of using ==
|
||||
// allow * for globbing
|
||||
// allow regexp
|
||||
matched, err := regexp.MatchString(`^(?i)[=!\/]?(~)?[a-z0-9:\-_\s\.\*/\(\)\?\+\[\]\\\^\$\|]+$`, parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if matched == false {
|
||||
return nil, fmt.Errorf("Key '%s' is invalid", parts[0])
|
||||
return nil, fmt.Errorf("Value '%s' is invalid", parts[1])
|
||||
}
|
||||
|
||||
if len(parts) == 2 {
|
||||
|
||||
// validate value
|
||||
// allow leading = in case of using ==
|
||||
// allow * for globbing
|
||||
// allow regexp
|
||||
matched, err := regexp.MatchString(`^(?i)[=!\/]?(~)?[a-z0-9:\-_\s\.\*/\(\)\?\+\[\]\\\^\$\|]+$`, parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if matched == false {
|
||||
return nil, fmt.Errorf("Value '%s' is invalid", parts[1])
|
||||
}
|
||||
exprs = append(exprs, expr{key: strings.ToLower(parts[0]), operator: i, value: strings.TrimLeft(parts[1], "~"), isSoft: isSoft(parts[1])})
|
||||
} else {
|
||||
exprs = append(exprs, expr{key: strings.ToLower(parts[0]), operator: i})
|
||||
}
|
||||
|
||||
found = true
|
||||
break // found an op, move to next entry
|
||||
exprs = append(exprs, expr{key: strings.ToLower(parts[0]), operator: i, value: strings.TrimLeft(parts[1], "~"), isSoft: isSoft(parts[1])})
|
||||
} else {
|
||||
exprs = append(exprs, expr{key: strings.ToLower(parts[0]), operator: i})
|
||||
}
|
||||
|
||||
found = true
|
||||
break // found an op, move to next entry
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("One of operator ==, != is expected")
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, fmt.Errorf("One of operator ==, != is expected")
|
||||
}
|
||||
}
|
||||
return exprs, nil
|
||||
|
|
|
@ -8,43 +8,43 @@ import (
|
|||
|
||||
func TestParseExprs(t *testing.T) {
|
||||
// Cannot use the leading digit for key
|
||||
_, err := parseExprs("constraint", []string{"constraint:1node"})
|
||||
_, err := parseExprs([]string{"1node"})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cannot use space in key
|
||||
_, err = parseExprs("constraint", []string{"constraint:node ==node1"})
|
||||
_, err = parseExprs([]string{"node ==node1"})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cannot use * in key
|
||||
_, err = parseExprs("constraint", []string{"constraint:no*de==node1"})
|
||||
_, err = parseExprs([]string{"no*de==node1"})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Cannot use $ in key
|
||||
_, err = parseExprs("constraint", []string{"constraint:no$de==node1"})
|
||||
_, err = parseExprs([]string{"no$de==node1"})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Allow CAPS in key
|
||||
_, err = parseExprs("constraint", []string{"constraint:NoDe==node1"})
|
||||
_, err = parseExprs([]string{"NoDe==node1"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Allow dot in key
|
||||
_, err = parseExprs("constraint", []string{"constraint:no.de==node1"})
|
||||
_, err = parseExprs([]string{"no.de==node1"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Allow leading underscore
|
||||
_, err = parseExprs("constraint", []string{"constraint:_node==_node1"})
|
||||
_, err = parseExprs([]string{"_node==_node1"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Allow globbing
|
||||
_, err = parseExprs("constraint", []string{"constraint:node==*node*"})
|
||||
_, err = parseExprs([]string{"node==*node*"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Allow regexp in value
|
||||
_, err = parseExprs("constraint", []string{"constraint:node==/(?i)^[a-b]+c*(n|b)$/"})
|
||||
_, err = parseExprs([]string{"node==/(?i)^[a-b]+c*(n|b)$/"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Allow space in value
|
||||
_, err = parseExprs("constraint", []string{"constraint:node==node 1"})
|
||||
_, err = parseExprs([]string{"node==node 1"})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"errors"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// Filter is exported
|
||||
|
@ -13,7 +13,7 @@ type Filter interface {
|
|||
Name() string
|
||||
|
||||
// Return a subset of nodes that were accepted by the filtering policy.
|
||||
Filter(*dockerclient.ContainerConfig, []*node.Node) ([]*node.Node, error)
|
||||
Filter(*cluster.ContainerConfig, []*node.Node) ([]*node.Node, error)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -54,7 +54,7 @@ func New(names []string) ([]Filter, error) {
|
|||
}
|
||||
|
||||
// ApplyFilters applies a set of filters in batch.
|
||||
func ApplyFilters(filters []Filter, config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
func ApplyFilters(filters []Filter, config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
var err error
|
||||
|
||||
for _, filter := range filters {
|
||||
|
|
|
@ -3,8 +3,8 @@ package filter
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -22,7 +22,7 @@ func (f *HealthFilter) Name() string {
|
|||
}
|
||||
|
||||
// Filter is exported
|
||||
func (f *HealthFilter) Filter(_ *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
func (f *HealthFilter) Filter(_ *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
result := []*node.Node{}
|
||||
for _, node := range nodes {
|
||||
if node.IsHealthy {
|
||||
|
|
|
@ -3,6 +3,7 @@ package filter
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
@ -19,7 +20,7 @@ func (p *PortFilter) Name() string {
|
|||
}
|
||||
|
||||
// Filter is exported
|
||||
func (p *PortFilter) Filter(config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
func (p *PortFilter) Filter(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
if config.HostConfig.NetworkMode == "host" {
|
||||
return p.filterHost(config, nodes)
|
||||
}
|
||||
|
@ -27,7 +28,7 @@ func (p *PortFilter) Filter(config *dockerclient.ContainerConfig, nodes []*node.
|
|||
return p.filterBridge(config, nodes)
|
||||
}
|
||||
|
||||
func (p *PortFilter) filterHost(config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
func (p *PortFilter) filterHost(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
for port := range config.ExposedPorts {
|
||||
candidates := []*node.Node{}
|
||||
for _, node := range nodes {
|
||||
|
@ -43,7 +44,7 @@ func (p *PortFilter) filterHost(config *dockerclient.ContainerConfig, nodes []*n
|
|||
return nodes, nil
|
||||
}
|
||||
|
||||
func (p *PortFilter) filterBridge(config *dockerclient.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
func (p *PortFilter) filterBridge(config *cluster.ContainerConfig, nodes []*node.Node) ([]*node.Node, error) {
|
||||
for _, port := range config.HostConfig.PortBindings {
|
||||
for _, binding := range port {
|
||||
candidates := []*node.Node{}
|
||||
|
|
|
@ -46,18 +46,18 @@ func TestPortFilterNoConflicts(t *testing.T) {
|
|||
)
|
||||
|
||||
// Request no ports.
|
||||
config := &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: map[string][]dockerclient.PortBinding{},
|
||||
}}
|
||||
}}}
|
||||
// Make sure we don't filter anything out.
|
||||
result, err = p.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodes)
|
||||
|
||||
// Request port 80.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("", "80"),
|
||||
}}
|
||||
}}}
|
||||
|
||||
// Since there are no other containers in the cluster, this shouldn't
|
||||
// filter anything either.
|
||||
|
@ -104,9 +104,9 @@ func TestPortFilterSimple(t *testing.T) {
|
|||
assert.NoError(t, nodes[0].AddContainer(container))
|
||||
|
||||
// Request port 80.
|
||||
config := &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("", "80"),
|
||||
}}
|
||||
}}}
|
||||
|
||||
// nodes[0] should be excluded since port 80 is taken away.
|
||||
result, err = p.Filter(config, nodes)
|
||||
|
@ -143,9 +143,9 @@ func TestPortFilterDifferentInterfaces(t *testing.T) {
|
|||
assert.NoError(t, nodes[0].AddContainer(container))
|
||||
|
||||
// Request port 80 for the local interface.
|
||||
config := &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("127.0.0.1", "80"),
|
||||
}}
|
||||
}}}
|
||||
|
||||
// nodes[0] should be excluded since port 80 is taken away for every
|
||||
// interface.
|
||||
|
@ -158,9 +158,9 @@ func TestPortFilterDifferentInterfaces(t *testing.T) {
|
|||
container = &cluster.Container{Container: dockerclient.Container{Id: "c1"}, Info: dockerclient.ContainerInfo{HostConfig: &dockerclient.HostConfig{PortBindings: makeBinding("127.0.0.1", "4242")}}}
|
||||
assert.NoError(t, nodes[1].AddContainer(container))
|
||||
// Request port 4242 on the same interface.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("127.0.0.1", "4242"),
|
||||
}}
|
||||
}}}
|
||||
// nodes[1] should be excluded since port 4242 is already taken on that
|
||||
// interface.
|
||||
result, err = p.Filter(config, nodes)
|
||||
|
@ -168,27 +168,27 @@ func TestPortFilterDifferentInterfaces(t *testing.T) {
|
|||
assert.NotContains(t, result, nodes[1])
|
||||
|
||||
// Request port 4242 on every interface.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("0.0.0.0", "4242"),
|
||||
}}
|
||||
}}}
|
||||
// nodes[1] should still be excluded since the port is not available on the same interface.
|
||||
result, err = p.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, result, nodes[1])
|
||||
|
||||
// Request port 4242 on every interface using an alternative syntax.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("", "4242"),
|
||||
}}
|
||||
}}}
|
||||
// nodes[1] should still be excluded since the port is not available on the same interface.
|
||||
result, err = p.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, result, nodes[1])
|
||||
|
||||
// Finally, request port 4242 on a different interface.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("192.168.1.1", "4242"),
|
||||
}}
|
||||
}}}
|
||||
// nodes[1] should be included this time since the port is available on the
|
||||
// other interface.
|
||||
result, err = p.Filter(config, nodes)
|
||||
|
@ -257,9 +257,9 @@ func TestPortFilterRandomAssignment(t *testing.T) {
|
|||
assert.NoError(t, nodes[0].AddContainer(container))
|
||||
|
||||
// Request port 80.
|
||||
config := &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("", "80"),
|
||||
}}
|
||||
}}}
|
||||
|
||||
// Since port "80" has been mapped to "1234", we should be able to request "80".
|
||||
result, err = p.Filter(config, nodes)
|
||||
|
@ -267,9 +267,9 @@ func TestPortFilterRandomAssignment(t *testing.T) {
|
|||
assert.Equal(t, result, nodes)
|
||||
|
||||
// However, we should not be able to request "1234" since it has been used for a random assignment.
|
||||
config = &dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
config = &cluster.ContainerConfig{dockerclient.ContainerConfig{HostConfig: dockerclient.HostConfig{
|
||||
PortBindings: makeBinding("", "1234"),
|
||||
}}
|
||||
}}}
|
||||
result, err = p.Filter(config, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, result, nodes[0])
|
||||
|
@ -315,12 +315,12 @@ func TestPortFilterForHostMode(t *testing.T) {
|
|||
assert.NoError(t, nodes[0].AddContainer(container))
|
||||
|
||||
// Request port 80 in the host mode
|
||||
config := &dockerclient.ContainerConfig{
|
||||
config := &cluster.ContainerConfig{dockerclient.ContainerConfig{
|
||||
ExposedPorts: map[string]struct{}{"80": {}},
|
||||
HostConfig: dockerclient.HostConfig{
|
||||
NetworkMode: "host",
|
||||
},
|
||||
}
|
||||
}}
|
||||
|
||||
// nodes[0] should be excluded since port 80 is taken away
|
||||
result, err = p.Filter(config, nodes)
|
||||
|
|
|
@ -4,10 +4,10 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/filter"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/docker/swarm/scheduler/strategy"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// Scheduler is exported
|
||||
|
@ -27,7 +27,7 @@ func New(strategy strategy.PlacementStrategy, filters []filter.Filter) *Schedule
|
|||
}
|
||||
|
||||
// SelectNodeForContainer will find a nice home for our container.
|
||||
func (s *Scheduler) SelectNodeForContainer(nodes []*node.Node, config *dockerclient.ContainerConfig) (*node.Node, error) {
|
||||
func (s *Scheduler) SelectNodeForContainer(nodes []*node.Node, config *cluster.ContainerConfig) (*node.Node, error) {
|
||||
accepted, err := filter.ApplyFilters(s.filters, config, nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -3,8 +3,8 @@ package strategy
|
|||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// BinpackPlacementStrategy is exported
|
||||
|
@ -22,7 +22,7 @@ func (p *BinpackPlacementStrategy) Name() string {
|
|||
}
|
||||
|
||||
// PlaceContainer is exported
|
||||
func (p *BinpackPlacementStrategy) PlaceContainer(config *dockerclient.ContainerConfig, nodes []*node.Node) (*node.Node, error) {
|
||||
func (p *BinpackPlacementStrategy) PlaceContainer(config *cluster.ContainerConfig, nodes []*node.Node) (*node.Node, error) {
|
||||
weightedNodes, err := weighNodes(config, nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -22,12 +22,12 @@ func createNode(ID string, memory int64, cpus int64) *node.Node {
|
|||
}
|
||||
}
|
||||
|
||||
func createConfig(memory int64, cpus int64) *dockerclient.ContainerConfig {
|
||||
return &dockerclient.ContainerConfig{Memory: memory * 1024 * 1024 * 1024, CpuShares: cpus}
|
||||
func createConfig(memory int64, cpus int64) *cluster.ContainerConfig {
|
||||
return &cluster.ContainerConfig{dockerclient.ContainerConfig{Memory: memory * 1024 * 1024 * 1024, CpuShares: cpus}}
|
||||
}
|
||||
|
||||
func createContainer(ID string, config *dockerclient.ContainerConfig) *cluster.Container {
|
||||
return &cluster.Container{Container: dockerclient.Container{Id: ID}, Info: dockerclient.ContainerInfo{Config: config}}
|
||||
func createContainer(ID string, config *cluster.ContainerConfig) *cluster.Container {
|
||||
return &cluster.Container{Container: dockerclient.Container{Id: ID}, Info: dockerclient.ContainerInfo{Config: &config.ContainerConfig}}
|
||||
}
|
||||
|
||||
func TestPlaceEqualWeight(t *testing.T) {
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// RandomPlacementStrategy randomly places the container into the cluster.
|
||||
|
@ -24,7 +24,7 @@ func (p *RandomPlacementStrategy) Name() string {
|
|||
}
|
||||
|
||||
// PlaceContainer is exported
|
||||
func (p *RandomPlacementStrategy) PlaceContainer(config *dockerclient.ContainerConfig, nodes []*node.Node) (*node.Node, error) {
|
||||
func (p *RandomPlacementStrategy) PlaceContainer(config *cluster.ContainerConfig, nodes []*node.Node) (*node.Node, error) {
|
||||
if size := len(nodes); size > 0 {
|
||||
return nodes[rand.Intn(size)], nil
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package strategy
|
|||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// SpreadPlacementStrategy is exported
|
||||
|
@ -22,7 +22,7 @@ func (p *SpreadPlacementStrategy) Name() string {
|
|||
}
|
||||
|
||||
// PlaceContainer is exported
|
||||
func (p *SpreadPlacementStrategy) PlaceContainer(config *dockerclient.ContainerConfig, nodes []*node.Node) (*node.Node, error) {
|
||||
func (p *SpreadPlacementStrategy) PlaceContainer(config *cluster.ContainerConfig, nodes []*node.Node) (*node.Node, error) {
|
||||
weightedNodes, err := weighNodes(config, nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"errors"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// PlacementStrategy is exported
|
||||
|
@ -15,7 +15,7 @@ type PlacementStrategy interface {
|
|||
Initialize() error
|
||||
// Given a container configuration and a set of nodes, select the target
|
||||
// node where the container should be scheduled.
|
||||
PlaceContainer(config *dockerclient.ContainerConfig, nodes []*node.Node) (*node.Node, error)
|
||||
PlaceContainer(config *cluster.ContainerConfig, nodes []*node.Node) (*node.Node, error)
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package strategy
|
||||
|
||||
import (
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
// WeightedNode represents a node in the cluster with a given weight, typically used for sorting
|
||||
|
@ -32,7 +32,7 @@ func (n weightedNodeList) Less(i, j int) bool {
|
|||
return ip.Weight < jp.Weight
|
||||
}
|
||||
|
||||
func weighNodes(config *dockerclient.ContainerConfig, nodes []*node.Node) (weightedNodeList, error) {
|
||||
func weighNodes(config *cluster.ContainerConfig, nodes []*node.Node) (weightedNodeList, error) {
|
||||
weightedNodes := weightedNodeList{}
|
||||
|
||||
for _, node := range nodes {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
import "github.com/docker/swarm/cluster"
|
||||
|
||||
// RequestedState is exported
|
||||
type RequestedState struct {
|
||||
ID string
|
||||
Name string
|
||||
Config *dockerclient.ContainerConfig
|
||||
Config *cluster.ContainerConfig
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ function teardown() {
|
|||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c3 -e affinity:container!=c1 -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c4 --label 'com.docker.swarm.affinities=["container==c1"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c5 --label 'com.docker.swarm.affinities=["container\!=c1"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_swarm inspect c1
|
||||
[ "$status" -eq 0 ]
|
||||
|
@ -29,6 +33,14 @@ function teardown() {
|
|||
run docker_swarm inspect c3
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" != *'"Name": "node-0"'* ]]
|
||||
|
||||
run docker_swarm inspect c4
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-0"'* ]]
|
||||
|
||||
run docker_swarm inspect c5
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" != *'"Name": "node-0"'* ]]
|
||||
}
|
||||
|
||||
@test "image affinity" {
|
||||
|
@ -41,6 +53,10 @@ function teardown() {
|
|||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c2 -e affinity:image!=busybox -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c3 --label 'com.docker.swarm.affinities=["image==busybox"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c4 --label 'com.docker.swarm.affinities=["image\!=busybox"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_swarm inspect c1
|
||||
[ "$status" -eq 0 ]
|
||||
|
@ -49,6 +65,14 @@ function teardown() {
|
|||
run docker_swarm inspect c2
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" != *'"Name": "node-0"'* ]]
|
||||
|
||||
run docker_swarm inspect c3
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-0"'* ]]
|
||||
|
||||
run docker_swarm inspect c4
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" != *'"Name": "node-0"'* ]]
|
||||
}
|
||||
|
||||
@test "label affinity" {
|
||||
|
@ -61,6 +85,10 @@ function teardown() {
|
|||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c3 -e affinity:test.label!=true -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c4 --label 'com.docker.swarm.affinities=["test.label==true"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c5 --label 'com.docker.swarm.affinities=["test.label\!=true"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_swarm inspect c1
|
||||
[ "$status" -eq 0 ]
|
||||
|
@ -73,4 +101,12 @@ function teardown() {
|
|||
run docker_swarm inspect c3
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" != *'"Name": "node-0"'* ]]
|
||||
|
||||
run docker_swarm inspect c4
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-0"'* ]]
|
||||
|
||||
run docker_swarm inspect c5
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" != *'"Name": "node-0"'* ]]
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@ function teardown() {
|
|||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c3 -e constraint:node==node-1 -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_swarm run --name c4 --label 'com.docker.swarm.constraints=["node==node-1"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_swarm inspect c1
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-0"'* ]]
|
||||
|
@ -29,6 +31,10 @@ function teardown() {
|
|||
run docker_swarm inspect c3
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-1"'* ]]
|
||||
|
||||
run docker_swarm inspect c4
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-1"'* ]]
|
||||
}
|
||||
|
||||
@test "label constraints" {
|
||||
|
@ -42,6 +48,8 @@ function teardown() {
|
|||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c3 -e constraint:foo==b -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run --name c4 --label 'com.docker.swarm.constraints=["foo==b"]' -d busybox:latest sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_swarm inspect c1
|
||||
[ "$status" -eq 0 ]
|
||||
|
@ -54,4 +62,8 @@ function teardown() {
|
|||
run docker_swarm inspect c3
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-1"'* ]]
|
||||
|
||||
run docker_swarm inspect c4
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"Name": "node-1"'* ]]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue