mirror of https://github.com/docker/docs.git
345 lines
8.7 KiB
Go
345 lines
8.7 KiB
Go
package filter
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/docker/swarm/cluster"
|
|
"github.com/samalba/dockerclient"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func testFixtures() (nodes []*cluster.Node) {
|
|
nodes = []*cluster.Node{
|
|
cluster.NewNode("node-0", 0),
|
|
cluster.NewNode("node-1", 0),
|
|
cluster.NewNode("node-2", 0),
|
|
cluster.NewNode("node-3", 0),
|
|
}
|
|
nodes[0].ID = "node-0-id"
|
|
nodes[0].Name = "node-0-name"
|
|
nodes[0].Labels = map[string]string{
|
|
"name": "node0",
|
|
"group": "1",
|
|
"region": "us-west",
|
|
}
|
|
|
|
nodes[1].ID = "node-1-id"
|
|
nodes[1].Name = "node-1-name"
|
|
nodes[1].Labels = map[string]string{
|
|
"name": "node1",
|
|
"group": "1",
|
|
"region": "us-east",
|
|
}
|
|
|
|
nodes[2].ID = "node-2-id"
|
|
nodes[2].Name = "node-2-name"
|
|
nodes[2].Labels = map[string]string{
|
|
"name": "node2",
|
|
"group": "2",
|
|
"region": "eu",
|
|
}
|
|
nodes[3].ID = "node-3-id"
|
|
nodes[3].Name = "node-3-name"
|
|
return
|
|
}
|
|
|
|
func TestConstrainteFilter(t *testing.T) {
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
// Without constraints we should get the unfiltered list of nodes back.
|
|
result, err = f.Filter(&dockerclient.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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
assert.Error(t, err)
|
|
assert.Len(t, result, 0)
|
|
|
|
result, err = f.Filter(&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)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 2)
|
|
}
|
|
|
|
func TestConstraintNotExpr(t *testing.T) {
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
// Check not (!) expression
|
|
result, err = f.Filter(&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)
|
|
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)
|
|
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)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 2)
|
|
}
|
|
|
|
func TestConstraintRegExp(t *testing.T) {
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
// Check with regular expression /node\d/ matches node{0..2}
|
|
result, err = f.Filter(&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)
|
|
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)
|
|
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)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 2)
|
|
}
|
|
|
|
func TestFilterRegExpCaseInsensitive(t *testing.T) {
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
// Prepare node with a strange name
|
|
node3 := cluster.NewNode("node-3", 0)
|
|
node3.ID = "node-3-id"
|
|
node3.Name = "node-3-name"
|
|
node3.Labels = map[string]string{
|
|
"name": "aBcDeF",
|
|
"group": "2",
|
|
"region": "eu",
|
|
}
|
|
nodes[3] = node3
|
|
|
|
// Case-sensitive, so not match
|
|
result, err = f.Filter(&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)
|
|
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)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 3)
|
|
}
|
|
|
|
func TestFilterWithRelativeComparisons(t *testing.T) {
|
|
t.Skip()
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
// Prepare node with a strange name
|
|
node3 := cluster.NewNode("node-3", 0)
|
|
node3.ID = "node-3-id"
|
|
node3.Name = "node-3-name"
|
|
node3.Labels = map[string]string{
|
|
"name": "aBcDeF",
|
|
"group": "4",
|
|
"kernel": "3.1",
|
|
"region": "eu",
|
|
}
|
|
nodes = append(nodes, node3)
|
|
|
|
// Check with less than or equal
|
|
result, err = f.Filter(&dockerclient.ContainerConfig{
|
|
Env: []string{`constraint:group<=3`},
|
|
}, nodes)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 3)
|
|
|
|
// Check with greater than or equal
|
|
result, err = f.Filter(&dockerclient.ContainerConfig{
|
|
Env: []string{`constraint:group>=4`},
|
|
}, nodes)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 1)
|
|
|
|
// Another gte check with a complex string
|
|
result, err = f.Filter(&dockerclient.ContainerConfig{
|
|
Env: []string{`constraint:kernel>=3.0`},
|
|
}, nodes)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 1)
|
|
assert.Equal(t, result[0], nodes[3])
|
|
assert.Equal(t, result[0].Labels["kernel"], "3.1")
|
|
|
|
// Check with greater than or equal. This should match node-3-id.
|
|
result, err = f.Filter(&dockerclient.ContainerConfig{
|
|
Env: []string{`constraint:node>=node-3`},
|
|
}, nodes)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 1)
|
|
}
|
|
|
|
func TestFilterEquals(t *testing.T) {
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
// Check == comparison
|
|
result, err = f.Filter(&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)
|
|
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)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result, 1)
|
|
assert.Equal(t, result[0], nodes[1])
|
|
}
|
|
|
|
func TestUnsupportedOperators(t *testing.T) {
|
|
var (
|
|
f = ConstraintFilter{}
|
|
nodes = testFixtures()
|
|
result []*cluster.Node
|
|
err error
|
|
)
|
|
|
|
result, err = f.Filter(&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)
|
|
assert.Error(t, err)
|
|
assert.Len(t, result, 0)
|
|
}
|