Add support for soft affinities

Add documentation for soft affinity

trim ~ if the expression is not regex

Add example for !=~

Signed-off-by: sivaram mothiki <sivaram@opdemand.com>
This commit is contained in:
smothiki 2015-03-13 16:26:42 -06:00
parent 24b46bb002
commit 204216f173
4 changed files with 75 additions and 7 deletions

View File

@ -185,6 +185,23 @@ For example,
* `constraint:node!=/foo\[bar\]/` will match all nodes, except `foo[bar]`. You can see the use of escape characters here.
* `constraint:node==/(?i)node1/` will match node `node1` case-insensitive. So 'NoDe1' or 'NODE1' will also match.
#### Soft Affinities
By default, affinities are hard enforced. If an affinity is not met, the container won't be scheduled.
With soft affinites the scheduler will try to meet the affinity. If it is not met, the scheduler will discard the filter and schedule the container according to the scheduler strategy.
soft affinites are expressed with a **~** in the expression
Example ,
```
$ docker run -d --name redis1 -e affinity:image==~redis redis
```
If none of the nodes in the cluster has image redis, the scheduler will discard the affinity and schedules according to the strategy.
```
$ docker run -d --name redis5 -e affinity:container!=~redis* redis
```
The affinity filter is about scheduling a new redis5 container to a different node that doesn't have a container with the name that satisfies redis*. If each node in the cluster has a redis* container the scheduler with discard the affinity rule and schedules according to the strategy.
## Port Filter
With this filter, `ports` are considered as unique resources.

View File

@ -48,6 +48,9 @@ func (f *AffinityFilter) Filter(config *dockerclient.ContainerConfig, nodes []cl
}
}
if len(candidates) == 0 {
if affinity.IsSoft() {
return nodes, nil
}
return nil, fmt.Errorf("unable to find a node that satisfies %s%s%s", affinity.key, OPERATORS[affinity.operator], affinity.value)
}
nodes = candidates

View File

@ -193,6 +193,38 @@ func TestAffinityFilter(t *testing.T) {
}, nodes)
assert.Error(t, err)
//Tests for Soft affinity
result, err = f.Filter(&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==~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)
assert.NoError(t, err)
assert.Len(t, result, 2)
result, err = f.Filter(&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)
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"},
@ -206,4 +238,5 @@ func TestAffinityFilter(t *testing.T) {
}, nodes)
assert.Error(t, err)
assert.Len(t, result, 0)
}

View File

@ -48,7 +48,7 @@ func parseExprs(key string, env []string) ([]expr, error) {
// allow leading = in case of using ==
// allow * for globbing
// allow regexp
matched, err := regexp.MatchString(`^(?i)[=!\/]?[a-z0-9:\-_\.\*/\(\)\?\+\[\]\\\^\$]+$`, parts[1])
matched, err := regexp.MatchString(`^(?i)[=!\/]?(~)?[a-z0-9:\-_\.\*/\(\)\?\+\[\]\\\^\$]+$`, parts[1])
if err != nil {
return nil, err
}
@ -74,17 +74,24 @@ func parseExprs(key string, env []string) ([]expr, error) {
func (e *expr) Match(whats ...string) bool {
var (
pattern string
match bool
err error
pattern string
match bool
err error
startIndex int
)
if e.value[0] == '/' && e.value[len(e.value)-1] == '/' {
if e.IsSoft() {
startIndex = 1
} else {
startIndex = 0
}
if e.value[startIndex] == '/' && e.value[len(e.value)-1] == '/' {
// regexp
pattern = e.value[1 : len(e.value)-1]
pattern = e.value[startIndex+1 : len(e.value)-1]
} else {
// simple match, create the regex for globbing (ex: ub*t* -> ^ub.*t.*$) and match.
pattern = "^" + strings.Replace(e.value, "*", ".*", -1) + "$"
pattern = "^" + strings.Replace(strings.Trim(e.value, "~"), "*", ".*", -1) + "$"
}
for _, what := range whats {
@ -104,3 +111,11 @@ func (e *expr) Match(whats ...string) bool {
return false
}
func (e *expr) IsSoft() bool {
if e.value[0] == '~' {
return true
} else {
return false
}
}