mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #9705 from acbodine/9311-truncindex-error-duplicate-id-on-ambiguous-id
Closes #9311 Handles container id/name collisions against daemon functionalities according to #8069
This commit is contained in:
commit
34c804a139
32 changed files with 478 additions and 310 deletions
|
@ -863,6 +863,12 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
|||
for _, name := range cmd.Args() {
|
||||
obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Too many") {
|
||||
fmt.Fprintf(cli.err, "Error: %s", err.Error())
|
||||
status = 1
|
||||
continue
|
||||
}
|
||||
|
||||
obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "No such") {
|
||||
|
|
|
@ -1114,7 +1114,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
|
|||
w.Header().Set("Content-Type", "application/x-tar")
|
||||
if err := job.Run(); err != nil {
|
||||
log.Errorf("%s", err.Error())
|
||||
if strings.Contains(strings.ToLower(err.Error()), "no such container") {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "no such id") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else if strings.Contains(err.Error(), "no such file or directory") {
|
||||
return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
|
||||
|
|
|
@ -87,9 +87,9 @@ func (b *Builder) commit(id string, autoCmd []string, comment string) error {
|
|||
}
|
||||
defer container.Unmount()
|
||||
}
|
||||
container := b.Daemon.Get(id)
|
||||
if container == nil {
|
||||
return fmt.Errorf("An error occured while creating the container")
|
||||
container, err := b.Daemon.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Note: Actually copy the struct
|
||||
|
@ -710,7 +710,11 @@ func fixPermissions(source, destination string, uid, gid int, destExisted bool)
|
|||
|
||||
func (b *Builder) clearTmp() {
|
||||
for c := range b.TmpContainers {
|
||||
tmp := b.Daemon.Get(c)
|
||||
tmp, err := b.Daemon.Get(c)
|
||||
if err != nil {
|
||||
fmt.Fprint(b.OutStream, err.Error())
|
||||
}
|
||||
|
||||
if err := b.Daemon.Destroy(tmp); err != nil {
|
||||
fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
|
||||
return
|
||||
|
|
|
@ -28,9 +28,9 @@ func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status {
|
|||
stderr = job.GetenvBool("stderr")
|
||||
)
|
||||
|
||||
container := daemon.Get(name)
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
//logs
|
||||
|
|
|
@ -9,24 +9,29 @@ func (daemon *Daemon) ContainerChanges(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s CONTAINER", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
if container := daemon.Get(name); container != nil {
|
||||
outs := engine.NewTable("", 0)
|
||||
changes, err := container.Changes()
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
for _, change := range changes {
|
||||
out := &engine.Env{}
|
||||
if err := out.Import(change); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
outs.Add(out)
|
||||
}
|
||||
if _, err := outs.WriteListTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
} else {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
|
||||
container, error := daemon.Get(name)
|
||||
if error != nil {
|
||||
return job.Error(error)
|
||||
}
|
||||
|
||||
outs := engine.NewTable("", 0)
|
||||
changes, err := container.Changes()
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
for _, change := range changes {
|
||||
out := &engine.Env{}
|
||||
if err := out.Import(change); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
outs.Add(out)
|
||||
}
|
||||
|
||||
if _, err := outs.WriteListTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -12,9 +12,9 @@ func (daemon *Daemon) ContainerCommit(job *engine.Job) engine.Status {
|
|||
}
|
||||
name := job.Args[0]
|
||||
|
||||
container := daemon.Get(name)
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -1126,7 +1126,12 @@ func (container *Container) updateParentsHosts() error {
|
|||
if ref.ParentID == "0" {
|
||||
continue
|
||||
}
|
||||
c := container.daemon.Get(ref.ParentID)
|
||||
|
||||
c, err := container.daemon.Get(ref.ParentID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() {
|
||||
log.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
|
||||
if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, ref.Name); err != nil {
|
||||
|
@ -1395,9 +1400,9 @@ func (container *Container) GetMountLabel() string {
|
|||
|
||||
func (container *Container) getIpcContainer() (*Container, error) {
|
||||
containerID := container.hostConfig.IpcMode.Container()
|
||||
c := container.daemon.Get(containerID)
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("no such container to join IPC: %s", containerID)
|
||||
c, err := container.daemon.Get(containerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !c.IsRunning() {
|
||||
return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID)
|
||||
|
@ -1412,9 +1417,9 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
|
|||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("no container specified to join network")
|
||||
}
|
||||
nc := container.daemon.Get(parts[1])
|
||||
if nc == nil {
|
||||
return nil, fmt.Errorf("no such container to join network: %s", parts[1])
|
||||
nc, err := container.daemon.Get(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !nc.IsRunning() {
|
||||
return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])
|
||||
|
|
|
@ -16,18 +16,19 @@ func (daemon *Daemon) ContainerCopy(job *engine.Job) engine.Status {
|
|||
resource = job.Args[1]
|
||||
)
|
||||
|
||||
if container := daemon.Get(name); container != nil {
|
||||
|
||||
data, err := container.Copy(resource)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
if _, err := io.Copy(job.Stdout, data); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return job.Errorf("No such container: %s", name)
|
||||
|
||||
data, err := container.Copy(resource)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
if _, err := io.Copy(job.Stdout, data); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -132,9 +132,9 @@ func (daemon *Daemon) GenerateSecurityOpt(ipcMode runconfig.IpcMode, pidMode run
|
|||
return label.DisableSecOpt(), nil
|
||||
}
|
||||
if ipcContainer := ipcMode.Container(); ipcContainer != "" {
|
||||
c := daemon.Get(ipcContainer)
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("no such container to join IPC: %s", ipcContainer)
|
||||
c, err := daemon.Get(ipcContainer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !c.IsRunning() {
|
||||
return nil, fmt.Errorf("cannot join IPC of a non running container: %s", ipcContainer)
|
||||
|
|
|
@ -155,28 +155,39 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Get looks for a container by the specified ID or name, and returns it.
|
||||
// If the container is not found, or if an error occurs, nil is returned.
|
||||
func (daemon *Daemon) Get(name string) *Container {
|
||||
id, err := daemon.idIndex.Get(name)
|
||||
if err == nil {
|
||||
return daemon.containers.Get(id)
|
||||
// Get looks for a container with the provided prefix
|
||||
func (daemon *Daemon) Get(prefix string) (*Container, error) {
|
||||
if containerByID := daemon.containers.Get(prefix); containerByID != nil {
|
||||
|
||||
// prefix is an exact match to a full container ID
|
||||
return containerByID, nil
|
||||
}
|
||||
|
||||
if c, _ := daemon.GetByName(name); c != nil {
|
||||
return c
|
||||
// Either GetByName finds an entity matching prefix exactly, or it doesn't.
|
||||
// Check value of containerByName and ignore any errors
|
||||
containerByName, _ := daemon.GetByName(prefix)
|
||||
containerId, indexError := daemon.idIndex.Get(prefix)
|
||||
|
||||
if containerByName != nil {
|
||||
|
||||
// prefix is an exact match to a full container Name
|
||||
return containerByName, nil
|
||||
}
|
||||
|
||||
if err == truncindex.ErrDuplicateID {
|
||||
log.Errorf("Short ID %s is ambiguous: please retry with more characters or use the full ID.\n", name)
|
||||
if containerId != "" {
|
||||
|
||||
// prefix is a fuzzy match to a container ID
|
||||
return daemon.containers.Get(containerId), nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return nil, indexError
|
||||
}
|
||||
|
||||
// Exists returns a true if a container of the specified ID or name exists,
|
||||
// false otherwise.
|
||||
func (daemon *Daemon) Exists(id string) bool {
|
||||
return daemon.Get(id) != nil
|
||||
c, _ := daemon.Get(id)
|
||||
return c != nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) containerRoot(id string) string {
|
||||
|
@ -715,9 +726,9 @@ func (daemon *Daemon) Children(name string) (map[string]*Container, error) {
|
|||
children := make(map[string]*Container)
|
||||
|
||||
err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
|
||||
c := daemon.Get(e.ID())
|
||||
if c == nil {
|
||||
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
|
||||
c, err := daemon.Get(e.ID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
children[p] = c
|
||||
return nil
|
||||
|
@ -754,7 +765,10 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
child := daemon.Get(parts["name"])
|
||||
child, err := daemon.Get(parts["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if child == nil {
|
||||
return fmt.Errorf("Could not get container for %s", parts["name"])
|
||||
}
|
||||
|
@ -1100,18 +1114,18 @@ func (daemon *Daemon) Stats(c *Container) (*execdriver.ResourceStats, error) {
|
|||
}
|
||||
|
||||
func (daemon *Daemon) SubscribeToContainerStats(name string) (chan interface{}, error) {
|
||||
c := daemon.Get(name)
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("no such container")
|
||||
c, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ch := daemon.statsCollector.collect(c)
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface{}) error {
|
||||
c := daemon.Get(name)
|
||||
if c == nil {
|
||||
return fmt.Errorf("no such container")
|
||||
c, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
daemon.statsCollector.unsubscribe(c, ch)
|
||||
return nil
|
||||
|
|
101
daemon/daemon_test.go
Normal file
101
daemon/daemon_test.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/pkg/graphdb"
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//
|
||||
// https://github.com/docker/docker/issues/8069
|
||||
//
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
c1 := &Container{
|
||||
ID: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
|
||||
Name: "tender_bardeen",
|
||||
}
|
||||
c2 := &Container{
|
||||
ID: "3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de",
|
||||
Name: "drunk_hawking",
|
||||
}
|
||||
c3 := &Container{
|
||||
ID: "3cdbd1aa394fd68559fd1441d6eff2abfafdcba06e72d2febdba229008b0bf57",
|
||||
Name: "3cdbd1aa",
|
||||
}
|
||||
c4 := &Container{
|
||||
ID: "75fb0b800922abdbef2d27e60abcdfaf7fb0698b2a96d22d3354da361a6ff4a5",
|
||||
Name: "5a4ff6a163ad4533d22d69a2b8960bf7fafdcba06e72d2febdba229008b0bf57",
|
||||
}
|
||||
c5 := &Container{
|
||||
ID: "d22d69a2b8960bf7fafdcba06e72d2febdba960bf7fafdcba06e72d2f9008b060b",
|
||||
Name: "d22d69a2b896",
|
||||
}
|
||||
|
||||
store := &contStore{
|
||||
s: map[string]*Container{
|
||||
c1.ID: c1,
|
||||
c2.ID: c2,
|
||||
c3.ID: c3,
|
||||
c4.ID: c4,
|
||||
c5.ID: c5,
|
||||
},
|
||||
}
|
||||
|
||||
index := truncindex.NewTruncIndex([]string{})
|
||||
index.Add(c1.ID)
|
||||
index.Add(c2.ID)
|
||||
index.Add(c3.ID)
|
||||
index.Add(c4.ID)
|
||||
index.Add(c5.ID)
|
||||
|
||||
daemonTestDbPath := path.Join(os.TempDir(), "daemon_test.db")
|
||||
graph, err := graphdb.NewSqliteConn(daemonTestDbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create daemon test sqlite database at %s", daemonTestDbPath)
|
||||
}
|
||||
graph.Set(c1.Name, c1.ID)
|
||||
graph.Set(c2.Name, c2.ID)
|
||||
graph.Set(c3.Name, c3.ID)
|
||||
graph.Set(c4.Name, c4.ID)
|
||||
graph.Set(c5.Name, c5.ID)
|
||||
|
||||
daemon := &Daemon{
|
||||
containers: store,
|
||||
idIndex: index,
|
||||
containerGraph: graph,
|
||||
}
|
||||
|
||||
if container, _ := daemon.Get("3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de"); container != c2 {
|
||||
t.Fatal("Should explicitly match full container IDs")
|
||||
}
|
||||
|
||||
if container, _ := daemon.Get("75fb0b8009"); container != c4 {
|
||||
t.Fatal("Should match a partial ID")
|
||||
}
|
||||
|
||||
if container, _ := daemon.Get("drunk_hawking"); container != c2 {
|
||||
t.Fatal("Should match a full name")
|
||||
}
|
||||
|
||||
// c3.Name is a partial match for both c3.ID and c2.ID
|
||||
if c, _ := daemon.Get("3cdbd1aa"); c != c3 {
|
||||
t.Fatal("Should match a full name even though it collides with another container's ID")
|
||||
}
|
||||
|
||||
if container, _ := daemon.Get("d22d69a2b896"); container != c5 {
|
||||
t.Fatal("Should match a container where the provided prefix is an exact match to the it's name, and is also a prefix for it's ID")
|
||||
}
|
||||
|
||||
if _, err := daemon.Get("3cdbd1"); err == nil {
|
||||
t.Fatal("Should return an error when provided a prefix that partially matches multiple container ID's")
|
||||
}
|
||||
|
||||
if _, err := daemon.Get("nothing"); err == nil {
|
||||
t.Fatal("Should return an error when provided a prefix that is neither a name or a partial match to an ID")
|
||||
}
|
||||
|
||||
os.Remove(daemonTestDbPath)
|
||||
}
|
|
@ -17,10 +17,10 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
|
|||
removeVolume := job.GetenvBool("removeVolume")
|
||||
removeLink := job.GetenvBool("removeLink")
|
||||
forceRemove := job.GetenvBool("forceRemove")
|
||||
container := daemon.Get(name)
|
||||
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
if removeLink {
|
||||
|
@ -36,7 +36,7 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
|
|||
if pe == nil {
|
||||
return job.Errorf("Cannot get parent %s for name %s", parent, name)
|
||||
}
|
||||
parentContainer := daemon.Get(pe.ID())
|
||||
parentContainer, _ := daemon.Get(pe.ID())
|
||||
|
||||
if parentContainer != nil {
|
||||
parentContainer.DisableLink(n)
|
||||
|
|
|
@ -97,10 +97,9 @@ func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
|
|||
}
|
||||
|
||||
func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
||||
container := d.Get(name)
|
||||
|
||||
if container == nil {
|
||||
return nil, fmt.Errorf("No such container: %s", name)
|
||||
container, err := d.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !container.IsRunning() {
|
||||
|
|
|
@ -11,20 +11,23 @@ func (daemon *Daemon) ContainerExport(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s container_id", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
if container := daemon.Get(name); container != nil {
|
||||
data, err := container.Export()
|
||||
if err != nil {
|
||||
return job.Errorf("%s: %s", name, err)
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
// Stream the entire contents of the container (basically a volatile snapshot)
|
||||
if _, err := io.Copy(job.Stdout, data); err != nil {
|
||||
return job.Errorf("%s: %s", name, err)
|
||||
}
|
||||
// FIXME: factor job-specific LogEvent to engine.Job.Run()
|
||||
container.LogEvent("export")
|
||||
return engine.StatusOK
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return job.Errorf("No such container: %s", name)
|
||||
|
||||
data, err := container.Export()
|
||||
if err != nil {
|
||||
return job.Errorf("%s: %s", name, err)
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
// Stream the entire contents of the container (basically a volatile snapshot)
|
||||
if _, err := io.Copy(job.Stdout, data); err != nil {
|
||||
return job.Errorf("%s: %s", name, err)
|
||||
}
|
||||
// FIXME: factor job-specific LogEvent to engine.Job.Run()
|
||||
container.LogEvent("export")
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -13,60 +13,62 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status {
|
|||
return job.Errorf("usage: %s NAME", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
if container := daemon.Get(name); container != nil {
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
if job.GetenvBool("raw") {
|
||||
b, err := json.Marshal(&struct {
|
||||
*Container
|
||||
HostConfig *runconfig.HostConfig
|
||||
}{container, container.hostConfig})
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
job.Stdout.Write(b)
|
||||
return engine.StatusOK
|
||||
}
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
out := &engine.Env{}
|
||||
out.SetJson("Id", container.ID)
|
||||
out.SetAuto("Created", container.Created)
|
||||
out.SetJson("Path", container.Path)
|
||||
out.SetList("Args", container.Args)
|
||||
out.SetJson("Config", container.Config)
|
||||
out.SetJson("State", container.State)
|
||||
out.Set("Image", container.ImageID)
|
||||
out.SetJson("NetworkSettings", container.NetworkSettings)
|
||||
out.Set("ResolvConfPath", container.ResolvConfPath)
|
||||
out.Set("HostnamePath", container.HostnamePath)
|
||||
out.Set("HostsPath", container.HostsPath)
|
||||
out.SetJson("Name", container.Name)
|
||||
out.SetInt("RestartCount", container.RestartCount)
|
||||
out.Set("Driver", container.Driver)
|
||||
out.Set("ExecDriver", container.ExecDriver)
|
||||
out.Set("MountLabel", container.MountLabel)
|
||||
out.Set("ProcessLabel", container.ProcessLabel)
|
||||
out.SetJson("Volumes", container.Volumes)
|
||||
out.SetJson("VolumesRW", container.VolumesRW)
|
||||
out.SetJson("AppArmorProfile", container.AppArmorProfile)
|
||||
|
||||
out.SetList("ExecIDs", container.GetExecIDs())
|
||||
|
||||
if children, err := daemon.Children(container.Name); err == nil {
|
||||
for linkAlias, child := range children {
|
||||
container.hostConfig.Links = append(container.hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
||||
}
|
||||
}
|
||||
|
||||
out.SetJson("HostConfig", container.hostConfig)
|
||||
|
||||
container.hostConfig.Links = nil
|
||||
if _, err := out.WriteTo(job.Stdout); err != nil {
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
if job.GetenvBool("raw") {
|
||||
b, err := json.Marshal(&struct {
|
||||
*Container
|
||||
HostConfig *runconfig.HostConfig
|
||||
}{container, container.hostConfig})
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
job.Stdout.Write(b)
|
||||
return engine.StatusOK
|
||||
}
|
||||
return job.Errorf("No such container: %s", name)
|
||||
|
||||
out := &engine.Env{}
|
||||
out.SetJson("Id", container.ID)
|
||||
out.SetAuto("Created", container.Created)
|
||||
out.SetJson("Path", container.Path)
|
||||
out.SetList("Args", container.Args)
|
||||
out.SetJson("Config", container.Config)
|
||||
out.SetJson("State", container.State)
|
||||
out.Set("Image", container.ImageID)
|
||||
out.SetJson("NetworkSettings", container.NetworkSettings)
|
||||
out.Set("ResolvConfPath", container.ResolvConfPath)
|
||||
out.Set("HostnamePath", container.HostnamePath)
|
||||
out.Set("HostsPath", container.HostsPath)
|
||||
out.SetJson("Name", container.Name)
|
||||
out.SetInt("RestartCount", container.RestartCount)
|
||||
out.Set("Driver", container.Driver)
|
||||
out.Set("ExecDriver", container.ExecDriver)
|
||||
out.Set("MountLabel", container.MountLabel)
|
||||
out.Set("ProcessLabel", container.ProcessLabel)
|
||||
out.SetJson("Volumes", container.Volumes)
|
||||
out.SetJson("VolumesRW", container.VolumesRW)
|
||||
out.SetJson("AppArmorProfile", container.AppArmorProfile)
|
||||
|
||||
out.SetList("ExecIDs", container.GetExecIDs())
|
||||
|
||||
if children, err := daemon.Children(container.Name); err == nil {
|
||||
for linkAlias, child := range children {
|
||||
container.hostConfig.Links = append(container.hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
||||
}
|
||||
}
|
||||
|
||||
out.SetJson("HostConfig", container.hostConfig)
|
||||
|
||||
container.hostConfig.Links = nil
|
||||
if _, err := out.WriteTo(job.Stdout); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerExecInspect(job *engine.Job) engine.Status {
|
||||
|
|
|
@ -38,22 +38,23 @@ func (daemon *Daemon) ContainerKill(job *engine.Job) engine.Status {
|
|||
}
|
||||
}
|
||||
|
||||
if container := daemon.Get(name); container != nil {
|
||||
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
||||
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
||||
if err := container.Kill(); err != nil {
|
||||
return job.Errorf("Cannot kill container %s: %s", name, err)
|
||||
}
|
||||
container.LogEvent("kill")
|
||||
} else {
|
||||
// Otherwise, just send the requested signal
|
||||
if err := container.KillSig(int(sig)); err != nil {
|
||||
return job.Errorf("Cannot kill container %s: %s", name, err)
|
||||
}
|
||||
// FIXME: Add event for signals
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
||||
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
||||
if err := container.Kill(); err != nil {
|
||||
return job.Errorf("Cannot kill container %s: %s", name, err)
|
||||
}
|
||||
container.LogEvent("kill")
|
||||
} else {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
// Otherwise, just send the requested signal
|
||||
if err := container.KillSig(int(sig)); err != nil {
|
||||
return job.Errorf("Cannot kill container %s: %s", name, err)
|
||||
}
|
||||
// FIXME: Add event for signals
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -63,16 +63,16 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
|
|||
|
||||
var beforeCont, sinceCont *Container
|
||||
if before != "" {
|
||||
beforeCont = daemon.Get(before)
|
||||
if beforeCont == nil {
|
||||
return job.Error(fmt.Errorf("Could not find container with name or id %s", before))
|
||||
beforeCont, err = daemon.Get(before)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if since != "" {
|
||||
sinceCont = daemon.Get(since)
|
||||
if sinceCont == nil {
|
||||
return job.Error(fmt.Errorf("Could not find container with name or id %s", since))
|
||||
sinceCont, err = daemon.Get(since)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,9 +40,9 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status {
|
|||
if tail == "" {
|
||||
tail = "all"
|
||||
}
|
||||
container := daemon.Get(name)
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
cLog, err := container.ReadLog("json")
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
|
|
|
@ -9,9 +9,9 @@ func (daemon *Daemon) ContainerPause(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s CONTAINER", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
container := daemon.Get(name)
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
if err := container.Pause(); err != nil {
|
||||
return job.Errorf("Cannot pause container %s: %s", name, err)
|
||||
|
@ -25,9 +25,9 @@ func (daemon *Daemon) ContainerUnpause(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s CONTAINER", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
container := daemon.Get(name)
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
if err := container.Unpause(); err != nil {
|
||||
return job.Errorf("Cannot unpause container %s: %s", name, err)
|
||||
|
|
|
@ -11,9 +11,9 @@ func (daemon *Daemon) ContainerRename(job *engine.Job) engine.Status {
|
|||
oldName := job.Args[0]
|
||||
newName := job.Args[1]
|
||||
|
||||
container := daemon.Get(oldName)
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", oldName)
|
||||
container, err := daemon.Get(oldName)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
oldName = container.Name
|
||||
|
|
|
@ -19,14 +19,14 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
|
|||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
if container := daemon.Get(name); container != nil {
|
||||
if err := container.Resize(height, width); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return job.Errorf("No such container: %s", name)
|
||||
if err := container.Resize(height, width); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerExecResize(job *engine.Job) engine.Status {
|
||||
|
|
|
@ -15,13 +15,13 @@ func (daemon *Daemon) ContainerRestart(job *engine.Job) engine.Status {
|
|||
if job.EnvExists("t") {
|
||||
t = job.GetenvInt("t")
|
||||
}
|
||||
if container := daemon.Get(name); container != nil {
|
||||
if err := container.Restart(int(t)); err != nil {
|
||||
return job.Errorf("Cannot restart container %s: %s\n", name, err)
|
||||
}
|
||||
container.LogEvent("restart")
|
||||
} else {
|
||||
return job.Errorf("No such container: %s\n", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
if err := container.Restart(int(t)); err != nil {
|
||||
return job.Errorf("Cannot restart container %s: %s\n", name, err)
|
||||
}
|
||||
container.LogEvent("restart")
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ func (daemon *Daemon) ContainerStart(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s container_id", job.Name)
|
||||
}
|
||||
var (
|
||||
name = job.Args[0]
|
||||
container = daemon.Get(name)
|
||||
name = job.Args[0]
|
||||
)
|
||||
|
||||
if container == nil {
|
||||
return job.Errorf("No such container: %s", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
|
||||
if container.IsPaused() {
|
||||
|
|
|
@ -15,16 +15,16 @@ func (daemon *Daemon) ContainerStop(job *engine.Job) engine.Status {
|
|||
if job.EnvExists("t") {
|
||||
t = job.GetenvInt("t")
|
||||
}
|
||||
if container := daemon.Get(name); container != nil {
|
||||
if !container.IsRunning() {
|
||||
return job.Errorf("Container already stopped")
|
||||
}
|
||||
if err := container.Stop(int(t)); err != nil {
|
||||
return job.Errorf("Cannot stop container %s: %s\n", name, err)
|
||||
}
|
||||
container.LogEvent("stop")
|
||||
} else {
|
||||
return job.Errorf("No such container: %s\n", name)
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
if !container.IsRunning() {
|
||||
return job.Errorf("Container already stopped")
|
||||
}
|
||||
if err := container.Stop(int(t)); err != nil {
|
||||
return job.Errorf("Cannot stop container %s: %s\n", name, err)
|
||||
}
|
||||
container.LogEvent("stop")
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
108
daemon/top.go
108
daemon/top.go
|
@ -21,59 +21,59 @@ func (daemon *Daemon) ContainerTop(job *engine.Job) engine.Status {
|
|||
psArgs = job.Args[1]
|
||||
}
|
||||
|
||||
if container := daemon.Get(name); container != nil {
|
||||
if !container.IsRunning() {
|
||||
return job.Errorf("Container %s is not running", name)
|
||||
}
|
||||
pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
|
||||
if err != nil {
|
||||
return job.Errorf("Error running ps: %s", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
header := strings.Fields(lines[0])
|
||||
out := &engine.Env{}
|
||||
out.SetList("Titles", header)
|
||||
|
||||
pidIndex := -1
|
||||
for i, name := range header {
|
||||
if name == "PID" {
|
||||
pidIndex = i
|
||||
}
|
||||
}
|
||||
if pidIndex == -1 {
|
||||
return job.Errorf("Couldn't find PID field in ps output")
|
||||
}
|
||||
|
||||
processes := [][]string{}
|
||||
for _, line := range lines[1:] {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
p, err := strconv.Atoi(fields[pidIndex])
|
||||
if err != nil {
|
||||
return job.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
if pid == p {
|
||||
// Make sure number of fields equals number of header titles
|
||||
// merging "overhanging" fields
|
||||
process := fields[:len(header)-1]
|
||||
process = append(process, strings.Join(fields[len(header)-1:], " "))
|
||||
processes = append(processes, process)
|
||||
}
|
||||
}
|
||||
}
|
||||
out.SetJson("Processes", processes)
|
||||
out.WriteTo(job.Stdout)
|
||||
return engine.StatusOK
|
||||
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
return job.Errorf("No such container: %s", name)
|
||||
if !container.IsRunning() {
|
||||
return job.Errorf("Container %s is not running", name)
|
||||
}
|
||||
pids, err := daemon.ExecutionDriver().GetPidsForContainer(container.ID)
|
||||
if err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
|
||||
if err != nil {
|
||||
return job.Errorf("Error running ps: %s", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
header := strings.Fields(lines[0])
|
||||
out := &engine.Env{}
|
||||
out.SetList("Titles", header)
|
||||
|
||||
pidIndex := -1
|
||||
for i, name := range header {
|
||||
if name == "PID" {
|
||||
pidIndex = i
|
||||
}
|
||||
}
|
||||
if pidIndex == -1 {
|
||||
return job.Errorf("Couldn't find PID field in ps output")
|
||||
}
|
||||
|
||||
processes := [][]string{}
|
||||
for _, line := range lines[1:] {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
p, err := strconv.Atoi(fields[pidIndex])
|
||||
if err != nil {
|
||||
return job.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
if pid == p {
|
||||
// Make sure number of fields equals number of header titles
|
||||
// merging "overhanging" fields
|
||||
process := fields[:len(header)-1]
|
||||
process = append(process, strings.Join(fields[len(header)-1:], " "))
|
||||
processes = append(processes, process)
|
||||
}
|
||||
}
|
||||
}
|
||||
out.SetJson("Processes", processes)
|
||||
out.WriteTo(job.Stdout)
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -266,9 +266,9 @@ func (container *Container) applyVolumesFrom() error {
|
|||
continue
|
||||
}
|
||||
|
||||
c := container.daemon.Get(id)
|
||||
if c == nil {
|
||||
return fmt.Errorf("container %s not found, impossible to mount its volumes", id)
|
||||
c, err := container.daemon.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -11,10 +11,11 @@ func (daemon *Daemon) ContainerWait(job *engine.Job) engine.Status {
|
|||
return job.Errorf("Usage: %s", job.Name)
|
||||
}
|
||||
name := job.Args[0]
|
||||
if container := daemon.Get(name); container != nil {
|
||||
status, _ := container.WaitStop(-1 * time.Second)
|
||||
job.Printf("%d\n", status)
|
||||
return engine.StatusOK
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return job.Errorf("%s: %s", job.Name, err.Error())
|
||||
}
|
||||
return job.Errorf("%s: No such container: %s", job.Name, name)
|
||||
status, _ := container.WaitStop(-1 * time.Second)
|
||||
job.Printf("%d\n", status)
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
|
|
@ -325,7 +325,7 @@ func TestPostCreateNull(t *testing.T) {
|
|||
|
||||
containerAssertExists(eng, containerID, t)
|
||||
|
||||
c := daemon.Get(containerID)
|
||||
c, _ := daemon.Get(containerID)
|
||||
if c.Config.Cpuset != "" {
|
||||
t.Fatalf("Cpuset should have been empty - instead its:" + c.Config.Cpuset)
|
||||
}
|
||||
|
|
|
@ -282,12 +282,12 @@ func TestDaemonCreate(t *testing.T) {
|
|||
}
|
||||
|
||||
// Make sure we can get the container with Get()
|
||||
if daemon.Get(container.ID) == nil {
|
||||
if _, err := daemon.Get(container.ID); err != nil {
|
||||
t.Errorf("Unable to get newly created container")
|
||||
}
|
||||
|
||||
// Make sure it is the right container
|
||||
if daemon.Get(container.ID) != container {
|
||||
if c, _ := daemon.Get(container.ID); c != container {
|
||||
t.Errorf("Get() returned the wrong container")
|
||||
}
|
||||
|
||||
|
@ -383,8 +383,8 @@ func TestDestroy(t *testing.T) {
|
|||
}
|
||||
|
||||
// Make sure daemon.Get() refuses to return the unexisting container
|
||||
if daemon.Get(container.ID) != nil {
|
||||
t.Errorf("Unable to get newly created container")
|
||||
if c, _ := daemon.Get(container.ID); c != nil {
|
||||
t.Errorf("Got a container that should not exist")
|
||||
}
|
||||
|
||||
// Test double destroy
|
||||
|
@ -407,16 +407,16 @@ func TestGet(t *testing.T) {
|
|||
container3, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t)
|
||||
defer daemon.Destroy(container3)
|
||||
|
||||
if daemon.Get(container1.ID) != container1 {
|
||||
t.Errorf("Get(test1) returned %v while expecting %v", daemon.Get(container1.ID), container1)
|
||||
if c, _ := daemon.Get(container1.ID); c != container1 {
|
||||
t.Errorf("Get(test1) returned %v while expecting %v", c, container1)
|
||||
}
|
||||
|
||||
if daemon.Get(container2.ID) != container2 {
|
||||
t.Errorf("Get(test2) returned %v while expecting %v", daemon.Get(container2.ID), container2)
|
||||
if c, _ := daemon.Get(container2.ID); c != container2 {
|
||||
t.Errorf("Get(test2) returned %v while expecting %v", c, container2)
|
||||
}
|
||||
|
||||
if daemon.Get(container3.ID) != container3 {
|
||||
t.Errorf("Get(test3) returned %v while expecting %v", daemon.Get(container3.ID), container3)
|
||||
if c, _ := daemon.Get(container3.ID); c != container3 {
|
||||
t.Errorf("Get(test3) returned %v while expecting %v", c, container3)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -485,9 +485,9 @@ func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daem
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container := daemon.Get(id)
|
||||
if container == nil {
|
||||
t.Fatalf("Couldn't fetch test container %s", id)
|
||||
container, err := daemon.Get(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
|
||||
|
@ -646,8 +646,8 @@ func TestRestore(t *testing.T) {
|
|||
if runningCount != 0 {
|
||||
t.Fatalf("Expected 0 container alive, %d found", runningCount)
|
||||
}
|
||||
container3 := daemon2.Get(container1.ID)
|
||||
if container3 == nil {
|
||||
container3, err := daemon2.Get(container1.ID)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to Get container")
|
||||
}
|
||||
if err := container3.Run(); err != nil {
|
||||
|
@ -666,16 +666,21 @@ func TestDefaultContainerName(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container := daemon.Get(createNamedTestContainer(eng, config, t, "some_name"))
|
||||
container, err := daemon.Get(createNamedTestContainer(eng, config, t, "some_name"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerID := container.ID
|
||||
|
||||
if container.Name != "/some_name" {
|
||||
t.Fatalf("Expect /some_name got %s", container.Name)
|
||||
}
|
||||
|
||||
if c := daemon.Get("/some_name"); c == nil {
|
||||
c, err := daemon.Get("/some_name")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't retrieve test container as /some_name")
|
||||
} else if c.ID != containerID {
|
||||
}
|
||||
if c.ID != containerID {
|
||||
t.Fatalf("Container /some_name has ID %s instead of %s", c.ID, containerID)
|
||||
}
|
||||
}
|
||||
|
@ -690,14 +695,17 @@ func TestRandomContainerName(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container := daemon.Get(createTestContainer(eng, config, t))
|
||||
container, err := daemon.Get(createTestContainer(eng, config, t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
containerID := container.ID
|
||||
|
||||
if container.Name == "" {
|
||||
t.Fatalf("Expected not empty container name")
|
||||
}
|
||||
|
||||
if c := daemon.Get(container.Name); c == nil {
|
||||
if c, err := daemon.Get(container.Name); err != nil {
|
||||
log.Fatalf("Could not lookup container %s by its name", container.Name)
|
||||
} else if c.ID != containerID {
|
||||
log.Fatalf("Looking up container name %s returned id %s instead of %s", container.Name, c.ID, containerID)
|
||||
|
@ -737,13 +745,16 @@ func TestContainerNameValidation(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container := daemon.Get(engine.Tail(outputBuffer, 1))
|
||||
container, err := daemon.Get(engine.Tail(outputBuffer, 1))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if container.Name != "/"+test.Name {
|
||||
t.Fatalf("Expect /%s got %s", test.Name, container.Name)
|
||||
}
|
||||
|
||||
if c := daemon.Get("/" + test.Name); c == nil {
|
||||
if c, err := daemon.Get("/" + test.Name); err != nil {
|
||||
t.Fatalf("Couldn't retrieve test container as /%s", test.Name)
|
||||
} else if c.ID != container.ID {
|
||||
t.Fatalf("Container /%s has ID %s instead of %s", test.Name, c.ID, container.ID)
|
||||
|
@ -762,7 +773,10 @@ func TestLinkChildContainer(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
|
||||
container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
webapp, err := daemon.GetByName("/webapp")
|
||||
if err != nil {
|
||||
|
@ -778,7 +792,10 @@ func TestLinkChildContainer(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
childContainer := daemon.Get(createTestContainer(eng, config, t))
|
||||
childContainer, err := daemon.Get(createTestContainer(eng, config, t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -804,7 +821,10 @@ func TestGetAllChildren(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
|
||||
container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
webapp, err := daemon.GetByName("/webapp")
|
||||
if err != nil {
|
||||
|
@ -820,7 +840,10 @@ func TestGetAllChildren(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
childContainer := daemon.Get(createTestContainer(eng, config, t))
|
||||
childContainer, err := daemon.Get(createTestContainer(eng, config, t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -117,7 +117,7 @@ func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
|
|||
|
||||
func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
|
||||
daemon := mkDaemonFromEngine(eng, t)
|
||||
if c := daemon.Get(id); c != nil {
|
||||
if c, _ := daemon.Get(id); c != nil {
|
||||
t.Fatal(fmt.Errorf("Container %s should not exist", id))
|
||||
}
|
||||
}
|
||||
|
@ -142,9 +142,9 @@ func assertHttpError(r *httptest.ResponseRecorder, t Fataler) {
|
|||
|
||||
func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
|
||||
daemon := mkDaemonFromEngine(eng, t)
|
||||
c := daemon.Get(id)
|
||||
if c == nil {
|
||||
t.Fatal(fmt.Errorf("No such container: %s", id))
|
||||
c, err := daemon.Get(id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -10,10 +10,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// ErrNoID is thrown when attempting to use empty prefixes
|
||||
ErrNoID = errors.New("prefix can't be empty")
|
||||
// ErrDuplicateID is thrown when a duplicated id was found
|
||||
ErrDuplicateID = errors.New("multiple IDs were found")
|
||||
ErrEmptyPrefix = errors.New("Prefix can't be empty")
|
||||
ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -47,7 +45,7 @@ func (idx *TruncIndex) addID(id string) error {
|
|||
return fmt.Errorf("illegal character: ' '")
|
||||
}
|
||||
if id == "" {
|
||||
return ErrNoID
|
||||
return ErrEmptyPrefix
|
||||
}
|
||||
if _, exists := idx.ids[id]; exists {
|
||||
return fmt.Errorf("id already exists: '%s'", id)
|
||||
|
@ -87,26 +85,26 @@ func (idx *TruncIndex) Delete(id string) error {
|
|||
// Get retrieves an ID from the TruncIndex. If there are multiple IDs
|
||||
// with the given prefix, an error is thrown.
|
||||
func (idx *TruncIndex) Get(s string) (string, error) {
|
||||
idx.RLock()
|
||||
defer idx.RUnlock()
|
||||
if s == "" {
|
||||
return "", ErrEmptyPrefix
|
||||
}
|
||||
var (
|
||||
id string
|
||||
)
|
||||
if s == "" {
|
||||
return "", ErrNoID
|
||||
}
|
||||
subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
|
||||
if id != "" {
|
||||
// we haven't found the ID if there are two or more IDs
|
||||
id = ""
|
||||
return ErrDuplicateID
|
||||
return ErrAmbiguousPrefix
|
||||
}
|
||||
id = string(prefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
idx.RLock()
|
||||
defer idx.RUnlock()
|
||||
if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
|
||||
return "", fmt.Errorf("no such id: %s", s)
|
||||
return "", err
|
||||
}
|
||||
if id != "" {
|
||||
return id, nil
|
||||
|
|
|
@ -59,6 +59,11 @@ func TestTruncIndex(t *testing.T) {
|
|||
assertIndexGet(t, index, id[:4], "", true)
|
||||
assertIndexGet(t, index, id[:1], "", true)
|
||||
|
||||
// An ambiguous id prefix should return an error
|
||||
if _, err := index.Get(id[:4]); err == nil || err == nil {
|
||||
t.Fatal("An ambiguous id prefix should return an error")
|
||||
}
|
||||
|
||||
// 7 characters should NOT conflict
|
||||
assertIndexGet(t, index, id[:7], id, false)
|
||||
assertIndexGet(t, index, id2[:7], id2, false)
|
||||
|
|
Loading…
Reference in a new issue