dotcloud/docker

View on GitHub
daemon/names.go

Summary

Maintainability
A
1 hr
Test Coverage
package daemon // import "github.com/docker/docker/daemon"

import (
    "context"
    "fmt"
    "strings"

    "github.com/containerd/log"
    "github.com/docker/docker/container"
    "github.com/docker/docker/daemon/names"
    "github.com/docker/docker/errdefs"
    "github.com/docker/docker/pkg/namesgenerator"
    "github.com/docker/docker/pkg/stringid"
    "github.com/pkg/errors"
)

var (
    validContainerNameChars   = names.RestrictedNameChars
    validContainerNamePattern = names.RestrictedNamePattern
)

func (daemon *Daemon) registerName(container *container.Container) error {
    if container.ID == "" {
        return fmt.Errorf("invalid empty id")
    }
    if daemon.containers.Get(container.ID) != nil {
        // TODO(thaJeztah): should this be a panic (duplicate IDs due to invalid state on disk?)
        // TODO(thaJeztah): should this also check for container.ID being a prefix of another container's ID? (daemon.containersReplica.GetByPrefix); only should happen due to corruption / truncated ID.
        return fmt.Errorf("container is already loaded")
    }
    if container.Name == "" {
        name, err := daemon.generateAndReserveName(container.ID)
        if err != nil {
            return err
        }
        container.Name = name
        return nil
    }
    return daemon.containersReplica.ReserveName(container.Name, container.ID)
}

func (daemon *Daemon) generateIDAndName(name string) (string, string, error) {
    var (
        err error
        id  = stringid.GenerateRandomID()
    )

    if name == "" {
        if name, err = daemon.generateAndReserveName(id); err != nil {
            return "", "", err
        }
        return id, name, nil
    }

    if name, err = daemon.reserveName(id, name); err != nil {
        return "", "", err
    }

    return id, name, nil
}

func (daemon *Daemon) reserveName(id, name string) (string, error) {
    if !validContainerNamePattern.MatchString(strings.TrimPrefix(name, "/")) {
        return "", errdefs.InvalidParameter(errors.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars))
    }
    if name[0] != '/' {
        name = "/" + name
    }

    if err := daemon.containersReplica.ReserveName(name, id); err != nil {
        if errdefs.IsConflict(err) {
            id, err := daemon.containersReplica.Snapshot().GetID(name)
            if err != nil {
                log.G(context.TODO()).Errorf("got unexpected error while looking up reserved name: %v", err)
                return "", err
            }
            return "", nameConflictError{id: id, name: name}
        }
        return "", errors.Wrapf(err, "error reserving name: %q", name)
    }
    return name, nil
}

func (daemon *Daemon) releaseName(name string) {
    daemon.containersReplica.ReleaseName(name)
}

func (daemon *Daemon) generateAndReserveName(id string) (string, error) {
    var name string
    for i := 0; i < 6; i++ {
        name = namesgenerator.GetRandomName(i)
        if name[0] != '/' {
            name = "/" + name
        }

        if err := daemon.containersReplica.ReserveName(name, id); err != nil {
            if errdefs.IsConflict(err) {
                continue
            }
            return "", err
        }
        return name, nil
    }

    name = "/" + stringid.TruncateID(id)
    if err := daemon.containersReplica.ReserveName(name, id); err != nil {
        return "", err
    }
    return name, nil
}