From abfe5e6f4c0cfe541e73f53026e6e5622531c4ad Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Thu, 7 May 2015 00:34:18 -0700 Subject: [PATCH] Add support for Swarm IDs. - Every container that gets created through Swarm gets a Swarm ID assigned (as a label). - All API operations (start, stop, ...) can be performed by using either the container ID or the swarm ID. Signed-off-by: Andrea Luzzardi --- cluster/engine.go | 7 ++++++- cluster/swarm/cluster.go | 16 +++++++++++++++- cluster/swarm/cluster_test.go | 22 +++++++++++++++++----- test/integration/swarm_id.bats | 31 +++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 test/integration/swarm_id.bats diff --git a/cluster/engine.go b/cluster/engine.go index 7235f23ff7..e2d2aac943 100644 --- a/cluster/engine.go +++ b/cluster/engine.go @@ -463,11 +463,16 @@ func (e *Engine) Container(IDOrName string) *Container { } for _, container := range e.Containers() { - // Match ID prefix. + // Match Container ID prefix. if strings.HasPrefix(container.Id, IDOrName) { return container } + // Match Swarm ID prefix. + if strings.HasPrefix(container.Config.SwarmID(), IDOrName) { + return container + } + // Match name, /name or engine/name. for _, name := range container.Names { if name == IDOrName || name == "/"+IDOrName || container.Engine.ID+name == IDOrName || container.Engine.Name+name == IDOrName { diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 5f4f2254dc..40db12b139 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -9,6 +9,7 @@ import ( "sync" log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/units" "github.com/docker/swarm/cluster" "github.com/docker/swarm/discovery" @@ -80,16 +81,29 @@ func (c *Cluster) RegisterEventHandler(h cluster.EventHandler) error { return nil } +// Generate a globally (across the cluster) unique ID. +func (c *Cluster) generateUniqueID() string { + for { + id := stringid.GenerateRandomID() + if c.Container(id) == nil { + return id + } + } +} + // CreateContainer aka schedule a brand new container into the cluster. func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) (*cluster.Container, error) { c.scheduler.Lock() defer c.scheduler.Unlock() - // check new name whether avaliable + // Ensure the name is avaliable if cID := c.getIDFromName(name); cID != "" { return nil, fmt.Errorf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", name, cID, name) } + // Associate a Swarm ID to the container we are creating. + config.SetSwarmID(c.generateUniqueID()) + n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config) if err != nil { return nil, err diff --git a/cluster/swarm/cluster_test.go b/cluster/swarm/cluster_test.go index 3078e7b22c..aca6828e4c 100644 --- a/cluster/swarm/cluster_test.go +++ b/cluster/swarm/cluster_test.go @@ -8,13 +8,14 @@ import ( "github.com/stretchr/testify/assert" ) -func createEngine(t *testing.T, ID string, containers ...dockerclient.Container) *cluster.Engine { +func createEngine(t *testing.T, ID string, containers ...*cluster.Container) *cluster.Engine { engine := cluster.NewEngine(ID, 0) engine.Name = ID engine.ID = ID for _, container := range containers { - engine.AddContainer(&cluster.Container{Container: container, Engine: engine}) + container.Engine = engine + engine.AddContainer(container) } return engine @@ -24,9 +25,16 @@ func TestContainerLookup(t *testing.T) { c := &Cluster{ engines: make(map[string]*cluster.Engine), } - container := dockerclient.Container{ - Id: "container-id", - Names: []string{"/container-name1", "/container-name2"}, + container := &cluster.Container{ + Container: dockerclient.Container{ + Id: "container-id", + Names: []string{"/container-name1", "/container-name2"}, + }, + Config: cluster.BuildContainerConfig(dockerclient.ContainerConfig{ + Labels: map[string]string{ + "com.docker.swarm.id": "swarm-id", + }, + }), } n := createEngine(t, "test-engine", container) @@ -45,4 +53,8 @@ func TestContainerLookup(t *testing.T) { // Container engine/name matching. assert.NotNil(t, c.Container("test-engine/container-name1")) assert.NotNil(t, c.Container("test-engine/container-name2")) + // Swarm ID lookup. + assert.NotNil(t, c.Container("swarm-id")) + // Swarm ID prefix lookup. + assert.NotNil(t, c.Container("swarm-")) } diff --git a/test/integration/swarm_id.bats b/test/integration/swarm_id.bats new file mode 100644 index 0000000000..5140d031c4 --- /dev/null +++ b/test/integration/swarm_id.bats @@ -0,0 +1,31 @@ +#!/usr/bin/env bats + +load helpers + +function teardown() { + swarm_manage_cleanup + stop_docker +} + +@test "swarm id generation" { + start_docker_with_busybox 2 + swarm_manage + + # Create a dummy container just so we interfere with the tests. + # This one won't be used. + docker_swarm run -d busybox true + + # Create a container and get its Swarm ID back. + id=$(docker_swarm run -d busybox true) + swarm_id=$(docker_swarm inspect -f '{{ index .Config.Labels "com.docker.swarm.id" }}' "$id") + + # Make sure we got a valid Swarm ID. + [[ "${#swarm_id}" -eq 64 ]] + [[ "$id" != "$swarm_id" ]] + + # API operations should work with Swarm IDs as well as Container IDs. + [[ $(docker_swarm inspect -f "{{ .Id }}" "$swarm_id") == "$id" ]] + + # `docker ps` should be able to filter by Swarm ID using the label. + [[ $(docker_swarm ps -a -q --no-trunc --filter="label=com.docker.swarm.id=$swarm_id") == "$id" ]] +}