Don't rely on sqlite db for name registration and linking.
Instead register names and links when the daemon starts to an in-memory
store.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -37,40 +37,36 @@ import ( |
| 37 | 37 |
|
| 38 | 38 |
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
|
| 39 | 39 |
var env []string |
| 40 |
- children, err := daemon.children(container.Name) |
|
| 41 |
- if err != nil {
|
|
| 42 |
- return nil, err |
|
| 43 |
- } |
|
| 40 |
+ children := daemon.children(container) |
|
| 44 | 41 |
|
| 45 | 42 |
bridgeSettings := container.NetworkSettings.Networks["bridge"] |
| 46 | 43 |
if bridgeSettings == nil {
|
| 47 | 44 |
return nil, nil |
| 48 | 45 |
} |
| 49 | 46 |
|
| 50 |
- if len(children) > 0 {
|
|
| 51 |
- for linkAlias, child := range children {
|
|
| 52 |
- if !child.IsRunning() {
|
|
| 53 |
- return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias) |
|
| 54 |
- } |
|
| 47 |
+ for linkAlias, child := range children {
|
|
| 48 |
+ if !child.IsRunning() {
|
|
| 49 |
+ return nil, derr.ErrorCodeLinkNotRunning.WithArgs(child.Name, linkAlias) |
|
| 50 |
+ } |
|
| 55 | 51 |
|
| 56 |
- childBridgeSettings := child.NetworkSettings.Networks["bridge"] |
|
| 57 |
- if childBridgeSettings == nil {
|
|
| 58 |
- return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID)
|
|
| 59 |
- } |
|
| 52 |
+ childBridgeSettings := child.NetworkSettings.Networks["bridge"] |
|
| 53 |
+ if childBridgeSettings == nil {
|
|
| 54 |
+ return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID)
|
|
| 55 |
+ } |
|
| 60 | 56 |
|
| 61 |
- link := links.NewLink( |
|
| 62 |
- bridgeSettings.IPAddress, |
|
| 63 |
- childBridgeSettings.IPAddress, |
|
| 64 |
- linkAlias, |
|
| 65 |
- child.Config.Env, |
|
| 66 |
- child.Config.ExposedPorts, |
|
| 67 |
- ) |
|
| 57 |
+ link := links.NewLink( |
|
| 58 |
+ bridgeSettings.IPAddress, |
|
| 59 |
+ childBridgeSettings.IPAddress, |
|
| 60 |
+ linkAlias, |
|
| 61 |
+ child.Config.Env, |
|
| 62 |
+ child.Config.ExposedPorts, |
|
| 63 |
+ ) |
|
| 68 | 64 |
|
| 69 |
- for _, envVar := range link.ToEnv() {
|
|
| 70 |
- env = append(env, envVar) |
|
| 71 |
- } |
|
| 65 |
+ for _, envVar := range link.ToEnv() {
|
|
| 66 |
+ env = append(env, envVar) |
|
| 72 | 67 |
} |
| 73 | 68 |
} |
| 69 |
+ |
|
| 74 | 70 |
return env, nil |
| 75 | 71 |
} |
| 76 | 72 |
|
| ... | ... |
@@ -419,11 +415,7 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn |
| 419 | 419 |
|
| 420 | 420 |
var childEndpoints, parentEndpoints []string |
| 421 | 421 |
|
| 422 |
- children, err := daemon.children(container.Name) |
|
| 423 |
- if err != nil {
|
|
| 424 |
- return nil, err |
|
| 425 |
- } |
|
| 426 |
- |
|
| 422 |
+ children := daemon.children(container) |
|
| 427 | 423 |
for linkAlias, child := range children {
|
| 428 | 424 |
if !isLinkable(child) {
|
| 429 | 425 |
return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name)
|
| ... | ... |
@@ -443,23 +435,20 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn |
| 443 | 443 |
} |
| 444 | 444 |
|
| 445 | 445 |
bridgeSettings := container.NetworkSettings.Networks["bridge"] |
| 446 |
- refs := daemon.containerGraph().RefPaths(container.ID) |
|
| 447 |
- for _, ref := range refs {
|
|
| 448 |
- if ref.ParentID == "0" {
|
|
| 446 |
+ for alias, parent := range daemon.parents(container) {
|
|
| 447 |
+ if daemon.configStore.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() {
|
|
| 449 | 448 |
continue |
| 450 | 449 |
} |
| 451 | 450 |
|
| 452 |
- c, err := daemon.GetContainer(ref.ParentID) |
|
| 453 |
- if err != nil {
|
|
| 454 |
- logrus.Error(err) |
|
| 455 |
- } |
|
| 456 |
- |
|
| 457 |
- if c != nil && !daemon.configStore.DisableBridge && container.HostConfig.NetworkMode.IsPrivate() {
|
|
| 458 |
- logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, bridgeSettings.IPAddress)
|
|
| 459 |
- sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, bridgeSettings.IPAddress)) |
|
| 460 |
- if ep.ID() != "" {
|
|
| 461 |
- parentEndpoints = append(parentEndpoints, ep.ID()) |
|
| 462 |
- } |
|
| 451 |
+ _, alias = path.Split(alias) |
|
| 452 |
+ logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", parent.ID, alias, bridgeSettings.IPAddress)
|
|
| 453 |
+ sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate( |
|
| 454 |
+ parent.ID, |
|
| 455 |
+ alias, |
|
| 456 |
+ bridgeSettings.IPAddress, |
|
| 457 |
+ )) |
|
| 458 |
+ if ep.ID() != "" {
|
|
| 459 |
+ parentEndpoints = append(parentEndpoints, ep.ID()) |
|
| 463 | 460 |
} |
| 464 | 461 |
} |
| 465 | 462 |
|
| ... | ... |
@@ -471,7 +460,6 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libn |
| 471 | 471 |
} |
| 472 | 472 |
|
| 473 | 473 |
sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) |
| 474 |
- |
|
| 475 | 474 |
return sboxOptions, nil |
| 476 | 475 |
} |
| 477 | 476 |
|
| ... | ... |
@@ -12,9 +12,9 @@ import ( |
| 12 | 12 |
"io/ioutil" |
| 13 | 13 |
"net" |
| 14 | 14 |
"os" |
| 15 |
+ "path" |
|
| 15 | 16 |
"path/filepath" |
| 16 | 17 |
"runtime" |
| 17 |
- "strings" |
|
| 18 | 18 |
"sync" |
| 19 | 19 |
"syscall" |
| 20 | 20 |
"time" |
| ... | ... |
@@ -48,11 +48,11 @@ import ( |
| 48 | 48 |
"github.com/docker/docker/pkg/archive" |
| 49 | 49 |
"github.com/docker/docker/pkg/discovery" |
| 50 | 50 |
"github.com/docker/docker/pkg/fileutils" |
| 51 |
- "github.com/docker/docker/pkg/graphdb" |
|
| 52 | 51 |
"github.com/docker/docker/pkg/idtools" |
| 53 | 52 |
"github.com/docker/docker/pkg/mount" |
| 54 | 53 |
"github.com/docker/docker/pkg/namesgenerator" |
| 55 | 54 |
"github.com/docker/docker/pkg/progress" |
| 55 |
+ "github.com/docker/docker/pkg/registrar" |
|
| 56 | 56 |
"github.com/docker/docker/pkg/signal" |
| 57 | 57 |
"github.com/docker/docker/pkg/streamformatter" |
| 58 | 58 |
"github.com/docker/docker/pkg/stringid" |
| ... | ... |
@@ -147,7 +147,6 @@ type Daemon struct {
|
| 147 | 147 |
trustKey libtrust.PrivateKey |
| 148 | 148 |
idIndex *truncindex.TruncIndex |
| 149 | 149 |
configStore *Config |
| 150 |
- containerGraphDB *graphdb.Database |
|
| 151 | 150 |
execDriver execdriver.Driver |
| 152 | 151 |
statsCollector *statsCollector |
| 153 | 152 |
defaultLogConfig containertypes.LogConfig |
| ... | ... |
@@ -162,6 +161,8 @@ type Daemon struct {
|
| 162 | 162 |
gidMaps []idtools.IDMap |
| 163 | 163 |
layerStore layer.Store |
| 164 | 164 |
imageStore image.Store |
| 165 |
+ nameIndex *registrar.Registrar |
|
| 166 |
+ linkIndex *linkIndex |
|
| 165 | 167 |
} |
| 166 | 168 |
|
| 167 | 169 |
// GetContainer looks for a container using the provided information, which could be |
| ... | ... |
@@ -245,8 +246,7 @@ func (daemon *Daemon) registerName(container *container.Container) error {
|
| 245 | 245 |
logrus.Errorf("Error saving container name to disk: %v", err)
|
| 246 | 246 |
} |
| 247 | 247 |
} |
| 248 |
- |
|
| 249 |
- return nil |
|
| 248 |
+ return daemon.nameIndex.Reserve(container.Name, container.ID) |
|
| 250 | 249 |
} |
| 251 | 250 |
|
| 252 | 251 |
// Register makes a container object usable by the daemon as <container.ID> |
| ... | ... |
@@ -257,10 +257,8 @@ func (daemon *Daemon) Register(container *container.Container) error {
|
| 257 | 257 |
} else {
|
| 258 | 258 |
container.NewNopInputPipe() |
| 259 | 259 |
} |
| 260 |
- daemon.containers.Add(container.ID, container) |
|
| 261 | 260 |
|
| 262 |
- // don't update the Suffixarray if we're starting up |
|
| 263 |
- // we'll waste time if we update it for every container |
|
| 261 |
+ daemon.containers.Add(container.ID, container) |
|
| 264 | 262 |
daemon.idIndex.Add(container.ID) |
| 265 | 263 |
|
| 266 | 264 |
if container.IsRunning() {
|
| ... | ... |
@@ -291,15 +289,10 @@ func (daemon *Daemon) Register(container *container.Container) error {
|
| 291 | 291 |
} |
| 292 | 292 |
|
| 293 | 293 |
func (daemon *Daemon) restore() error {
|
| 294 |
- type cr struct {
|
|
| 295 |
- container *container.Container |
|
| 296 |
- registered bool |
|
| 297 |
- } |
|
| 298 |
- |
|
| 299 | 294 |
var ( |
| 300 | 295 |
debug = os.Getenv("DEBUG") != ""
|
| 301 | 296 |
currentDriver = daemon.GraphDriverName() |
| 302 |
- containers = make(map[string]*cr) |
|
| 297 |
+ containers = make(map[string]*container.Container) |
|
| 303 | 298 |
) |
| 304 | 299 |
|
| 305 | 300 |
if !debug {
|
| ... | ... |
@@ -332,63 +325,48 @@ func (daemon *Daemon) restore() error {
|
| 332 | 332 |
if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver {
|
| 333 | 333 |
logrus.Debugf("Loaded container %v", container.ID)
|
| 334 | 334 |
|
| 335 |
- containers[container.ID] = &cr{container: container}
|
|
| 335 |
+ containers[container.ID] = container |
|
| 336 | 336 |
} else {
|
| 337 | 337 |
logrus.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID)
|
| 338 | 338 |
} |
| 339 | 339 |
} |
| 340 | 340 |
|
| 341 |
- if entities := daemon.containerGraphDB.List("/", -1); entities != nil {
|
|
| 342 |
- for _, p := range entities.Paths() {
|
|
| 343 |
- if !debug && logrus.GetLevel() == logrus.InfoLevel {
|
|
| 344 |
- fmt.Print(".")
|
|
| 345 |
- } |
|
| 346 |
- |
|
| 347 |
- e := entities[p] |
|
| 348 |
- |
|
| 349 |
- if c, ok := containers[e.ID()]; ok {
|
|
| 350 |
- c.registered = true |
|
| 351 |
- } |
|
| 352 |
- } |
|
| 353 |
- } |
|
| 354 |
- |
|
| 355 | 341 |
restartContainers := make(map[*container.Container]chan struct{})
|
| 356 | 342 |
for _, c := range containers {
|
| 357 |
- if !c.registered {
|
|
| 358 |
- // Try to set the default name for a container if it exists prior to links |
|
| 359 |
- c.container.Name, err = daemon.generateNewName(c.container.ID) |
|
| 360 |
- if err != nil {
|
|
| 361 |
- logrus.Debugf("Setting default id - %s", err)
|
|
| 362 |
- } |
|
| 363 |
- if err := daemon.registerName(c.container); err != nil {
|
|
| 364 |
- logrus.Errorf("Failed to register container %s: %s", c.container.ID, err)
|
|
| 365 |
- continue |
|
| 366 |
- } |
|
| 343 |
+ if err := daemon.registerName(c); err != nil {
|
|
| 344 |
+ logrus.Errorf("Failed to register container %s: %s", c.ID, err)
|
|
| 345 |
+ continue |
|
| 367 | 346 |
} |
| 368 |
- |
|
| 369 |
- if err := daemon.Register(c.container); err != nil {
|
|
| 370 |
- logrus.Errorf("Failed to register container %s: %s", c.container.ID, err)
|
|
| 347 |
+ if err := daemon.Register(c); err != nil {
|
|
| 348 |
+ logrus.Errorf("Failed to register container %s: %s", c.ID, err)
|
|
| 371 | 349 |
continue |
| 372 | 350 |
} |
| 351 |
+ |
|
| 373 | 352 |
// get list of containers we need to restart |
| 374 |
- if daemon.configStore.AutoRestart && c.container.ShouldRestart() {
|
|
| 375 |
- restartContainers[c.container] = make(chan struct{})
|
|
| 353 |
+ if daemon.configStore.AutoRestart && c.ShouldRestart() {
|
|
| 354 |
+ restartContainers[c] = make(chan struct{})
|
|
| 355 |
+ } |
|
| 356 |
+ } |
|
| 357 |
+ |
|
| 358 |
+ // Now that all the containers are registered, register the links |
|
| 359 |
+ for _, c := range containers {
|
|
| 360 |
+ if err := daemon.registerLinks(c, c.HostConfig); err != nil {
|
|
| 361 |
+ logrus.Errorf("failed to register link for container %s: %v", c.ID, err)
|
|
| 376 | 362 |
} |
| 377 | 363 |
} |
| 378 | 364 |
|
| 379 | 365 |
group := sync.WaitGroup{}
|
| 380 | 366 |
for c, notifier := range restartContainers {
|
| 381 | 367 |
group.Add(1) |
| 382 |
- go func(container *container.Container, chNotify chan struct{}) {
|
|
| 368 |
+ |
|
| 369 |
+ go func(c *container.Container, chNotify chan struct{}) {
|
|
| 383 | 370 |
defer group.Done() |
| 384 |
- logrus.Debugf("Starting container %s", container.ID)
|
|
| 371 |
+ |
|
| 372 |
+ logrus.Debugf("Starting container %s", c.ID)
|
|
| 385 | 373 |
|
| 386 | 374 |
// ignore errors here as this is a best effort to wait for children to be |
| 387 | 375 |
// running before we try to start the container |
| 388 |
- children, err := daemon.children(container.Name) |
|
| 389 |
- if err != nil {
|
|
| 390 |
- logrus.Warnf("error getting children for %s: %v", container.Name, err)
|
|
| 391 |
- } |
|
| 376 |
+ children := daemon.children(c) |
|
| 392 | 377 |
timeout := time.After(5 * time.Second) |
| 393 | 378 |
for _, child := range children {
|
| 394 | 379 |
if notifier, exists := restartContainers[child]; exists {
|
| ... | ... |
@@ -398,11 +376,12 @@ func (daemon *Daemon) restore() error {
|
| 398 | 398 |
} |
| 399 | 399 |
} |
| 400 | 400 |
} |
| 401 |
- if err := daemon.containerStart(container); err != nil {
|
|
| 402 |
- logrus.Errorf("Failed to start container %s: %s", container.ID, err)
|
|
| 401 |
+ if err := daemon.containerStart(c); err != nil {
|
|
| 402 |
+ logrus.Errorf("Failed to start container %s: %s", c.ID, err)
|
|
| 403 | 403 |
} |
| 404 | 404 |
close(chNotify) |
| 405 | 405 |
}(c, notifier) |
| 406 |
+ |
|
| 406 | 407 |
} |
| 407 | 408 |
group.Wait() |
| 408 | 409 |
|
| ... | ... |
@@ -452,28 +431,28 @@ func (daemon *Daemon) reserveName(id, name string) (string, error) {
|
| 452 | 452 |
if !validContainerNamePattern.MatchString(name) {
|
| 453 | 453 |
return "", fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars)
|
| 454 | 454 |
} |
| 455 |
- |
|
| 456 | 455 |
if name[0] != '/' {
|
| 457 | 456 |
name = "/" + name |
| 458 | 457 |
} |
| 459 | 458 |
|
| 460 |
- if _, err := daemon.containerGraphDB.Set(name, id); err != nil {
|
|
| 461 |
- if !graphdb.IsNonUniqueNameError(err) {
|
|
| 462 |
- return "", err |
|
| 463 |
- } |
|
| 464 |
- |
|
| 465 |
- conflictingContainer, err := daemon.GetByName(name) |
|
| 466 |
- if err != nil {
|
|
| 467 |
- return "", err |
|
| 459 |
+ if err := daemon.nameIndex.Reserve(name, id); err != nil {
|
|
| 460 |
+ if err == registrar.ErrNameReserved {
|
|
| 461 |
+ id, err := daemon.nameIndex.Get(name) |
|
| 462 |
+ if err != nil {
|
|
| 463 |
+ logrus.Errorf("got unexpected error while looking up reserved name: %v", err)
|
|
| 464 |
+ return "", err |
|
| 465 |
+ } |
|
| 466 |
+ return "", fmt.Errorf("Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", name, id)
|
|
| 468 | 467 |
} |
| 469 |
- return "", fmt.Errorf( |
|
| 470 |
- "Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", strings.TrimPrefix(name, "/"), |
|
| 471 |
- stringid.TruncateID(conflictingContainer.ID)) |
|
| 472 |
- |
|
| 468 |
+ return "", fmt.Errorf("error reserving name: %s, error: %v", name, err)
|
|
| 473 | 469 |
} |
| 474 | 470 |
return name, nil |
| 475 | 471 |
} |
| 476 | 472 |
|
| 473 |
+func (daemon *Daemon) releaseName(name string) {
|
|
| 474 |
+ daemon.nameIndex.Release(name) |
|
| 475 |
+} |
|
| 476 |
+ |
|
| 477 | 477 |
func (daemon *Daemon) generateNewName(id string) (string, error) {
|
| 478 | 478 |
var name string |
| 479 | 479 |
for i := 0; i < 6; i++ {
|
| ... | ... |
@@ -482,17 +461,17 @@ func (daemon *Daemon) generateNewName(id string) (string, error) {
|
| 482 | 482 |
name = "/" + name |
| 483 | 483 |
} |
| 484 | 484 |
|
| 485 |
- if _, err := daemon.containerGraphDB.Set(name, id); err != nil {
|
|
| 486 |
- if !graphdb.IsNonUniqueNameError(err) {
|
|
| 487 |
- return "", err |
|
| 485 |
+ if err := daemon.nameIndex.Reserve(name, id); err != nil {
|
|
| 486 |
+ if err == registrar.ErrNameReserved {
|
|
| 487 |
+ continue |
|
| 488 | 488 |
} |
| 489 |
- continue |
|
| 489 |
+ return "", err |
|
| 490 | 490 |
} |
| 491 | 491 |
return name, nil |
| 492 | 492 |
} |
| 493 | 493 |
|
| 494 | 494 |
name = "/" + stringid.TruncateID(id) |
| 495 |
- if _, err := daemon.containerGraphDB.Set(name, id); err != nil {
|
|
| 495 |
+ if err := daemon.nameIndex.Reserve(name, id); err != nil {
|
|
| 496 | 496 |
return "", err |
| 497 | 497 |
} |
| 498 | 498 |
return name, nil |
| ... | ... |
@@ -542,32 +521,19 @@ func (daemon *Daemon) newContainer(name string, config *containertypes.Config, i |
| 542 | 542 |
return base, err |
| 543 | 543 |
} |
| 544 | 544 |
|
| 545 |
-// GetFullContainerName returns a constructed container name. I think |
|
| 546 |
-// it has to do with the fact that a container is a file on disk and |
|
| 547 |
-// this is sort of just creating a file name. |
|
| 548 |
-func GetFullContainerName(name string) (string, error) {
|
|
| 549 |
- if name == "" {
|
|
| 550 |
- return "", fmt.Errorf("Container name cannot be empty")
|
|
| 551 |
- } |
|
| 552 |
- if name[0] != '/' {
|
|
| 553 |
- name = "/" + name |
|
| 554 |
- } |
|
| 555 |
- return name, nil |
|
| 556 |
-} |
|
| 557 |
- |
|
| 558 | 545 |
// GetByName returns a container given a name. |
| 559 | 546 |
func (daemon *Daemon) GetByName(name string) (*container.Container, error) {
|
| 560 |
- fullName, err := GetFullContainerName(name) |
|
| 561 |
- if err != nil {
|
|
| 562 |
- return nil, err |
|
| 547 |
+ fullName := name |
|
| 548 |
+ if name[0] != '/' {
|
|
| 549 |
+ fullName = "/" + name |
|
| 563 | 550 |
} |
| 564 |
- entity := daemon.containerGraphDB.Get(fullName) |
|
| 565 |
- if entity == nil {
|
|
| 551 |
+ id, err := daemon.nameIndex.Get(fullName) |
|
| 552 |
+ if err != nil {
|
|
| 566 | 553 |
return nil, fmt.Errorf("Could not find entity for %s", name)
|
| 567 | 554 |
} |
| 568 |
- e := daemon.containers.Get(entity.ID()) |
|
| 555 |
+ e := daemon.containers.Get(id) |
|
| 569 | 556 |
if e == nil {
|
| 570 |
- return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
|
|
| 557 |
+ return nil, fmt.Errorf("Could not find container for entity id %s", id)
|
|
| 571 | 558 |
} |
| 572 | 559 |
return e, nil |
| 573 | 560 |
} |
| ... | ... |
@@ -584,48 +550,37 @@ func (daemon *Daemon) UnsubscribeFromEvents(listener chan interface{}) {
|
| 584 | 584 |
daemon.EventsService.Evict(listener) |
| 585 | 585 |
} |
| 586 | 586 |
|
| 587 |
-// children returns all child containers of the container with the |
|
| 588 |
-// given name. The containers are returned as a map from the container |
|
| 589 |
-// name to a pointer to Container. |
|
| 590 |
-func (daemon *Daemon) children(name string) (map[string]*container.Container, error) {
|
|
| 591 |
- name, err := GetFullContainerName(name) |
|
| 592 |
- if err != nil {
|
|
| 593 |
- return nil, err |
|
| 587 |
+// GetLabels for a container or image id |
|
| 588 |
+func (daemon *Daemon) GetLabels(id string) map[string]string {
|
|
| 589 |
+ // TODO: TestCase |
|
| 590 |
+ container := daemon.containers.Get(id) |
|
| 591 |
+ if container != nil {
|
|
| 592 |
+ return container.Config.Labels |
|
| 594 | 593 |
} |
| 595 |
- children := make(map[string]*container.Container) |
|
| 596 | 594 |
|
| 597 |
- err = daemon.containerGraphDB.Walk(name, func(p string, e *graphdb.Entity) error {
|
|
| 598 |
- c, err := daemon.GetContainer(e.ID()) |
|
| 599 |
- if err != nil {
|
|
| 600 |
- return err |
|
| 601 |
- } |
|
| 602 |
- children[p] = c |
|
| 603 |
- return nil |
|
| 604 |
- }, 0) |
|
| 605 |
- |
|
| 606 |
- if err != nil {
|
|
| 607 |
- return nil, err |
|
| 595 |
+ img, err := daemon.GetImage(id) |
|
| 596 |
+ if err == nil {
|
|
| 597 |
+ return img.ContainerConfig.Labels |
|
| 608 | 598 |
} |
| 609 |
- return children, nil |
|
| 599 |
+ return nil |
|
| 600 |
+} |
|
| 601 |
+ |
|
| 602 |
+func (daemon *Daemon) children(c *container.Container) map[string]*container.Container {
|
|
| 603 |
+ return daemon.linkIndex.children(c) |
|
| 610 | 604 |
} |
| 611 | 605 |
|
| 612 | 606 |
// parents returns the names of the parent containers of the container |
| 613 | 607 |
// with the given name. |
| 614 |
-func (daemon *Daemon) parents(name string) ([]string, error) {
|
|
| 615 |
- name, err := GetFullContainerName(name) |
|
| 616 |
- if err != nil {
|
|
| 617 |
- return nil, err |
|
| 618 |
- } |
|
| 619 |
- |
|
| 620 |
- return daemon.containerGraphDB.Parents(name) |
|
| 608 |
+func (daemon *Daemon) parents(c *container.Container) map[string]*container.Container {
|
|
| 609 |
+ return daemon.linkIndex.parents(c) |
|
| 621 | 610 |
} |
| 622 | 611 |
|
| 623 | 612 |
func (daemon *Daemon) registerLink(parent, child *container.Container, alias string) error {
|
| 624 |
- fullName := filepath.Join(parent.Name, alias) |
|
| 625 |
- if !daemon.containerGraphDB.Exists(fullName) {
|
|
| 626 |
- _, err := daemon.containerGraphDB.Set(fullName, child.ID) |
|
| 613 |
+ fullName := path.Join(parent.Name, alias) |
|
| 614 |
+ if err := daemon.nameIndex.Reserve(fullName, child.ID); err != nil {
|
|
| 627 | 615 |
return err |
| 628 | 616 |
} |
| 617 |
+ daemon.linkIndex.link(parent, child, fullName) |
|
| 629 | 618 |
return nil |
| 630 | 619 |
} |
| 631 | 620 |
|
| ... | ... |
@@ -813,14 +768,6 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
| 813 | 813 |
return nil, fmt.Errorf("Error initializing network controller: %v", err)
|
| 814 | 814 |
} |
| 815 | 815 |
|
| 816 |
- graphdbPath := filepath.Join(config.Root, "linkgraph.db") |
|
| 817 |
- graph, err := graphdb.NewSqliteConn(graphdbPath) |
|
| 818 |
- if err != nil {
|
|
| 819 |
- return nil, err |
|
| 820 |
- } |
|
| 821 |
- |
|
| 822 |
- d.containerGraphDB = graph |
|
| 823 |
- |
|
| 824 | 816 |
sysInfo := sysinfo.New(false) |
| 825 | 817 |
// Check if Devices cgroup is mounted, it is hard requirement for container security, |
| 826 | 818 |
// on Linux/FreeBSD. |
| ... | ... |
@@ -852,10 +799,12 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
| 852 | 852 |
d.uidMaps = uidMaps |
| 853 | 853 |
d.gidMaps = gidMaps |
| 854 | 854 |
|
| 855 |
+ d.nameIndex = registrar.NewRegistrar() |
|
| 856 |
+ d.linkIndex = newLinkIndex() |
|
| 857 |
+ |
|
| 855 | 858 |
if err := d.cleanupMounts(); err != nil {
|
| 856 | 859 |
return nil, err |
| 857 | 860 |
} |
| 858 |
- |
|
| 859 | 861 |
go d.execCommandGC() |
| 860 | 862 |
|
| 861 | 863 |
if err := d.restore(); err != nil {
|
| ... | ... |
@@ -933,12 +882,6 @@ func (daemon *Daemon) Shutdown() error {
|
| 933 | 933 |
daemon.netController.Stop() |
| 934 | 934 |
} |
| 935 | 935 |
|
| 936 |
- if daemon.containerGraphDB != nil {
|
|
| 937 |
- if err := daemon.containerGraphDB.Close(); err != nil {
|
|
| 938 |
- logrus.Errorf("Error during container graph.Close(): %v", err)
|
|
| 939 |
- } |
|
| 940 |
- } |
|
| 941 |
- |
|
| 942 | 936 |
if daemon.layerStore != nil {
|
| 943 | 937 |
if err := daemon.layerStore.Cleanup(); err != nil {
|
| 944 | 938 |
logrus.Errorf("Error during layer Store.Cleanup(): %v", err)
|
| ... | ... |
@@ -1340,10 +1283,6 @@ func (daemon *Daemon) ExecutionDriver() execdriver.Driver {
|
| 1340 | 1340 |
return daemon.execDriver |
| 1341 | 1341 |
} |
| 1342 | 1342 |
|
| 1343 |
-func (daemon *Daemon) containerGraph() *graphdb.Database {
|
|
| 1344 |
- return daemon.containerGraphDB |
|
| 1345 |
-} |
|
| 1346 |
- |
|
| 1347 | 1343 |
// GetUIDGIDMaps returns the current daemon's user namespace settings |
| 1348 | 1344 |
// for the full uid and gid maps which will be applied to containers |
| 1349 | 1345 |
// started in this instance. |
| ... | ... |
@@ -1421,8 +1360,7 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig * |
| 1421 | 1421 |
} |
| 1422 | 1422 |
|
| 1423 | 1423 |
container.HostConfig = hostConfig |
| 1424 |
- container.ToDisk() |
|
| 1425 |
- return nil |
|
| 1424 |
+ return container.ToDisk() |
|
| 1426 | 1425 |
} |
| 1427 | 1426 |
|
| 1428 | 1427 |
func (daemon *Daemon) setupInitLayer(initPath string) error {
|
| ... | ... |
@@ -3,12 +3,11 @@ package daemon |
| 3 | 3 |
import ( |
| 4 | 4 |
"io/ioutil" |
| 5 | 5 |
"os" |
| 6 |
- "path" |
|
| 7 | 6 |
"path/filepath" |
| 8 | 7 |
"testing" |
| 9 | 8 |
|
| 10 | 9 |
"github.com/docker/docker/container" |
| 11 |
- "github.com/docker/docker/pkg/graphdb" |
|
| 10 |
+ "github.com/docker/docker/pkg/registrar" |
|
| 12 | 11 |
"github.com/docker/docker/pkg/truncindex" |
| 13 | 12 |
"github.com/docker/docker/volume" |
| 14 | 13 |
volumedrivers "github.com/docker/docker/volume/drivers" |
| ... | ... |
@@ -75,23 +74,18 @@ func TestGetContainer(t *testing.T) {
|
| 75 | 75 |
index.Add(c4.ID) |
| 76 | 76 |
index.Add(c5.ID) |
| 77 | 77 |
|
| 78 |
- daemonTestDbPath := path.Join(os.TempDir(), "daemon_test.db") |
|
| 79 |
- graph, err := graphdb.NewSqliteConn(daemonTestDbPath) |
|
| 80 |
- if err != nil {
|
|
| 81 |
- t.Fatalf("Failed to create daemon test sqlite database at %s", daemonTestDbPath)
|
|
| 82 |
- } |
|
| 83 |
- graph.Set(c1.Name, c1.ID) |
|
| 84 |
- graph.Set(c2.Name, c2.ID) |
|
| 85 |
- graph.Set(c3.Name, c3.ID) |
|
| 86 |
- graph.Set(c4.Name, c4.ID) |
|
| 87 |
- graph.Set(c5.Name, c5.ID) |
|
| 88 |
- |
|
| 89 | 78 |
daemon := &Daemon{
|
| 90 |
- containers: store, |
|
| 91 |
- idIndex: index, |
|
| 92 |
- containerGraphDB: graph, |
|
| 79 |
+ containers: store, |
|
| 80 |
+ idIndex: index, |
|
| 81 |
+ nameIndex: registrar.NewRegistrar(), |
|
| 93 | 82 |
} |
| 94 | 83 |
|
| 84 |
+ daemon.reserveName(c1.ID, c1.Name) |
|
| 85 |
+ daemon.reserveName(c2.ID, c2.Name) |
|
| 86 |
+ daemon.reserveName(c3.ID, c3.Name) |
|
| 87 |
+ daemon.reserveName(c4.ID, c4.Name) |
|
| 88 |
+ daemon.reserveName(c5.ID, c5.Name) |
|
| 89 |
+ |
|
| 95 | 90 |
if container, _ := daemon.GetContainer("3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de"); container != c2 {
|
| 96 | 91 |
t.Fatal("Should explicitly match full container IDs")
|
| 97 | 92 |
} |
| ... | ... |
@@ -120,8 +114,6 @@ func TestGetContainer(t *testing.T) {
|
| 120 | 120 |
if _, err := daemon.GetContainer("nothing"); err == nil {
|
| 121 | 121 |
t.Fatal("Should return an error when provided a prefix that is neither a name or a partial match to an ID")
|
| 122 | 122 |
} |
| 123 |
- |
|
| 124 |
- os.Remove(daemonTestDbPath) |
|
| 125 | 123 |
} |
| 126 | 124 |
|
| 127 | 125 |
func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
|
| ... | ... |
@@ -206,19 +198,6 @@ func TestNetworkOptions(t *testing.T) {
|
| 206 | 206 |
} |
| 207 | 207 |
} |
| 208 | 208 |
|
| 209 |
-func TestGetFullName(t *testing.T) {
|
|
| 210 |
- name, err := GetFullContainerName("testing")
|
|
| 211 |
- if err != nil {
|
|
| 212 |
- t.Fatal(err) |
|
| 213 |
- } |
|
| 214 |
- if name != "/testing" {
|
|
| 215 |
- t.Fatalf("Expected /testing got %s", name)
|
|
| 216 |
- } |
|
| 217 |
- if _, err := GetFullContainerName(""); err == nil {
|
|
| 218 |
- t.Fatal("Error should not be nil")
|
|
| 219 |
- } |
|
| 220 |
-} |
|
| 221 |
- |
|
| 222 | 209 |
func TestValidContainerNames(t *testing.T) {
|
| 223 | 210 |
invalidNames := []string{"-rm", "&sdfsfd", "safd%sd"}
|
| 224 | 211 |
validNames := []string{"word-word", "word_word", "1weoid"}
|
| ... | ... |
@@ -676,7 +676,7 @@ func setupInitLayer(initLayer string, rootUID, rootGID int) error {
|
| 676 | 676 |
|
| 677 | 677 |
// registerLinks writes the links to a file. |
| 678 | 678 |
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
| 679 |
- if hostConfig == nil || hostConfig.Links == nil {
|
|
| 679 |
+ if hostConfig == nil {
|
|
| 680 | 680 |
return nil |
| 681 | 681 |
} |
| 682 | 682 |
|
| ... | ... |
@@ -707,12 +707,7 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig * |
| 707 | 707 |
|
| 708 | 708 |
// After we load all the links into the daemon |
| 709 | 709 |
// set them to nil on the hostconfig |
| 710 |
- hostConfig.Links = nil |
|
| 711 |
- if err := container.WriteHostConfig(); err != nil {
|
|
| 712 |
- return err |
|
| 713 |
- } |
|
| 714 |
- |
|
| 715 |
- return nil |
|
| 710 |
+ return container.WriteHostConfig() |
|
| 716 | 711 |
} |
| 717 | 712 |
|
| 718 | 713 |
// conditionalMountOnStart is a platform specific helper function during the |
| ... | ... |
@@ -1,8 +1,10 @@ |
| 1 | 1 |
package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"os" |
| 5 | 6 |
"path" |
| 7 |
+ "strings" |
|
| 6 | 8 |
|
| 7 | 9 |
"github.com/Sirupsen/logrus" |
| 8 | 10 |
"github.com/docker/docker/container" |
| ... | ... |
@@ -38,11 +40,10 @@ func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) |
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 | 40 |
if config.RemoveLink {
|
| 41 |
- return daemon.rmLink(name) |
|
| 41 |
+ return daemon.rmLink(container, name) |
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 | 44 |
if err := daemon.cleanupContainer(container, config.ForceRemove); err != nil {
|
| 45 |
- // return derr.ErrorCodeCantDestroy.WithArgs(name, utils.GetErrorMessage(err)) |
|
| 46 | 45 |
return err |
| 47 | 46 |
} |
| 48 | 47 |
|
| ... | ... |
@@ -53,32 +54,29 @@ func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) |
| 53 | 53 |
return nil |
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 |
-// rmLink removes link by name from other containers |
|
| 57 |
-func (daemon *Daemon) rmLink(name string) error {
|
|
| 58 |
- name, err := GetFullContainerName(name) |
|
| 59 |
- if err != nil {
|
|
| 60 |
- return err |
|
| 56 |
+func (daemon *Daemon) rmLink(container *container.Container, name string) error {
|
|
| 57 |
+ if name[0] != '/' {
|
|
| 58 |
+ name = "/" + name |
|
| 61 | 59 |
} |
| 62 | 60 |
parent, n := path.Split(name) |
| 63 | 61 |
if parent == "/" {
|
| 64 |
- return derr.ErrorCodeDefaultName |
|
| 65 |
- } |
|
| 66 |
- pe := daemon.containerGraph().Get(parent) |
|
| 67 |
- if pe == nil {
|
|
| 68 |
- return derr.ErrorCodeNoParent.WithArgs(parent, name) |
|
| 62 |
+ return fmt.Errorf("Conflict, cannot remove the default name of the container")
|
|
| 69 | 63 |
} |
| 70 | 64 |
|
| 71 |
- if err := daemon.containerGraph().Delete(name); err != nil {
|
|
| 72 |
- return err |
|
| 65 |
+ parent = strings.TrimSuffix(parent, "/") |
|
| 66 |
+ pe, err := daemon.nameIndex.Get(parent) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return fmt.Errorf("Cannot get parent %s for name %s", parent, name)
|
|
| 73 | 69 |
} |
| 74 | 70 |
|
| 75 |
- parentContainer, _ := daemon.GetContainer(pe.ID()) |
|
| 71 |
+ daemon.releaseName(name) |
|
| 72 |
+ parentContainer, _ := daemon.GetContainer(pe) |
|
| 76 | 73 |
if parentContainer != nil {
|
| 74 |
+ daemon.linkIndex.unlink(name, container, parentContainer) |
|
| 77 | 75 |
if err := daemon.updateNetwork(parentContainer); err != nil {
|
| 78 | 76 |
logrus.Debugf("Could not update network to remove link %s: %v", n, err)
|
| 79 | 77 |
} |
| 80 | 78 |
} |
| 81 |
- |
|
| 82 | 79 |
return nil |
| 83 | 80 |
} |
| 84 | 81 |
|
| ... | ... |
@@ -116,9 +114,8 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo |
| 116 | 116 |
// indexes even if removal failed. |
| 117 | 117 |
defer func() {
|
| 118 | 118 |
if err == nil || forceRemove {
|
| 119 |
- if _, err := daemon.containerGraphDB.Purge(container.ID); err != nil {
|
|
| 120 |
- logrus.Debugf("Unable to remove container from link graph: %s", err)
|
|
| 121 |
- } |
|
| 119 |
+ daemon.nameIndex.Delete(container.ID) |
|
| 120 |
+ daemon.linkIndex.delete(container) |
|
| 122 | 121 |
selinuxFreeLxcContexts(container.ProcessLabel) |
| 123 | 122 |
daemon.idIndex.Delete(container.ID) |
| 124 | 123 |
daemon.containers.Delete(container.ID) |
| ... | ... |
@@ -139,7 +136,6 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo |
| 139 | 139 |
if err = daemon.execDriver.Clean(container.ID); err != nil {
|
| 140 | 140 |
return derr.ErrorCodeRmExecDriver.WithArgs(container.ID, err) |
| 141 | 141 |
} |
| 142 |
- |
|
| 143 | 142 |
return nil |
| 144 | 143 |
} |
| 145 | 144 |
|
| ... | ... |
@@ -102,11 +102,12 @@ func (daemon *Daemon) getInspectData(container *container.Container, size bool) |
| 102 | 102 |
// make a copy to play with |
| 103 | 103 |
hostConfig := *container.HostConfig |
| 104 | 104 |
|
| 105 |
- if children, err := daemon.children(container.Name); err == nil {
|
|
| 106 |
- for linkAlias, child := range children {
|
|
| 107 |
- hostConfig.Links = append(hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
|
| 108 |
- } |
|
| 105 |
+ children := daemon.children(container) |
|
| 106 |
+ hostConfig.Links = nil // do not expose the internal structure |
|
| 107 |
+ for linkAlias, child := range children {
|
|
| 108 |
+ hostConfig.Links = append(hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
|
| 109 | 109 |
} |
| 110 |
+ |
|
| 110 | 111 |
// we need this trick to preserve empty log driver, so |
| 111 | 112 |
// container will use daemon defaults even if daemon change them |
| 112 | 113 |
if hostConfig.LogConfig.Type == "" {
|
| 113 | 114 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,87 @@ |
| 0 |
+package daemon |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "sync" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/container" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// linkIndex stores link relationships between containers, including their specified alias |
|
| 9 |
+// The alias is the name the parent uses to reference the child |
|
| 10 |
+type linkIndex struct {
|
|
| 11 |
+ // idx maps a parent->alias->child relationship |
|
| 12 |
+ idx map[*container.Container]map[string]*container.Container |
|
| 13 |
+ // childIdx maps child->parent->aliases |
|
| 14 |
+ childIdx map[*container.Container]map[*container.Container]map[string]struct{}
|
|
| 15 |
+ mu sync.Mutex |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func newLinkIndex() *linkIndex {
|
|
| 19 |
+ return &linkIndex{
|
|
| 20 |
+ idx: make(map[*container.Container]map[string]*container.Container), |
|
| 21 |
+ childIdx: make(map[*container.Container]map[*container.Container]map[string]struct{}),
|
|
| 22 |
+ } |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// link adds indexes for the passed in parent/child/alias relationships |
|
| 26 |
+func (l *linkIndex) link(parent, child *container.Container, alias string) {
|
|
| 27 |
+ l.mu.Lock() |
|
| 28 |
+ |
|
| 29 |
+ if l.idx[parent] == nil {
|
|
| 30 |
+ l.idx[parent] = make(map[string]*container.Container) |
|
| 31 |
+ } |
|
| 32 |
+ l.idx[parent][alias] = child |
|
| 33 |
+ if l.childIdx[child] == nil {
|
|
| 34 |
+ l.childIdx[child] = make(map[*container.Container]map[string]struct{})
|
|
| 35 |
+ } |
|
| 36 |
+ if l.childIdx[child][parent] == nil {
|
|
| 37 |
+ l.childIdx[child][parent] = make(map[string]struct{})
|
|
| 38 |
+ } |
|
| 39 |
+ l.childIdx[child][parent][alias] = struct{}{}
|
|
| 40 |
+ |
|
| 41 |
+ l.mu.Unlock() |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// unlink removes the requested alias for the given parent/child |
|
| 45 |
+func (l *linkIndex) unlink(alias string, child, parent *container.Container) {
|
|
| 46 |
+ l.mu.Lock() |
|
| 47 |
+ delete(l.idx[parent], alias) |
|
| 48 |
+ delete(l.childIdx[child], parent) |
|
| 49 |
+ l.mu.Unlock() |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// children maps all the aliases-> children for the passed in parent |
|
| 53 |
+// aliases here are the aliases the parent uses to refer to the child |
|
| 54 |
+func (l *linkIndex) children(parent *container.Container) map[string]*container.Container {
|
|
| 55 |
+ l.mu.Lock() |
|
| 56 |
+ children := l.idx[parent] |
|
| 57 |
+ l.mu.Unlock() |
|
| 58 |
+ return children |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// parents maps all the aliases->parent for the passed in child |
|
| 62 |
+// aliases here are the aliases the parents use to refer to the child |
|
| 63 |
+func (l *linkIndex) parents(child *container.Container) map[string]*container.Container {
|
|
| 64 |
+ l.mu.Lock() |
|
| 65 |
+ |
|
| 66 |
+ parents := make(map[string]*container.Container) |
|
| 67 |
+ for parent, aliases := range l.childIdx[child] {
|
|
| 68 |
+ for alias := range aliases {
|
|
| 69 |
+ parents[alias] = parent |
|
| 70 |
+ } |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ l.mu.Unlock() |
|
| 74 |
+ return parents |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+// delete deletes all link relationships referencing this container |
|
| 78 |
+func (l *linkIndex) delete(container *container.Container) {
|
|
| 79 |
+ l.mu.Lock() |
|
| 80 |
+ for _, child := range l.idx[container] {
|
|
| 81 |
+ delete(l.childIdx[child], container) |
|
| 82 |
+ } |
|
| 83 |
+ delete(l.idx, container) |
|
| 84 |
+ delete(l.childIdx, container) |
|
| 85 |
+ l.mu.Unlock() |
|
| 86 |
+} |
| ... | ... |
@@ -9,7 +9,6 @@ import ( |
| 9 | 9 |
"github.com/Sirupsen/logrus" |
| 10 | 10 |
"github.com/docker/docker/container" |
| 11 | 11 |
"github.com/docker/docker/image" |
| 12 |
- "github.com/docker/docker/pkg/graphdb" |
|
| 13 | 12 |
"github.com/docker/engine-api/types" |
| 14 | 13 |
"github.com/docker/engine-api/types/filters" |
| 15 | 14 |
"github.com/docker/go-connections/nat" |
| ... | ... |
@@ -197,12 +196,6 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) |
| 197 | 197 |
}) |
| 198 | 198 |
} |
| 199 | 199 |
|
| 200 |
- names := make(map[string][]string) |
|
| 201 |
- daemon.containerGraph().Walk("/", func(p string, e *graphdb.Entity) error {
|
|
| 202 |
- names[e.ID()] = append(names[e.ID()], p) |
|
| 203 |
- return nil |
|
| 204 |
- }, 1) |
|
| 205 |
- |
|
| 206 | 200 |
if config.Before != "" && beforeContFilter == nil {
|
| 207 | 201 |
beforeContFilter, err = daemon.GetContainer(config.Before) |
| 208 | 202 |
if err != nil {
|
| ... | ... |
@@ -220,12 +213,12 @@ func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) |
| 220 | 220 |
return &listContext{
|
| 221 | 221 |
filters: psFilters, |
| 222 | 222 |
ancestorFilter: ancestorFilter, |
| 223 |
- names: names, |
|
| 224 | 223 |
images: imagesFilter, |
| 225 | 224 |
exitAllowed: filtExited, |
| 226 | 225 |
beforeFilter: beforeContFilter, |
| 227 | 226 |
sinceFilter: sinceContFilter, |
| 228 | 227 |
ContainersConfig: config, |
| 228 |
+ names: daemon.nameIndex.GetAll(), |
|
| 229 | 229 |
}, nil |
| 230 | 230 |
} |
| 231 | 231 |
|
| ... | ... |
@@ -40,14 +40,11 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
| 40 | 40 |
if err != nil {
|
| 41 | 41 |
container.Name = oldName |
| 42 | 42 |
daemon.reserveName(container.ID, oldName) |
| 43 |
- daemon.containerGraphDB.Delete(newName) |
|
| 43 |
+ daemon.releaseName(newName) |
|
| 44 | 44 |
} |
| 45 | 45 |
}() |
| 46 | 46 |
|
| 47 |
- if err = daemon.containerGraphDB.Delete(oldName); err != nil {
|
|
| 48 |
- return derr.ErrorCodeRenameDelete.WithArgs(oldName, err) |
|
| 49 |
- } |
|
| 50 |
- |
|
| 47 |
+ daemon.releaseName(oldName) |
|
| 51 | 48 |
if err = container.ToDisk(); err != nil {
|
| 52 | 49 |
return err |
| 53 | 50 |
} |
| ... | ... |
@@ -76,7 +73,6 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
|
| 76 | 76 |
if err != nil {
|
| 77 | 77 |
return err |
| 78 | 78 |
} |
| 79 |
- |
|
| 80 | 79 |
daemon.LogContainerEvent(container, "rename") |
| 81 | 80 |
return nil |
| 82 | 81 |
} |
| ... | ... |
@@ -1083,9 +1083,9 @@ func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) {
|
| 1083 | 1083 |
c.Assert(err, checker.IsNil) |
| 1084 | 1084 |
c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers"))
|
| 1085 | 1085 |
|
| 1086 |
- status, _, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil)
|
|
| 1087 |
- c.Assert(err, checker.IsNil) |
|
| 1088 |
- c.Assert(status, checker.Equals, http.StatusNoContent) |
|
| 1086 |
+ status, b, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil)
|
|
| 1087 |
+ c.Assert(err, check.IsNil) |
|
| 1088 |
+ c.Assert(status, check.Equals, http.StatusNoContent, check.Commentf(string(b))) |
|
| 1089 | 1089 |
|
| 1090 | 1090 |
linksPostRm, err := inspectFieldJSON(id2, "HostConfig.Links") |
| 1091 | 1091 |
c.Assert(err, checker.IsNil) |
| ... | ... |
@@ -1960,3 +1960,104 @@ func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *check.C) {
|
| 1960 | 1960 |
} |
| 1961 | 1961 |
c.Assert(found, checker.True, check.Commentf("Cgroup path for container (%s) doesn't found in cgroups file: %s", expectedCgroup, cgroupPaths))
|
| 1962 | 1962 |
} |
| 1963 |
+ |
|
| 1964 |
+func (s *DockerDaemonSuite) TestDaemonRestartWithLinks(c *check.C) {
|
|
| 1965 |
+ testRequires(c, DaemonIsLinux) // Windows does not support links |
|
| 1966 |
+ err := s.d.StartWithBusybox() |
|
| 1967 |
+ c.Assert(err, check.IsNil) |
|
| 1968 |
+ |
|
| 1969 |
+ out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top")
|
|
| 1970 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 1971 |
+ |
|
| 1972 |
+ out, err = s.d.Cmd("run", "--name=test2", "--link", "test:abc", "busybox", "sh", "-c", "ping -c 1 -w 1 abc")
|
|
| 1973 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 1974 |
+ |
|
| 1975 |
+ c.Assert(s.d.Restart(), check.IsNil) |
|
| 1976 |
+ |
|
| 1977 |
+ // should fail since test is not running yet |
|
| 1978 |
+ out, err = s.d.Cmd("start", "test2")
|
|
| 1979 |
+ c.Assert(err, check.NotNil, check.Commentf(out)) |
|
| 1980 |
+ |
|
| 1981 |
+ out, err = s.d.Cmd("start", "test")
|
|
| 1982 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 1983 |
+ out, err = s.d.Cmd("start", "-a", "test2")
|
|
| 1984 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 1985 |
+ c.Assert(strings.Contains(out, "1 packets transmitted, 1 packets received"), check.Equals, true, check.Commentf(out)) |
|
| 1986 |
+} |
|
| 1987 |
+ |
|
| 1988 |
+func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *check.C) {
|
|
| 1989 |
+ testRequires(c, DaemonIsLinux) // Windows does not support links |
|
| 1990 |
+ err := s.d.StartWithBusybox() |
|
| 1991 |
+ c.Assert(err, check.IsNil) |
|
| 1992 |
+ |
|
| 1993 |
+ out, err := s.d.Cmd("create", "--name=test", "busybox")
|
|
| 1994 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 1995 |
+ |
|
| 1996 |
+ out, err = s.d.Cmd("run", "-d", "--name=test2", "busybox", "top")
|
|
| 1997 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 1998 |
+ test2ID := strings.TrimSpace(out) |
|
| 1999 |
+ |
|
| 2000 |
+ out, err = s.d.Cmd("run", "-d", "--name=test3", "--link", "test2:abc", "busybox", "top")
|
|
| 2001 |
+ test3ID := strings.TrimSpace(out) |
|
| 2002 |
+ |
|
| 2003 |
+ c.Assert(s.d.Restart(), check.IsNil) |
|
| 2004 |
+ |
|
| 2005 |
+ out, err = s.d.Cmd("create", "--name=test", "busybox")
|
|
| 2006 |
+ c.Assert(err, check.NotNil, check.Commentf("expected error trying to create container with duplicate name"))
|
|
| 2007 |
+ // this one is no longer needed, removing simplifies the remainder of the test |
|
| 2008 |
+ out, err = s.d.Cmd("rm", "-f", "test")
|
|
| 2009 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 2010 |
+ |
|
| 2011 |
+ out, err = s.d.Cmd("ps", "-a", "--no-trunc")
|
|
| 2012 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 2013 |
+ |
|
| 2014 |
+ lines := strings.Split(strings.TrimSpace(out), "\n")[1:] |
|
| 2015 |
+ |
|
| 2016 |
+ test2validated := false |
|
| 2017 |
+ test3validated := false |
|
| 2018 |
+ for _, line := range lines {
|
|
| 2019 |
+ fields := strings.Fields(line) |
|
| 2020 |
+ names := fields[len(fields)-1] |
|
| 2021 |
+ switch fields[0] {
|
|
| 2022 |
+ case test2ID: |
|
| 2023 |
+ c.Assert(names, check.Equals, "test2,test3/abc") |
|
| 2024 |
+ test2validated = true |
|
| 2025 |
+ case test3ID: |
|
| 2026 |
+ c.Assert(names, check.Equals, "test3") |
|
| 2027 |
+ test3validated = true |
|
| 2028 |
+ } |
|
| 2029 |
+ } |
|
| 2030 |
+ |
|
| 2031 |
+ c.Assert(test2validated, check.Equals, true) |
|
| 2032 |
+ c.Assert(test3validated, check.Equals, true) |
|
| 2033 |
+} |
|
| 2034 |
+ |
|
| 2035 |
+// TestRunLinksChanged checks that creating a new container with the same name does not update links |
|
| 2036 |
+// this ensures that the old, pre gh#16032 functionality continues on |
|
| 2037 |
+func (s *DockerDaemonSuite) TestRunLinksChanged(c *check.C) {
|
|
| 2038 |
+ testRequires(c, DaemonIsLinux) // Windows does not support links |
|
| 2039 |
+ err := s.d.StartWithBusybox() |
|
| 2040 |
+ c.Assert(err, check.IsNil) |
|
| 2041 |
+ |
|
| 2042 |
+ out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top")
|
|
| 2043 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 2044 |
+ |
|
| 2045 |
+ out, err = s.d.Cmd("run", "--name=test2", "--link=test:abc", "busybox", "sh", "-c", "ping -c 1 abc")
|
|
| 2046 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 2047 |
+ c.Assert(out, checker.Contains, "1 packets transmitted, 1 packets received") |
|
| 2048 |
+ |
|
| 2049 |
+ out, err = s.d.Cmd("rm", "-f", "test")
|
|
| 2050 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 2051 |
+ |
|
| 2052 |
+ out, err = s.d.Cmd("run", "-d", "--name=test", "busybox", "top")
|
|
| 2053 |
+ c.Assert(err, check.IsNil, check.Commentf(out)) |
|
| 2054 |
+ out, err = s.d.Cmd("start", "-a", "test2")
|
|
| 2055 |
+ c.Assert(err, check.NotNil, check.Commentf(out)) |
|
| 2056 |
+ c.Assert(out, check.Not(checker.Contains), "1 packets transmitted, 1 packets received") |
|
| 2057 |
+ |
|
| 2058 |
+ err = s.d.Restart() |
|
| 2059 |
+ c.Assert(err, check.IsNil) |
|
| 2060 |
+ out, err = s.d.Cmd("start", "-a", "test2")
|
|
| 2061 |
+ c.Assert(err, check.NotNil, check.Commentf(out)) |
|
| 2062 |
+ c.Assert(out, check.Not(checker.Contains), "1 packets transmitted, 1 packets received") |
|
| 2063 |
+} |
| 1963 | 2064 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,127 @@ |
| 0 |
+// Package registrar provides name registration. It reserves a name to a given key. |
|
| 1 |
+package registrar |
|
| 2 |
+ |
|
| 3 |
+import ( |
|
| 4 |
+ "errors" |
|
| 5 |
+ "sync" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+var ( |
|
| 9 |
+ // ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved |
|
| 10 |
+ ErrNameReserved = errors.New("name is reserved")
|
|
| 11 |
+ // ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved |
|
| 12 |
+ ErrNameNotReserved = errors.New("name is not reserved")
|
|
| 13 |
+ // ErrNoSuchKey is returned when trying to find the names for a key which is not known |
|
| 14 |
+ ErrNoSuchKey = errors.New("provided key does not exist")
|
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+// Registrar stores indexes a list of keys and their registered names as well as indexes names and the key that they are registred to |
|
| 18 |
+// Names must be unique. |
|
| 19 |
+// Registrar is safe for concurrent access. |
|
| 20 |
+type Registrar struct {
|
|
| 21 |
+ idx map[string][]string |
|
| 22 |
+ names map[string]string |
|
| 23 |
+ mu sync.Mutex |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+// NewRegistrar creates a new Registrar with the an empty index |
|
| 27 |
+func NewRegistrar() *Registrar {
|
|
| 28 |
+ return &Registrar{
|
|
| 29 |
+ idx: make(map[string][]string), |
|
| 30 |
+ names: make(map[string]string), |
|
| 31 |
+ } |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// Reserve registers a key to a name |
|
| 35 |
+// Reserve is idempotent |
|
| 36 |
+// Attempting to reserve a key to a name that already exists results in an `ErrNameReserved` |
|
| 37 |
+// A name reservation is globally unique |
|
| 38 |
+func (r *Registrar) Reserve(name, key string) error {
|
|
| 39 |
+ r.mu.Lock() |
|
| 40 |
+ defer r.mu.Unlock() |
|
| 41 |
+ |
|
| 42 |
+ if k, exists := r.names[name]; exists {
|
|
| 43 |
+ if k != key {
|
|
| 44 |
+ return ErrNameReserved |
|
| 45 |
+ } |
|
| 46 |
+ return nil |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ r.idx[key] = append(r.idx[key], name) |
|
| 50 |
+ r.names[name] = key |
|
| 51 |
+ return nil |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// Release releases the reserved name |
|
| 55 |
+// Once released, a name can be reserved again |
|
| 56 |
+func (r *Registrar) Release(name string) {
|
|
| 57 |
+ r.mu.Lock() |
|
| 58 |
+ defer r.mu.Unlock() |
|
| 59 |
+ |
|
| 60 |
+ key, exists := r.names[name] |
|
| 61 |
+ if !exists {
|
|
| 62 |
+ return |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ for i, n := range r.idx[key] {
|
|
| 66 |
+ if n != name {
|
|
| 67 |
+ continue |
|
| 68 |
+ } |
|
| 69 |
+ r.idx[key] = append(r.idx[key][:i], r.idx[key][i+1:]...) |
|
| 70 |
+ break |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ delete(r.names, name) |
|
| 74 |
+ |
|
| 75 |
+ if len(r.idx[key]) == 0 {
|
|
| 76 |
+ delete(r.idx, key) |
|
| 77 |
+ } |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// Delete removes all reservations for the passed in key. |
|
| 81 |
+// All names reserved to this key are released. |
|
| 82 |
+func (r *Registrar) Delete(key string) {
|
|
| 83 |
+ r.mu.Lock() |
|
| 84 |
+ for _, name := range r.idx[key] {
|
|
| 85 |
+ delete(r.names, name) |
|
| 86 |
+ } |
|
| 87 |
+ delete(r.idx, key) |
|
| 88 |
+ r.mu.Unlock() |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// GetNames lists all the reserved names for the given key |
|
| 92 |
+func (r *Registrar) GetNames(key string) ([]string, error) {
|
|
| 93 |
+ r.mu.Lock() |
|
| 94 |
+ defer r.mu.Unlock() |
|
| 95 |
+ |
|
| 96 |
+ names, exists := r.idx[key] |
|
| 97 |
+ if !exists {
|
|
| 98 |
+ return nil, ErrNoSuchKey |
|
| 99 |
+ } |
|
| 100 |
+ return names, nil |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+// Get returns the key that the passed in name is reserved to |
|
| 104 |
+func (r *Registrar) Get(name string) (string, error) {
|
|
| 105 |
+ r.mu.Lock() |
|
| 106 |
+ key, exists := r.names[name] |
|
| 107 |
+ r.mu.Unlock() |
|
| 108 |
+ |
|
| 109 |
+ if !exists {
|
|
| 110 |
+ return "", ErrNameNotReserved |
|
| 111 |
+ } |
|
| 112 |
+ return key, nil |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+// GetAll returns all registered names |
|
| 116 |
+func (r *Registrar) GetAll() map[string][]string {
|
|
| 117 |
+ out := make(map[string][]string) |
|
| 118 |
+ |
|
| 119 |
+ r.mu.Lock() |
|
| 120 |
+ // copy index into out |
|
| 121 |
+ for id, names := range r.idx {
|
|
| 122 |
+ out[id] = names |
|
| 123 |
+ } |
|
| 124 |
+ r.mu.Unlock() |
|
| 125 |
+ return out |
|
| 126 |
+} |
| 0 | 127 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,119 @@ |
| 0 |
+package registrar |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "reflect" |
|
| 4 |
+ "testing" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+func TestReserve(t *testing.T) {
|
|
| 8 |
+ r := NewRegistrar() |
|
| 9 |
+ |
|
| 10 |
+ obj := "test1" |
|
| 11 |
+ if err := r.Reserve("test", obj); err != nil {
|
|
| 12 |
+ t.Fatal(err) |
|
| 13 |
+ } |
|
| 14 |
+ |
|
| 15 |
+ if err := r.Reserve("test", obj); err != nil {
|
|
| 16 |
+ t.Fatal(err) |
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ obj2 := "test2" |
|
| 20 |
+ err := r.Reserve("test", obj2)
|
|
| 21 |
+ if err == nil {
|
|
| 22 |
+ t.Fatalf("expected error when reserving an already reserved name to another object")
|
|
| 23 |
+ } |
|
| 24 |
+ if err != ErrNameReserved {
|
|
| 25 |
+ t.Fatal("expected `ErrNameReserved` error when attempting to reserve an already reserved name")
|
|
| 26 |
+ } |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+func TestRelease(t *testing.T) {
|
|
| 30 |
+ r := NewRegistrar() |
|
| 31 |
+ obj := "testing" |
|
| 32 |
+ |
|
| 33 |
+ if err := r.Reserve("test", obj); err != nil {
|
|
| 34 |
+ t.Fatal(err) |
|
| 35 |
+ } |
|
| 36 |
+ r.Release("test")
|
|
| 37 |
+ r.Release("test") // Ensure there is no panic here
|
|
| 38 |
+ |
|
| 39 |
+ if err := r.Reserve("test", obj); err != nil {
|
|
| 40 |
+ t.Fatal(err) |
|
| 41 |
+ } |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+func TestGetNames(t *testing.T) {
|
|
| 45 |
+ r := NewRegistrar() |
|
| 46 |
+ obj := "testing" |
|
| 47 |
+ names := []string{"test1", "test2"}
|
|
| 48 |
+ |
|
| 49 |
+ for _, name := range names {
|
|
| 50 |
+ if err := r.Reserve(name, obj); err != nil {
|
|
| 51 |
+ t.Fatal(err) |
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ r.Reserve("test3", "other")
|
|
| 55 |
+ |
|
| 56 |
+ names2, err := r.GetNames(obj) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ t.Fatal(err) |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ if !reflect.DeepEqual(names, names2) {
|
|
| 62 |
+ t.Fatalf("Exepected: %v, Got: %v", names, names2)
|
|
| 63 |
+ } |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+func TestDelete(t *testing.T) {
|
|
| 67 |
+ r := NewRegistrar() |
|
| 68 |
+ obj := "testing" |
|
| 69 |
+ names := []string{"test1", "test2"}
|
|
| 70 |
+ for _, name := range names {
|
|
| 71 |
+ if err := r.Reserve(name, obj); err != nil {
|
|
| 72 |
+ t.Fatal(err) |
|
| 73 |
+ } |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ r.Reserve("test3", "other")
|
|
| 77 |
+ r.Delete(obj) |
|
| 78 |
+ |
|
| 79 |
+ _, err := r.GetNames(obj) |
|
| 80 |
+ if err == nil {
|
|
| 81 |
+ t.Fatal("expected error getting names for deleted key")
|
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ if err != ErrNoSuchKey {
|
|
| 85 |
+ t.Fatal("expected `ErrNoSuchKey`")
|
|
| 86 |
+ } |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+func TestGet(t *testing.T) {
|
|
| 90 |
+ r := NewRegistrar() |
|
| 91 |
+ obj := "testing" |
|
| 92 |
+ name := "test" |
|
| 93 |
+ |
|
| 94 |
+ _, err := r.Get(name) |
|
| 95 |
+ if err == nil {
|
|
| 96 |
+ t.Fatal("expected error when key does not exist")
|
|
| 97 |
+ } |
|
| 98 |
+ if err != ErrNameNotReserved {
|
|
| 99 |
+ t.Fatal(err) |
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ if err := r.Reserve(name, obj); err != nil {
|
|
| 103 |
+ t.Fatal(err) |
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ if _, err = r.Get(name); err != nil {
|
|
| 107 |
+ t.Fatal(err) |
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ r.Delete(obj) |
|
| 111 |
+ _, err = r.Get(name) |
|
| 112 |
+ if err == nil {
|
|
| 113 |
+ t.Fatal("expected error when key does not exist")
|
|
| 114 |
+ } |
|
| 115 |
+ if err != ErrNameNotReserved {
|
|
| 116 |
+ t.Fatal(err) |
|
| 117 |
+ } |
|
| 118 |
+} |