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
}
Exemple #2
0
func TestPortFilterSimple(t *testing.T) {
	var (
		p     = PortFilter{}
		nodes = []*cluster.Node{
			cluster.NewNode("node-1", 0),
			cluster.NewNode("node-2", 0),
			cluster.NewNode("node-3", 0),
		}
		result []*cluster.Node
		err    error
	)

	// Add a container taking away port 80 to nodes[0].
	container := &cluster.Container{Container: dockerclient.Container{Id: "c1"}, Info: dockerclient.ContainerInfo{HostConfig: &dockerclient.HostConfig{PortBindings: makeBinding("", "80")}}}
	assert.NoError(t, nodes[0].AddContainer(container))

	// Request port 80.
	config := &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)
	assert.NoError(t, err)
	assert.NotContains(t, result, nodes[0])
}
func createNode(ID string, memory int64, cpus int64) *cluster.Node {
	node := cluster.NewNode(ID, 0.05)
	node.ID = ID
	node.Memory = memory * 1024 * 1024 * 1024
	node.Cpus = cpus
	return node
}
Exemple #4
0
func TestPortFilterNoConflicts(t *testing.T) {
	var (
		p     = PortFilter{}
		nodes = []*cluster.Node{
			cluster.NewNode("node-1", 0),
			cluster.NewNode("node-2", 0),
			cluster.NewNode("node-3", 0),
		}
		result []*cluster.Node
		err    error
	)

	// Request no ports.
	config := &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{
			PortBindings: makeBinding("", "80"),
		},
	}

	// Since there are no other containers in the cluster, this shouldn't
	// filter anything either.
	result, err = p.Filter(config, nodes)
	assert.NoError(t, err)
	assert.Equal(t, result, nodes)

	// Add a container taking a different (4242) port.
	container := &cluster.Container{Container: dockerclient.Container{Id: "c1"}, Info: dockerclient.ContainerInfo{HostConfig: &dockerclient.HostConfig{PortBindings: makeBinding("", "4242")}}}
	assert.NoError(t, nodes[0].AddContainer(container))

	// Since no node is using port 80, there should be no filter
	result, err = p.Filter(config, nodes)
	assert.NoError(t, err)
	assert.Equal(t, result, nodes)
}
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 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)
}
Exemple #7
0
func manage(c *cli.Context) {
	var (
		tlsConfig *tls.Config = nil
		err       error
	)

	// If either --tls or --tlsverify are specified, load the certificates.
	if c.Bool("tls") || c.Bool("tlsverify") {
		tlsConfig, err = loadTlsConfig(
			c.String("tlscacert"),
			c.String("tlscert"),
			c.String("tlskey"),
			c.Bool("tlsverify"))
		if err != nil {
			log.Fatal(err)
		}
	}

	refresh := func(c *cluster.Cluster, nodes []string) {
		for _, addr := range nodes {
			go func(addr string) {
				if !strings.Contains(addr, "://") {
					addr = "http://" + addr
				}
				if c.Node(addr) == nil {
					n := cluster.NewNode(addr)
					if err := n.Connect(tlsConfig); err != nil {
						log.Error(err)
						return
					}
					if err := c.AddNode(n); err != nil {
						log.Error(err)
						return
					}
				}
			}(addr)
		}
	}

	cluster := cluster.NewCluster()
	cluster.Events(&logHandler{})

	go func() {
		if c.String("token") != "" {
			nodes, err := discovery.FetchSlaves(c.String("token"))
			if err != nil {
				log.Fatal(err)

			}
			refresh(cluster, nodes)

			hb := time.Duration(c.Int("heartbeat"))
			go func() {
				for {
					time.Sleep(hb * time.Second)
					nodes, err = discovery.FetchSlaves(c.String("token"))
					if err == nil {
						refresh(cluster, nodes)
					}
				}
			}()
		} else {
			refresh(cluster, c.Args())
		}
	}()

	s := scheduler.NewScheduler(
		cluster,
		&strategy.BinPackingPlacementStrategy{OvercommitRatio: 0.05},
		[]filter.Filter{
			&filter.HealthFilter{},
			&filter.LabelFilter{},
			&filter.PortFilter{},
		},
	)

	log.Fatal(api.ListenAndServe(cluster, s, c.String("addr"), c.App.Version, c.Bool("cors"), tlsConfig))
}
Exemple #8
0
func TestPortFilterDifferentInterfaces(t *testing.T) {
	var (
		p     = PortFilter{}
		nodes = []*cluster.Node{
			cluster.NewNode("node-1", 0),
			cluster.NewNode("node-2", 0),
			cluster.NewNode("node-3", 0),
		}
		result []*cluster.Node
		err    error
	)

	// Add a container taking away port 80 on every interface to nodes[0].
	container := &cluster.Container{Container: dockerclient.Container{Id: "c1"}, Info: dockerclient.ContainerInfo{HostConfig: &dockerclient.HostConfig{PortBindings: makeBinding("", "80")}}}
	assert.NoError(t, nodes[0].AddContainer(container))

	// Request port 80 for the local interface.
	config := &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.
	result, err = p.Filter(config, nodes)
	assert.NoError(t, err)
	assert.NotContains(t, result, nodes[0])

	// Add a container taking away port 4242 on the local interface of
	// nodes[1].
	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{
			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)
	assert.NoError(t, err)
	assert.NotContains(t, result, nodes[1])

	// Request port 4242 on every interface.
	config = &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{
			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{
			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)
	assert.NoError(t, err)
	assert.Contains(t, result, nodes[1])
}
Exemple #9
0
func TestPortFilterRandomAssignment(t *testing.T) {
	var (
		p     = PortFilter{}
		nodes = []*cluster.Node{
			cluster.NewNode("node-1", 0),
			cluster.NewNode("node-2", 0),
			cluster.NewNode("node-3", 0),
		}
		result []*cluster.Node
		err    error
	)

	// Simulate a container that requested to map 80 to a random port.
	// In this case, HostConfig.PortBindings should contain a binding with no
	// HostPort defined and NetworkSettings.Ports should contain the actual
	// mapped port.
	container := &cluster.Container{
		Container: dockerclient.Container{Id: "c1"},
		Info: dockerclient.ContainerInfo{
			HostConfig: &dockerclient.HostConfig{
				PortBindings: map[string][]dockerclient.PortBinding{
					"80/tcp": {
						{
							HostIp:   "",
							HostPort: "",
						},
					},
				},
			},
			NetworkSettings: struct {
				IpAddress   string
				IpPrefixLen int
				Gateway     string
				Bridge      string
				Ports       map[string][]dockerclient.PortBinding
			}{
				Ports: map[string][]dockerclient.PortBinding{
					"80/tcp": {
						{
							HostIp:   "127.0.0.1",
							HostPort: "1234",
						},
					},
				},
			},
		},
	}

	assert.NoError(t, nodes[0].AddContainer(container))

	// Request port 80.
	config := &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)
	assert.NoError(t, err)
	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{
			PortBindings: makeBinding("", "1234"),
		},
	}
	result, err = p.Filter(config, nodes)
	assert.NoError(t, err)
	assert.NotContains(t, result, nodes[0])
}
func TestAffinityFilter(t *testing.T) {
	var (
		f     = AffinityFilter{}
		nodes = []*cluster.Node{
			cluster.NewNode("node-0", 0),
			cluster.NewNode("node-1", 0),
			cluster.NewNode("node-2", 0),
		}
		result []*cluster.Node
		err    error
	)

	nodes[0].ID = "node-0-id"
	nodes[0].Name = "node-0-name"
	nodes[0].AddContainer(&cluster.Container{
		Container: dockerclient.Container{
			Id:    "container-n0-0-id",
			Names: []string{"/container-n0-0-name"},
		},
	})
	nodes[0].AddContainer(&cluster.Container{
		Container: dockerclient.Container{
			Id:    "container-n0-1-id",
			Names: []string{"/container-n0-1-name"},
		},
	})
	nodes[0].AddImage(&dockerclient.Image{
		Id:       "image-0-id",
		RepoTags: []string{"image-0:tag1", "image-0:tag2"},
	})

	nodes[1].ID = "node-1-id"
	nodes[1].Name = "node-1-name"
	nodes[1].AddContainer(&cluster.Container{
		Container: dockerclient.Container{
			Id:    "container-n1-0-id",
			Names: []string{"/container-n1-0-name"},
		},
	})
	nodes[1].AddContainer(&cluster.Container{
		Container: dockerclient.Container{
			Id:    "container-n1-1-id",
			Names: []string{"/container-n1-1-name"},
		},
	})
	nodes[1].AddImage(&dockerclient.Image{
		Id:       "image-1-id",
		RepoTags: []string{"image-1:tag1", "image-0:tag3", "image-1:tag2"},
	})

	nodes[2].ID = "node-2-id"
	nodes[2].Name = "node-2-name"

	// 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{"affinity:container==does_not_exsits"},
	}, 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{"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 fullfilled by a subset of nodes.
	result, err = f.Filter(&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)
	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)
	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)
	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)
	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)
	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)
	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)
	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)
	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)
	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)
	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)
	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)
	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)
	assert.Error(t, err)

	// Not support = any more
	result, err = f.Filter(&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)
	assert.Error(t, err)
	assert.Len(t, result, 0)
}