Skip to content

Commit 3c82fad

Browse files
committedJan 19, 2016
Extract container store from the daemon.
- Generalize in an interface. - Stop abusing of List for everything. Signed-off-by: David Calavera <[email protected]>
1 parent 402ba93 commit 3c82fad

10 files changed

+289
-120
lines changed
 
+10-9
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
1-
package daemon
1+
package container
22

3-
import (
4-
"sort"
5-
6-
"github.com/docker/docker/container"
7-
)
3+
import "sort"
84

95
// History is a convenience type for storing a list of containers,
10-
// ordered by creation date.
11-
type History []*container.Container
6+
// sorted by creation date in descendant order.
7+
type History []*Container
128

9+
// Len returns the number of containers in the history.
1310
func (history *History) Len() int {
1411
return len(*history)
1512
}
1613

14+
// Less compares two containers and returns true if the second one
15+
// was created before the first one.
1716
func (history *History) Less(i, j int) bool {
1817
containers := *history
1918
return containers[j].Created.Before(containers[i].Created)
2019
}
2120

21+
// Swap switches containers i and j positions in the history.
2222
func (history *History) Swap(i, j int) {
2323
containers := *history
2424
containers[i], containers[j] = containers[j], containers[i]
2525
}
2626

2727
// Add the given container to history.
28-
func (history *History) Add(container *container.Container) {
28+
func (history *History) Add(container *Container) {
2929
*history = append(*history, container)
3030
}
3131

32+
// sort orders the history by creation date in descendant order.
3233
func (history *History) sort() {
3334
sort.Sort(history)
3435
}

‎container/memory_store.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package container
2+
3+
import "sync"
4+
5+
// memoryStore implements a Store in memory.
6+
type memoryStore struct {
7+
s map[string]*Container
8+
sync.Mutex
9+
}
10+
11+
// NewMemoryStore initializes a new memory store.
12+
func NewMemoryStore() Store {
13+
return &memoryStore{
14+
s: make(map[string]*Container),
15+
}
16+
}
17+
18+
// Add appends a new container to the memory store.
19+
// It overrides the id if it existed before.
20+
func (c *memoryStore) Add(id string, cont *Container) {
21+
c.Lock()
22+
c.s[id] = cont
23+
c.Unlock()
24+
}
25+
26+
// Get returns a container from the store by id.
27+
func (c *memoryStore) Get(id string) *Container {
28+
c.Lock()
29+
res := c.s[id]
30+
c.Unlock()
31+
return res
32+
}
33+
34+
// Delete removes a container from the store by id.
35+
func (c *memoryStore) Delete(id string) {
36+
c.Lock()
37+
delete(c.s, id)
38+
c.Unlock()
39+
}
40+
41+
// List returns a sorted list of containers from the store.
42+
// The containers are ordered by creation date.
43+
func (c *memoryStore) List() []*Container {
44+
containers := new(History)
45+
c.Lock()
46+
for _, cont := range c.s {
47+
containers.Add(cont)
48+
}
49+
c.Unlock()
50+
containers.sort()
51+
return *containers
52+
}
53+
54+
// Size returns the number of containers in the store.
55+
func (c *memoryStore) Size() int {
56+
c.Lock()
57+
defer c.Unlock()
58+
return len(c.s)
59+
}
60+
61+
// First returns the first container found in the store by a given filter.
62+
func (c *memoryStore) First(filter StoreFilter) *Container {
63+
c.Lock()
64+
defer c.Unlock()
65+
for _, cont := range c.s {
66+
if filter(cont) {
67+
return cont
68+
}
69+
}
70+
return nil
71+
}
72+
73+
// ApplyAll calls the reducer function with every container in the store.
74+
// This operation is asyncronous in the memory store.
75+
func (c *memoryStore) ApplyAll(apply StoreReducer) {
76+
c.Lock()
77+
defer c.Unlock()
78+
79+
wg := new(sync.WaitGroup)
80+
for _, cont := range c.s {
81+
wg.Add(1)
82+
go func(container *Container) {
83+
apply(container)
84+
wg.Done()
85+
}(cont)
86+
}
87+
88+
wg.Wait()
89+
}
90+
91+
var _ Store = &memoryStore{}

‎container/memory_store_test.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package container
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestNewMemoryStore(t *testing.T) {
9+
s := NewMemoryStore()
10+
m, ok := s.(*memoryStore)
11+
if !ok {
12+
t.Fatalf("store is not a memory store %v", s)
13+
}
14+
if m.s == nil {
15+
t.Fatal("expected store map to not be nil")
16+
}
17+
}
18+
19+
func TestAddContainers(t *testing.T) {
20+
s := NewMemoryStore()
21+
s.Add("id", NewBaseContainer("id", "root"))
22+
if s.Size() != 1 {
23+
t.Fatalf("expected store size 1, got %v", s.Size())
24+
}
25+
}
26+
27+
func TestGetContainer(t *testing.T) {
28+
s := NewMemoryStore()
29+
s.Add("id", NewBaseContainer("id", "root"))
30+
c := s.Get("id")
31+
if c == nil {
32+
t.Fatal("expected container to not be nil")
33+
}
34+
}
35+
36+
func TestDeleteContainer(t *testing.T) {
37+
s := NewMemoryStore()
38+
s.Add("id", NewBaseContainer("id", "root"))
39+
s.Delete("id")
40+
if c := s.Get("id"); c != nil {
41+
t.Fatalf("expected container to be nil after removal, got %v", c)
42+
}
43+
44+
if s.Size() != 0 {
45+
t.Fatalf("expected store size to be 0, got %v", s.Size())
46+
}
47+
}
48+
49+
func TestListContainers(t *testing.T) {
50+
s := NewMemoryStore()
51+
52+
cont := NewBaseContainer("id", "root")
53+
cont.Created = time.Now()
54+
cont2 := NewBaseContainer("id2", "root")
55+
cont2.Created = time.Now().Add(24 * time.Hour)
56+
57+
s.Add("id", cont)
58+
s.Add("id2", cont2)
59+
60+
list := s.List()
61+
if len(list) != 2 {
62+
t.Fatalf("expected list size 2, got %v", len(list))
63+
}
64+
if list[0].ID != "id2" {
65+
t.Fatalf("expected older container to be first, got %v", list[0].ID)
66+
}
67+
}
68+
69+
func TestFirstContainer(t *testing.T) {
70+
s := NewMemoryStore()
71+
72+
s.Add("id", NewBaseContainer("id", "root"))
73+
s.Add("id2", NewBaseContainer("id2", "root"))
74+
75+
first := s.First(func(cont *Container) bool {
76+
return cont.ID == "id2"
77+
})
78+
79+
if first == nil {
80+
t.Fatal("expected container to not be nil")
81+
}
82+
if first.ID != "id2" {
83+
t.Fatalf("expected id2, got %v", first)
84+
}
85+
}
86+
87+
func TestApplyAllContainer(t *testing.T) {
88+
s := NewMemoryStore()
89+
90+
s.Add("id", NewBaseContainer("id", "root"))
91+
s.Add("id2", NewBaseContainer("id2", "root"))
92+
93+
s.ApplyAll(func(cont *Container) {
94+
if cont.ID == "id2" {
95+
cont.ID = "newID"
96+
}
97+
})
98+
99+
cont := s.Get("id2")
100+
if cont == nil {
101+
t.Fatal("expected container to not be nil")
102+
}
103+
if cont.ID != "newID" {
104+
t.Fatalf("expected newID, got %v", cont)
105+
}
106+
}

‎container/store.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package container
2+
3+
// StoreFilter defines a function to filter
4+
// container in the store.
5+
type StoreFilter func(*Container) bool
6+
7+
// StoreReducer defines a function to
8+
// manipulate containers in the store
9+
type StoreReducer func(*Container)
10+
11+
// Store defines an interface that
12+
// any container store must implement.
13+
type Store interface {
14+
// Add appends a new container to the store.
15+
Add(string, *Container)
16+
// Get returns a container from the store by the identifier it was stored with.
17+
Get(string) *Container
18+
// Delete removes a container from the store by the identifier it was stored with.
19+
Delete(string)
20+
// List returns a list of containers from the store.
21+
List() []*Container
22+
// Size returns the number of containers in the store.
23+
Size() int
24+
// First returns the first container found in the store by a given filter.
25+
First(StoreFilter) *Container
26+
// ApplyAll calls the reducer function with every container in the store.
27+
ApplyAll(StoreReducer)
28+
}

‎daemon/daemon.go

+12-53
Original file line numberDiff line numberDiff line change
@@ -99,46 +99,11 @@ func (e ErrImageDoesNotExist) Error() string {
9999
return fmt.Sprintf("no such id: %s", e.RefOrID)
100100
}
101101

102-
type contStore struct {
103-
s map[string]*container.Container
104-
sync.Mutex
105-
}
106-
107-
func (c *contStore) Add(id string, cont *container.Container) {
108-
c.Lock()
109-
c.s[id] = cont
110-
c.Unlock()
111-
}
112-
113-
func (c *contStore) Get(id string) *container.Container {
114-
c.Lock()
115-
res := c.s[id]
116-
c.Unlock()
117-
return res
118-
}
119-
120-
func (c *contStore) Delete(id string) {
121-
c.Lock()
122-
delete(c.s, id)
123-
c.Unlock()
124-
}
125-
126-
func (c *contStore) List() []*container.Container {
127-
containers := new(History)
128-
c.Lock()
129-
for _, cont := range c.s {
130-
containers.Add(cont)
131-
}
132-
c.Unlock()
133-
containers.sort()
134-
return *containers
135-
}
136-
137102
// Daemon holds information about the Docker daemon.
138103
type Daemon struct {
139104
ID string
140105
repository string
141-
containers *contStore
106+
containers container.Store
142107
execCommands *exec.Store
143108
referenceStore reference.Store
144109
downloadManager *xfer.LayerDownloadManager
@@ -794,7 +759,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
794759

795760
d.ID = trustKey.PublicKey().KeyID()
796761
d.repository = daemonRepo
797-
d.containers = &contStore{s: make(map[string]*container.Container)}
762+
d.containers = container.NewMemoryStore()
798763
d.execCommands = exec.NewStore()
799764
d.referenceStore = referenceStore
800765
d.distributionMetadataStore = distributionMetadataStore
@@ -873,24 +838,18 @@ func (daemon *Daemon) shutdownContainer(c *container.Container) error {
873838
func (daemon *Daemon) Shutdown() error {
874839
daemon.shutdown = true
875840
if daemon.containers != nil {
876-
group := sync.WaitGroup{}
877841
logrus.Debug("starting clean shutdown of all containers...")
878-
for _, cont := range daemon.List() {
879-
if !cont.IsRunning() {
880-
continue
842+
daemon.containers.ApplyAll(func(c *container.Container) {
843+
if !c.IsRunning() {
844+
return
881845
}
882-
logrus.Debugf("stopping %s", cont.ID)
883-
group.Add(1)
884-
go func(c *container.Container) {
885-
defer group.Done()
886-
if err := daemon.shutdownContainer(c); err != nil {
887-
logrus.Errorf("Stop container error: %v", err)
888-
return
889-
}
890-
logrus.Debugf("container stopped %s", c.ID)
891-
}(cont)
892-
}
893-
group.Wait()
846+
logrus.Debugf("stopping %s", c.ID)
847+
if err := daemon.shutdownContainer(c); err != nil {
848+
logrus.Errorf("Stop container error: %v", err)
849+
return
850+
}
851+
logrus.Debugf("container stopped %s", c.ID)
852+
})
894853
}
895854

896855
// trigger libnetwork Stop only if it's initialized

‎daemon/daemon_test.go

+6-9
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,12 @@ func TestGetContainer(t *testing.T) {
6161
},
6262
}
6363

64-
store := &contStore{
65-
s: map[string]*container.Container{
66-
c1.ID: c1,
67-
c2.ID: c2,
68-
c3.ID: c3,
69-
c4.ID: c4,
70-
c5.ID: c5,
71-
},
72-
}
64+
store := container.NewMemoryStore()
65+
store.Add(c1.ID, c1)
66+
store.Add(c2.ID, c2)
67+
store.Add(c3.ID, c3)
68+
store.Add(c4.ID, c4)
69+
store.Add(c5.ID, c5)
7370

7471
index := truncindex.NewTruncIndex([]string{})
7572
index.Add(c1.ID)

‎daemon/delete_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestContainerDoubleDelete(t *testing.T) {
2020
repository: tmp,
2121
root: tmp,
2222
}
23-
daemon.containers = &contStore{s: make(map[string]*container.Container)}
23+
daemon.containers = container.NewMemoryStore()
2424

2525
container := &container.Container{
2626
CommonContainer: container.CommonContainer{

‎daemon/image_delete.go

+20-32
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,9 @@ func isImageIDPrefix(imageID, possiblePrefix string) bool {
179179
// getContainerUsingImage returns a container that was created using the given
180180
// imageID. Returns nil if there is no such container.
181181
func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Container {
182-
for _, container := range daemon.List() {
183-
if container.ImageID == imageID {
184-
return container
185-
}
186-
}
187-
188-
return nil
182+
return daemon.containers.First(func(c *container.Container) bool {
183+
return c.ImageID == imageID
184+
})
189185
}
190186

191187
// removeImageRef attempts to parse and remove the given image reference from
@@ -328,19 +324,15 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType
328324

329325
if mask&conflictRunningContainer != 0 {
330326
// Check if any running container is using the image.
331-
for _, container := range daemon.List() {
332-
if !container.IsRunning() {
333-
// Skip this until we check for soft conflicts later.
334-
continue
335-
}
336-
337-
if container.ImageID == imgID {
338-
return &imageDeleteConflict{
339-
imgID: imgID,
340-
hard: true,
341-
used: true,
342-
message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
343-
}
327+
running := func(c *container.Container) bool {
328+
return c.IsRunning() && c.ImageID == imgID
329+
}
330+
if container := daemon.containers.First(running); container != nil {
331+
return &imageDeleteConflict{
332+
imgID: imgID,
333+
hard: true,
334+
used: true,
335+
message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
344336
}
345337
}
346338
}
@@ -355,18 +347,14 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType
355347

356348
if mask&conflictStoppedContainer != 0 {
357349
// Check if any stopped containers reference this image.
358-
for _, container := range daemon.List() {
359-
if container.IsRunning() {
360-
// Skip this as it was checked above in hard conflict conditions.
361-
continue
362-
}
363-
364-
if container.ImageID == imgID {
365-
return &imageDeleteConflict{
366-
imgID: imgID,
367-
used: true,
368-
message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
369-
}
350+
stopped := func(c *container.Container) bool {
351+
return !c.IsRunning() && c.ImageID == imgID
352+
}
353+
if container := daemon.containers.First(stopped); container != nil {
354+
return &imageDeleteConflict{
355+
imgID: imgID,
356+
used: true,
357+
message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
370358
}
371359
}
372360
}

‎daemon/info.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import (
44
"os"
55
"runtime"
66
"strings"
7+
"sync/atomic"
78
"time"
89

910
"github.com/Sirupsen/logrus"
11+
"github.com/docker/docker/container"
1012
"github.com/docker/docker/dockerversion"
1113
"github.com/docker/docker/pkg/fileutils"
1214
"github.com/docker/docker/pkg/parsers/kernel"
@@ -54,24 +56,24 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
5456
initPath := utils.DockerInitPath("")
5557
sysInfo := sysinfo.New(true)
5658

57-
var cRunning, cPaused, cStopped int
58-
for _, c := range daemon.List() {
59+
var cRunning, cPaused, cStopped int32
60+
daemon.containers.ApplyAll(func(c *container.Container) {
5961
switch c.StateString() {
6062
case "paused":
61-
cPaused++
63+
atomic.AddInt32(&cPaused, 1)
6264
case "running":
63-
cRunning++
65+
atomic.AddInt32(&cRunning, 1)
6466
default:
65-
cStopped++
67+
atomic.AddInt32(&cStopped, 1)
6668
}
67-
}
69+
})
6870

6971
v := &types.Info{
7072
ID: daemon.ID,
71-
Containers: len(daemon.List()),
72-
ContainersRunning: cRunning,
73-
ContainersPaused: cPaused,
74-
ContainersStopped: cStopped,
73+
Containers: int(cRunning + cPaused + cStopped),
74+
ContainersRunning: int(cRunning),
75+
ContainersPaused: int(cPaused),
76+
ContainersStopped: int(cStopped),
7577
Images: len(daemon.imageStore.Map()),
7678
Driver: daemon.GraphDriverName(),
7779
DriverStatus: daemon.layerStore.DriverStatus(),

‎daemon/links_test.go

+3-6
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,9 @@ func TestMigrateLegacySqliteLinks(t *testing.T) {
3939
},
4040
}
4141

42-
store := &contStore{
43-
s: map[string]*container.Container{
44-
c1.ID: c1,
45-
c2.ID: c2,
46-
},
47-
}
42+
store := container.NewMemoryStore()
43+
store.Add(c1.ID, c1)
44+
store.Add(c2.ID, c2)
4845

4946
d := &Daemon{root: tmpDir, containers: store}
5047
db, err := graphdb.NewSqliteConn(filepath.Join(d.root, "linkgraph.db"))

0 commit comments

Comments
 (0)
Please sign in to comment.