Browse code

Store container names in memdb

Currently, names are maintained by a separate system called "registrar".
This means there is no way to atomically snapshot the state of
containers and the names associated with them.

We can add this atomicity and simplify the code by storing name
associations in the memdb. This removes the need for pkg/registrar, and
makes snapshots a lot less expensive because they no longer need to copy
all the names. This change also avoids some problematic behavior from
pkg/registrar where it returns slices which may be modified later on.

Note that while this change makes the *snapshotting* atomic, it doesn't
yet do anything to make sure containers are named at the same time that
they are added to the database. We can do that by adding a transactional
interface, either as a followup, or as part of this PR.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2017/06/30 10:56:22
Showing 11 changed files
... ...
@@ -1,6 +1,7 @@
1 1
 package container
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
5 6
 	"strings"
6 7
 	"time"
... ...
@@ -8,14 +9,23 @@ import (
8 8
 	"github.com/Sirupsen/logrus"
9 9
 	"github.com/docker/docker/api/types"
10 10
 	"github.com/docker/docker/api/types/network"
11
-	"github.com/docker/docker/pkg/registrar"
12 11
 	"github.com/docker/go-connections/nat"
13 12
 	"github.com/hashicorp/go-memdb"
14 13
 )
15 14
 
16 15
 const (
17
-	memdbTable   = "containers"
18
-	memdbIDIndex = "id"
16
+	memdbContainersTable = "containers"
17
+	memdbNamesTable      = "names"
18
+
19
+	memdbIDIndex          = "id"
20
+	memdbContainerIDIndex = "containerid"
21
+)
22
+
23
+var (
24
+	// ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved
25
+	ErrNameReserved = errors.New("name is reserved")
26
+	// ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved
27
+	ErrNameNotReserved = errors.New("name is not reserved")
19 28
 )
20 29
 
21 30
 // Snapshot is a read only view for Containers. It holds all information necessary to serve container queries in a
... ...
@@ -41,23 +51,37 @@ type Snapshot struct {
41 41
 	}
42 42
 }
43 43
 
44
+// nameAssociation associates a container id with a name.
45
+type nameAssociation struct {
46
+	// name is the name to associate. Note that name is the primary key
47
+	// ("id" in memdb).
48
+	name        string
49
+	containerID string
50
+}
51
+
44 52
 // ViewDB provides an in-memory transactional (ACID) container Store
45 53
 type ViewDB interface {
46
-	Snapshot(nameIndex *registrar.Registrar) View
54
+	Snapshot() View
47 55
 	Save(*Container) error
48 56
 	Delete(*Container) error
57
+
58
+	ReserveName(name, containerID string) error
59
+	ReleaseName(name string)
49 60
 }
50 61
 
51 62
 // View can be used by readers to avoid locking
52 63
 type View interface {
53 64
 	All() ([]Snapshot, error)
54 65
 	Get(id string) (*Snapshot, error)
66
+
67
+	GetID(name string) (string, error)
68
+	GetAllNames() map[string][]string
55 69
 }
56 70
 
57 71
 var schema = &memdb.DBSchema{
58 72
 	Tables: map[string]*memdb.TableSchema{
59
-		memdbTable: {
60
-			Name: memdbTable,
73
+		memdbContainersTable: {
74
+			Name: memdbContainersTable,
61 75
 			Indexes: map[string]*memdb.IndexSchema{
62 76
 				memdbIDIndex: {
63 77
 					Name:    memdbIDIndex,
... ...
@@ -66,6 +90,21 @@ var schema = &memdb.DBSchema{
66 66
 				},
67 67
 			},
68 68
 		},
69
+		memdbNamesTable: {
70
+			Name: memdbNamesTable,
71
+			Indexes: map[string]*memdb.IndexSchema{
72
+				// Used for names, because "id" is the primary key in memdb.
73
+				memdbIDIndex: {
74
+					Name:    memdbIDIndex,
75
+					Unique:  true,
76
+					Indexer: &namesByNameIndexer{},
77
+				},
78
+				memdbContainerIDIndex: {
79
+					Name:    memdbContainerIDIndex,
80
+					Indexer: &namesByContainerIDIndexer{},
81
+				},
82
+			},
83
+		},
69 84
 	},
70 85
 }
71 86
 
... ...
@@ -94,10 +133,9 @@ func NewViewDB() (ViewDB, error) {
94 94
 }
95 95
 
96 96
 // Snapshot provides a consistent read-only View of the database
97
-func (db *memDB) Snapshot(index *registrar.Registrar) View {
97
+func (db *memDB) Snapshot() View {
98 98
 	return &memdbView{
99
-		txn:       db.store.Txn(false),
100
-		nameIndex: index.GetAll(),
99
+		txn: db.store.Txn(false),
101 100
 	}
102 101
 }
103 102
 
... ...
@@ -106,25 +144,75 @@ func (db *memDB) Snapshot(index *registrar.Registrar) View {
106 106
 func (db *memDB) Save(c *Container) error {
107 107
 	txn := db.store.Txn(true)
108 108
 	defer txn.Commit()
109
-	return txn.Insert(memdbTable, c)
109
+	return txn.Insert(memdbContainersTable, c)
110 110
 }
111 111
 
112 112
 // Delete removes an item by ID
113 113
 func (db *memDB) Delete(c *Container) error {
114 114
 	txn := db.store.Txn(true)
115 115
 	defer txn.Commit()
116
-	return txn.Delete(memdbTable, NewBaseContainer(c.ID, c.Root))
116
+
117
+	// Delete any names referencing this container's ID.
118
+	iter, err := txn.Get(memdbNamesTable, memdbContainerIDIndex, c.ID)
119
+	if err != nil {
120
+		return err
121
+	}
122
+
123
+	var names []string
124
+	for {
125
+		item := iter.Next()
126
+		if item == nil {
127
+			break
128
+		}
129
+		names = append(names, item.(nameAssociation).name)
130
+	}
131
+
132
+	for _, name := range names {
133
+		txn.Delete(memdbNamesTable, nameAssociation{name: name})
134
+	}
135
+
136
+	return txn.Delete(memdbContainersTable, NewBaseContainer(c.ID, c.Root))
137
+}
138
+
139
+// ReserveName registers a container ID to a name
140
+// ReserveName is idempotent
141
+// Attempting to reserve a container ID to a name that already exists results in an `ErrNameReserved`
142
+// A name reservation is globally unique
143
+func (db *memDB) ReserveName(name, containerID string) error {
144
+	txn := db.store.Txn(true)
145
+	defer txn.Commit()
146
+
147
+	s, err := txn.First(memdbNamesTable, memdbIDIndex, name)
148
+	if err != nil {
149
+		return err
150
+	}
151
+	if s != nil {
152
+		if s.(nameAssociation).containerID != containerID {
153
+			return ErrNameReserved
154
+		}
155
+		return nil
156
+	}
157
+
158
+	txn.Insert(memdbNamesTable, nameAssociation{name: name, containerID: containerID})
159
+	return nil
160
+}
161
+
162
+// ReleaseName releases the reserved name
163
+// Once released, a name can be reserved again
164
+func (db *memDB) ReleaseName(name string) {
165
+	txn := db.store.Txn(true)
166
+	txn.Delete(memdbNamesTable, nameAssociation{name: name})
167
+	txn.Commit()
117 168
 }
118 169
 
119 170
 type memdbView struct {
120
-	txn       *memdb.Txn
121
-	nameIndex map[string][]string
171
+	txn *memdb.Txn
122 172
 }
123 173
 
124 174
 // All returns a all items in this snapshot. Returned objects must never be modified.
125 175
 func (v *memdbView) All() ([]Snapshot, error) {
126 176
 	var all []Snapshot
127
-	iter, err := v.txn.Get(memdbTable, memdbIDIndex)
177
+	iter, err := v.txn.Get(memdbContainersTable, memdbIDIndex)
128 178
 	if err != nil {
129 179
 		return nil, err
130 180
 	}
... ...
@@ -141,7 +229,7 @@ func (v *memdbView) All() ([]Snapshot, error) {
141 141
 
142 142
 // Get returns an item by id. Returned objects must never be modified.
143 143
 func (v *memdbView) Get(id string) (*Snapshot, error) {
144
-	s, err := v.txn.First(memdbTable, memdbIDIndex, id)
144
+	s, err := v.txn.First(memdbContainersTable, memdbIDIndex, id)
145 145
 	if err != nil {
146 146
 		return nil, err
147 147
 	}
... ...
@@ -151,13 +239,64 @@ func (v *memdbView) Get(id string) (*Snapshot, error) {
151 151
 	return v.transform(s.(*Container)), nil
152 152
 }
153 153
 
154
+// getNames lists all the reserved names for the given container ID.
155
+func (v *memdbView) getNames(containerID string) []string {
156
+	iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex, containerID)
157
+	if err != nil {
158
+		return nil
159
+	}
160
+
161
+	var names []string
162
+	for {
163
+		item := iter.Next()
164
+		if item == nil {
165
+			break
166
+		}
167
+		names = append(names, item.(nameAssociation).name)
168
+	}
169
+
170
+	return names
171
+}
172
+
173
+// GetID returns the container ID that the passed in name is reserved to.
174
+func (v *memdbView) GetID(name string) (string, error) {
175
+	s, err := v.txn.First(memdbNamesTable, memdbIDIndex, name)
176
+	if err != nil {
177
+		return "", err
178
+	}
179
+	if s == nil {
180
+		return "", ErrNameNotReserved
181
+	}
182
+	return s.(nameAssociation).containerID, nil
183
+}
184
+
185
+// GetAllNames returns all registered names.
186
+func (v *memdbView) GetAllNames() map[string][]string {
187
+	iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex)
188
+	if err != nil {
189
+		return nil
190
+	}
191
+
192
+	out := make(map[string][]string)
193
+	for {
194
+		item := iter.Next()
195
+		if item == nil {
196
+			break
197
+		}
198
+		assoc := item.(nameAssociation)
199
+		out[assoc.containerID] = append(out[assoc.containerID], assoc.name)
200
+	}
201
+
202
+	return out
203
+}
204
+
154 205
 // transform maps a (deep) copied Container object to what queries need.
155 206
 // A lock on the Container is not held because these are immutable deep copies.
156 207
 func (v *memdbView) transform(container *Container) *Snapshot {
157 208
 	snapshot := &Snapshot{
158 209
 		Container: types.Container{
159 210
 			ID:      container.ID,
160
-			Names:   v.nameIndex[container.ID],
211
+			Names:   v.getNames(container.ID),
161 212
 			ImageID: container.ImageID.String(),
162 213
 			Ports:   []types.Port{},
163 214
 			Mounts:  container.GetMountPoints(),
... ...
@@ -300,3 +439,55 @@ func (e *containerByIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
300 300
 	arg += "\x00"
301 301
 	return []byte(arg), nil
302 302
 }
303
+
304
+// namesByNameIndexer is used to index container name associations by name.
305
+type namesByNameIndexer struct{}
306
+
307
+func (e *namesByNameIndexer) FromObject(obj interface{}) (bool, []byte, error) {
308
+	n, ok := obj.(nameAssociation)
309
+	if !ok {
310
+		return false, nil, fmt.Errorf(`%T does not have type "nameAssociation"`, obj)
311
+	}
312
+
313
+	// Add the null character as a terminator
314
+	return true, []byte(n.name + "\x00"), nil
315
+}
316
+
317
+func (e *namesByNameIndexer) FromArgs(args ...interface{}) ([]byte, error) {
318
+	if len(args) != 1 {
319
+		return nil, fmt.Errorf("must provide only a single argument")
320
+	}
321
+	arg, ok := args[0].(string)
322
+	if !ok {
323
+		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
324
+	}
325
+	// Add the null character as a terminator
326
+	arg += "\x00"
327
+	return []byte(arg), nil
328
+}
329
+
330
+// namesByContainerIDIndexer is used to index container names by container ID.
331
+type namesByContainerIDIndexer struct{}
332
+
333
+func (e *namesByContainerIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
334
+	n, ok := obj.(nameAssociation)
335
+	if !ok {
336
+		return false, nil, fmt.Errorf(`%T does not have type "nameAssocation"`, obj)
337
+	}
338
+
339
+	// Add the null character as a terminator
340
+	return true, []byte(n.containerID + "\x00"), nil
341
+}
342
+
343
+func (e *namesByContainerIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
344
+	if len(args) != 1 {
345
+		return nil, fmt.Errorf("must provide only a single argument")
346
+	}
347
+	arg, ok := args[0].(string)
348
+	if !ok {
349
+		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
350
+	}
351
+	// Add the null character as a terminator
352
+	arg += "\x00"
353
+	return []byte(arg), nil
354
+}
... ...
@@ -7,8 +7,8 @@ import (
7 7
 	"testing"
8 8
 
9 9
 	containertypes "github.com/docker/docker/api/types/container"
10
-	"github.com/docker/docker/pkg/registrar"
11 10
 	"github.com/pborman/uuid"
11
+	"github.com/stretchr/testify/assert"
12 12
 )
13 13
 
14 14
 var root string
... ...
@@ -54,7 +54,6 @@ func TestViewSaveDelete(t *testing.T) {
54 54
 func TestViewAll(t *testing.T) {
55 55
 	var (
56 56
 		db, _ = NewViewDB()
57
-		names = registrar.NewRegistrar()
58 57
 		one   = newContainer(t)
59 58
 		two   = newContainer(t)
60 59
 	)
... ...
@@ -67,7 +66,7 @@ func TestViewAll(t *testing.T) {
67 67
 		t.Fatal(err)
68 68
 	}
69 69
 
70
-	all, err := db.Snapshot(names).All()
70
+	all, err := db.Snapshot().All()
71 71
 	if err != nil {
72 72
 		t.Fatal(err)
73 73
 	}
... ...
@@ -89,14 +88,13 @@ func TestViewAll(t *testing.T) {
89 89
 func TestViewGet(t *testing.T) {
90 90
 	var (
91 91
 		db, _ = NewViewDB()
92
-		names = registrar.NewRegistrar()
93 92
 		one   = newContainer(t)
94 93
 	)
95 94
 	one.ImageID = "some-image-123"
96 95
 	if err := one.CheckpointTo(db); err != nil {
97 96
 		t.Fatal(err)
98 97
 	}
99
-	s, err := db.Snapshot(names).Get(one.ID)
98
+	s, err := db.Snapshot().Get(one.ID)
100 99
 	if err != nil {
101 100
 		t.Fatal(err)
102 101
 	}
... ...
@@ -104,3 +102,52 @@ func TestViewGet(t *testing.T) {
104 104
 		t.Fatalf("expected ImageID=some-image-123. Got: %v", s)
105 105
 	}
106 106
 }
107
+
108
+func TestNames(t *testing.T) {
109
+	db, err := NewViewDB()
110
+	if err != nil {
111
+		t.Fatal(err)
112
+	}
113
+	assert.NoError(t, db.ReserveName("name1", "containerid1"))
114
+	assert.NoError(t, db.ReserveName("name1", "containerid1")) // idempotent
115
+	assert.NoError(t, db.ReserveName("name2", "containerid2"))
116
+	assert.EqualError(t, db.ReserveName("name2", "containerid3"), ErrNameReserved.Error())
117
+
118
+	// Releasing a name allows the name to point to something else later.
119
+	db.ReleaseName("name2")
120
+	assert.NoError(t, db.ReserveName("name2", "containerid3"))
121
+
122
+	view := db.Snapshot()
123
+
124
+	id, err := view.GetID("name1")
125
+	assert.NoError(t, err)
126
+	assert.Equal(t, "containerid1", id)
127
+
128
+	id, err = view.GetID("name2")
129
+	assert.NoError(t, err)
130
+	assert.Equal(t, "containerid3", id)
131
+
132
+	_, err = view.GetID("notreserved")
133
+	assert.EqualError(t, err, ErrNameNotReserved.Error())
134
+
135
+	// Releasing and re-reserving a name doesn't affect the snapshot.
136
+	db.ReleaseName("name2")
137
+	assert.NoError(t, db.ReserveName("name2", "containerid4"))
138
+
139
+	id, err = view.GetID("name1")
140
+	assert.NoError(t, err)
141
+	assert.Equal(t, "containerid1", id)
142
+
143
+	id, err = view.GetID("name2")
144
+	assert.NoError(t, err)
145
+	assert.Equal(t, "containerid3", id)
146
+
147
+	// GetAllNames
148
+	assert.Equal(t, map[string][]string{"containerid1": {"name1"}, "containerid3": {"name2"}}, view.GetAllNames())
149
+
150
+	assert.NoError(t, db.ReserveName("name3", "containerid1"))
151
+	assert.NoError(t, db.ReserveName("name4", "containerid1"))
152
+
153
+	view = db.Snapshot()
154
+	assert.Equal(t, map[string][]string{"containerid1": {"name1", "name3", "name4"}, "containerid4": {"name2"}}, view.GetAllNames())
155
+}
... ...
@@ -168,7 +168,7 @@ func (daemon *Daemon) GetByName(name string) (*container.Container, error) {
168 168
 	if name[0] != '/' {
169 169
 		fullName = "/" + name
170 170
 	}
171
-	id, err := daemon.nameIndex.Get(fullName)
171
+	id, err := daemon.containersReplica.Snapshot().GetID(fullName)
172 172
 	if err != nil {
173 173
 		return nil, fmt.Errorf("Could not find entity for %s", name)
174 174
 	}
... ...
@@ -42,7 +42,6 @@ import (
42 42
 	"github.com/docker/docker/migrate/v1"
43 43
 	"github.com/docker/docker/pkg/idtools"
44 44
 	"github.com/docker/docker/pkg/plugingetter"
45
-	"github.com/docker/docker/pkg/registrar"
46 45
 	"github.com/docker/docker/pkg/sysinfo"
47 46
 	"github.com/docker/docker/pkg/system"
48 47
 	"github.com/docker/docker/pkg/truncindex"
... ...
@@ -104,7 +103,6 @@ type Daemon struct {
104 104
 	stores                map[string]daemonStore // By container target platform
105 105
 	PluginStore           *plugin.Store          // todo: remove
106 106
 	pluginManager         *plugin.Manager
107
-	nameIndex             *registrar.Registrar
108 107
 	linkIndex             *linkIndex
109 108
 	containerd            libcontainerd.Client
110 109
 	containerdRemote      libcontainerd.Remote
... ...
@@ -448,8 +446,8 @@ func (daemon *Daemon) parents(c *container.Container) map[string]*container.Cont
448 448
 
449 449
 func (daemon *Daemon) registerLink(parent, child *container.Container, alias string) error {
450 450
 	fullName := path.Join(parent.Name, alias)
451
-	if err := daemon.nameIndex.Reserve(fullName, child.ID); err != nil {
452
-		if err == registrar.ErrNameReserved {
451
+	if err := daemon.containersReplica.ReserveName(fullName, child.ID); err != nil {
452
+		if err == container.ErrNameReserved {
453 453
 			logrus.Warnf("error registering link for %s, to %s, as alias %s, ignoring: %v", parent.ID, child.ID, alias, err)
454 454
 			return nil
455 455
 		}
... ...
@@ -780,7 +778,6 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
780 780
 	d.seccompEnabled = sysInfo.Seccomp
781 781
 	d.apparmorEnabled = sysInfo.AppArmor
782 782
 
783
-	d.nameIndex = registrar.NewRegistrar()
784 783
 	d.linkIndex = newLinkIndex()
785 784
 	d.containerdRemote = containerdRemote
786 785
 
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	"github.com/docker/docker/container"
13 13
 	_ "github.com/docker/docker/pkg/discovery/memory"
14 14
 	"github.com/docker/docker/pkg/idtools"
15
-	"github.com/docker/docker/pkg/registrar"
16 15
 	"github.com/docker/docker/pkg/truncindex"
17 16
 	"github.com/docker/docker/volume"
18 17
 	volumedrivers "github.com/docker/docker/volume/drivers"
... ...
@@ -65,10 +64,15 @@ func TestGetContainer(t *testing.T) {
65 65
 	index.Add(c4.ID)
66 66
 	index.Add(c5.ID)
67 67
 
68
+	containersReplica, err := container.NewViewDB()
69
+	if err != nil {
70
+		t.Fatalf("could not create ViewDB: %v", err)
71
+	}
72
+
68 73
 	daemon := &Daemon{
69
-		containers: store,
70
-		idIndex:    index,
71
-		nameIndex:  registrar.NewRegistrar(),
74
+		containers:        store,
75
+		containersReplica: containersReplica,
76
+		idIndex:           index,
72 77
 	}
73 78
 
74 79
 	daemon.reserveName(c1.ID, c1.Name)
... ...
@@ -60,7 +60,7 @@ func (daemon *Daemon) rmLink(container *container.Container, name string) error
60 60
 	}
61 61
 
62 62
 	parent = strings.TrimSuffix(parent, "/")
63
-	pe, err := daemon.nameIndex.Get(parent)
63
+	pe, err := daemon.containersReplica.Snapshot().GetID(parent)
64 64
 	if err != nil {
65 65
 		return fmt.Errorf("Cannot get parent %s for name %s", parent, name)
66 66
 	}
... ...
@@ -128,7 +128,6 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
128 128
 		return errors.Wrapf(err, "unable to remove filesystem for %s", container.ID)
129 129
 	}
130 130
 
131
-	daemon.nameIndex.Delete(container.ID)
132 131
 	daemon.linkIndex.delete(container)
133 132
 	selinuxFreeLxcContexts(container.ProcessLabel)
134 133
 	daemon.idIndex.Delete(container.ID)
... ...
@@ -182,7 +182,7 @@ func (daemon *Daemon) filterByNameIDMatches(view container.View, ctx *listContex
182 182
 // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
183 183
 func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
184 184
 	var (
185
-		view       = daemon.containersReplica.Snapshot(daemon.nameIndex)
185
+		view       = daemon.containersReplica.Snapshot()
186 186
 		containers = []*types.Container{}
187 187
 	)
188 188
 
... ...
@@ -361,7 +361,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
361 361
 		publish:              publishFilter,
362 362
 		expose:               exposeFilter,
363 363
 		ContainerListOptions: config,
364
-		names:                daemon.nameIndex.GetAll(),
364
+		names:                view.GetAllNames(),
365 365
 	}, nil
366 366
 }
367 367
 func portOp(key string, filter map[nat.Port]bool) func(value string) error {
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"github.com/docker/docker/api"
9 9
 	"github.com/docker/docker/container"
10 10
 	"github.com/docker/docker/pkg/namesgenerator"
11
-	"github.com/docker/docker/pkg/registrar"
12 11
 	"github.com/docker/docker/pkg/stringid"
13 12
 )
14 13
 
... ...
@@ -31,7 +30,7 @@ func (daemon *Daemon) registerName(container *container.Container) error {
31 31
 		}
32 32
 		container.Name = name
33 33
 	}
34
-	return daemon.nameIndex.Reserve(container.Name, container.ID)
34
+	return daemon.containersReplica.ReserveName(container.Name, container.ID)
35 35
 }
36 36
 
37 37
 func (daemon *Daemon) generateIDAndName(name string) (string, string, error) {
... ...
@@ -62,9 +61,9 @@ func (daemon *Daemon) reserveName(id, name string) (string, error) {
62 62
 		name = "/" + name
63 63
 	}
64 64
 
65
-	if err := daemon.nameIndex.Reserve(name, id); err != nil {
66
-		if err == registrar.ErrNameReserved {
67
-			id, err := daemon.nameIndex.Get(name)
65
+	if err := daemon.containersReplica.ReserveName(name, id); err != nil {
66
+		if err == container.ErrNameReserved {
67
+			id, err := daemon.containersReplica.Snapshot().GetID(name)
68 68
 			if err != nil {
69 69
 				logrus.Errorf("got unexpected error while looking up reserved name: %v", err)
70 70
 				return "", err
... ...
@@ -77,7 +76,7 @@ func (daemon *Daemon) reserveName(id, name string) (string, error) {
77 77
 }
78 78
 
79 79
 func (daemon *Daemon) releaseName(name string) {
80
-	daemon.nameIndex.Release(name)
80
+	daemon.containersReplica.ReleaseName(name)
81 81
 }
82 82
 
83 83
 func (daemon *Daemon) generateNewName(id string) (string, error) {
... ...
@@ -88,8 +87,8 @@ func (daemon *Daemon) generateNewName(id string) (string, error) {
88 88
 			name = "/" + name
89 89
 		}
90 90
 
91
-		if err := daemon.nameIndex.Reserve(name, id); err != nil {
92
-			if err == registrar.ErrNameReserved {
91
+		if err := daemon.containersReplica.ReserveName(name, id); err != nil {
92
+			if err == container.ErrNameReserved {
93 93
 				continue
94 94
 			}
95 95
 			return "", err
... ...
@@ -98,7 +97,7 @@ func (daemon *Daemon) generateNewName(id string) (string, error) {
98 98
 	}
99 99
 
100 100
 	name = "/" + stringid.TruncateID(id)
101
-	if err := daemon.nameIndex.Reserve(name, id); err != nil {
101
+	if err := daemon.containersReplica.ReserveName(name, id); err != nil {
102 102
 		return "", err
103 103
 	}
104 104
 	return name, nil
... ...
@@ -55,7 +55,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
55 55
 	}
56 56
 
57 57
 	for k, v := range links {
58
-		daemon.nameIndex.Reserve(newName+k, v.ID)
58
+		daemon.containersReplica.ReserveName(newName+k, v.ID)
59 59
 		daemon.linkIndex.link(container, v, newName+k)
60 60
 	}
61 61
 
... ...
@@ -68,10 +68,10 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
68 68
 			container.NetworkSettings.IsAnonymousEndpoint = oldIsAnonymousEndpoint
69 69
 			daemon.reserveName(container.ID, oldName)
70 70
 			for k, v := range links {
71
-				daemon.nameIndex.Reserve(oldName+k, v.ID)
71
+				daemon.containersReplica.ReserveName(oldName+k, v.ID)
72 72
 				daemon.linkIndex.link(container, v, oldName+k)
73 73
 				daemon.linkIndex.unlink(newName+k, v, container)
74
-				daemon.nameIndex.Release(newName + k)
74
+				daemon.containersReplica.ReleaseName(newName + k)
75 75
 			}
76 76
 			daemon.releaseName(newName)
77 77
 		}
... ...
@@ -79,7 +79,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
79 79
 
80 80
 	for k, v := range links {
81 81
 		daemon.linkIndex.unlink(oldName+k, v, container)
82
-		daemon.nameIndex.Release(oldName + k)
82
+		daemon.containersReplica.ReleaseName(oldName + k)
83 83
 	}
84 84
 	daemon.releaseName(oldName)
85 85
 	if err = container.CheckpointTo(daemon.containersReplica); err != nil {
86 86
deleted file mode 100644
... ...
@@ -1,130 +0,0 @@
1
-// Package registrar provides name registration. It reserves a name to a given key.
2
-package registrar
3
-
4
-import (
5
-	"errors"
6
-	"sync"
7
-)
8
-
9
-var (
10
-	// ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved
11
-	ErrNameReserved = errors.New("name is reserved")
12
-	// ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved
13
-	ErrNameNotReserved = errors.New("name is not reserved")
14
-	// ErrNoSuchKey is returned when trying to find the names for a key which is not known
15
-	ErrNoSuchKey = errors.New("provided key does not exist")
16
-)
17
-
18
-// Registrar stores indexes a list of keys and their registered names as well as indexes names and the key that they are registered to
19
-// Names must be unique.
20
-// Registrar is safe for concurrent access.
21
-type Registrar struct {
22
-	idx   map[string][]string
23
-	names map[string]string
24
-	mu    sync.Mutex
25
-}
26
-
27
-// NewRegistrar creates a new Registrar with the an empty index
28
-func NewRegistrar() *Registrar {
29
-	return &Registrar{
30
-		idx:   make(map[string][]string),
31
-		names: make(map[string]string),
32
-	}
33
-}
34
-
35
-// Reserve registers a key to a name
36
-// Reserve is idempotent
37
-// Attempting to reserve a key to a name that already exists results in an `ErrNameReserved`
38
-// A name reservation is globally unique
39
-func (r *Registrar) Reserve(name, key string) error {
40
-	r.mu.Lock()
41
-	defer r.mu.Unlock()
42
-
43
-	if k, exists := r.names[name]; exists {
44
-		if k != key {
45
-			return ErrNameReserved
46
-		}
47
-		return nil
48
-	}
49
-
50
-	r.idx[key] = append(r.idx[key], name)
51
-	r.names[name] = key
52
-	return nil
53
-}
54
-
55
-// Release releases the reserved name
56
-// Once released, a name can be reserved again
57
-func (r *Registrar) Release(name string) {
58
-	r.mu.Lock()
59
-	defer r.mu.Unlock()
60
-
61
-	key, exists := r.names[name]
62
-	if !exists {
63
-		return
64
-	}
65
-
66
-	for i, n := range r.idx[key] {
67
-		if n != name {
68
-			continue
69
-		}
70
-		r.idx[key] = append(r.idx[key][:i], r.idx[key][i+1:]...)
71
-		break
72
-	}
73
-
74
-	delete(r.names, name)
75
-
76
-	if len(r.idx[key]) == 0 {
77
-		delete(r.idx, key)
78
-	}
79
-}
80
-
81
-// Delete removes all reservations for the passed in key.
82
-// All names reserved to this key are released.
83
-func (r *Registrar) Delete(key string) {
84
-	r.mu.Lock()
85
-	for _, name := range r.idx[key] {
86
-		delete(r.names, name)
87
-	}
88
-	delete(r.idx, key)
89
-	r.mu.Unlock()
90
-}
91
-
92
-// GetNames lists all the reserved names for the given key
93
-func (r *Registrar) GetNames(key string) ([]string, error) {
94
-	r.mu.Lock()
95
-	defer r.mu.Unlock()
96
-
97
-	names, exists := r.idx[key]
98
-	if !exists {
99
-		return nil, ErrNoSuchKey
100
-	}
101
-
102
-	ls := make([]string, 0, len(names))
103
-	ls = append(ls, names...)
104
-	return ls, nil
105
-}
106
-
107
-// Get returns the key that the passed in name is reserved to
108
-func (r *Registrar) Get(name string) (string, error) {
109
-	r.mu.Lock()
110
-	key, exists := r.names[name]
111
-	r.mu.Unlock()
112
-
113
-	if !exists {
114
-		return "", ErrNameNotReserved
115
-	}
116
-	return key, nil
117
-}
118
-
119
-// GetAll returns all registered names
120
-func (r *Registrar) GetAll() map[string][]string {
121
-	out := make(map[string][]string)
122
-
123
-	r.mu.Lock()
124
-	// copy index into out
125
-	for id, names := range r.idx {
126
-		out[id] = names
127
-	}
128
-	r.mu.Unlock()
129
-	return out
130
-}
131 1
deleted file mode 100644
... ...
@@ -1,119 +0,0 @@
1
-package registrar
2
-
3
-import (
4
-	"reflect"
5
-	"testing"
6
-)
7
-
8
-func TestReserve(t *testing.T) {
9
-	r := NewRegistrar()
10
-
11
-	obj := "test1"
12
-	if err := r.Reserve("test", obj); err != nil {
13
-		t.Fatal(err)
14
-	}
15
-
16
-	if err := r.Reserve("test", obj); err != nil {
17
-		t.Fatal(err)
18
-	}
19
-
20
-	obj2 := "test2"
21
-	err := r.Reserve("test", obj2)
22
-	if err == nil {
23
-		t.Fatalf("expected error when reserving an already reserved name to another object")
24
-	}
25
-	if err != ErrNameReserved {
26
-		t.Fatal("expected `ErrNameReserved` error when attempting to reserve an already reserved name")
27
-	}
28
-}
29
-
30
-func TestRelease(t *testing.T) {
31
-	r := NewRegistrar()
32
-	obj := "testing"
33
-
34
-	if err := r.Reserve("test", obj); err != nil {
35
-		t.Fatal(err)
36
-	}
37
-	r.Release("test")
38
-	r.Release("test") // Ensure there is no panic here
39
-
40
-	if err := r.Reserve("test", obj); err != nil {
41
-		t.Fatal(err)
42
-	}
43
-}
44
-
45
-func TestGetNames(t *testing.T) {
46
-	r := NewRegistrar()
47
-	obj := "testing"
48
-	names := []string{"test1", "test2"}
49
-
50
-	for _, name := range names {
51
-		if err := r.Reserve(name, obj); err != nil {
52
-			t.Fatal(err)
53
-		}
54
-	}
55
-	r.Reserve("test3", "other")
56
-
57
-	names2, err := r.GetNames(obj)
58
-	if err != nil {
59
-		t.Fatal(err)
60
-	}
61
-
62
-	if !reflect.DeepEqual(names, names2) {
63
-		t.Fatalf("Expected: %v, Got: %v", names, names2)
64
-	}
65
-}
66
-
67
-func TestDelete(t *testing.T) {
68
-	r := NewRegistrar()
69
-	obj := "testing"
70
-	names := []string{"test1", "test2"}
71
-	for _, name := range names {
72
-		if err := r.Reserve(name, obj); err != nil {
73
-			t.Fatal(err)
74
-		}
75
-	}
76
-
77
-	r.Reserve("test3", "other")
78
-	r.Delete(obj)
79
-
80
-	_, err := r.GetNames(obj)
81
-	if err == nil {
82
-		t.Fatal("expected error getting names for deleted key")
83
-	}
84
-
85
-	if err != ErrNoSuchKey {
86
-		t.Fatal("expected `ErrNoSuchKey`")
87
-	}
88
-}
89
-
90
-func TestGet(t *testing.T) {
91
-	r := NewRegistrar()
92
-	obj := "testing"
93
-	name := "test"
94
-
95
-	_, err := r.Get(name)
96
-	if err == nil {
97
-		t.Fatal("expected error when key does not exist")
98
-	}
99
-	if err != ErrNameNotReserved {
100
-		t.Fatal(err)
101
-	}
102
-
103
-	if err := r.Reserve(name, obj); err != nil {
104
-		t.Fatal(err)
105
-	}
106
-
107
-	if _, err = r.Get(name); err != nil {
108
-		t.Fatal(err)
109
-	}
110
-
111
-	r.Delete(obj)
112
-	_, err = r.Get(name)
113
-	if err == nil {
114
-		t.Fatal("expected error when key does not exist")
115
-	}
116
-	if err != ErrNameNotReserved {
117
-		t.Fatal(err)
118
-	}
119
-}