1
0
Fork 0
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:
Michael Crosby 2015-02-06 14:01:28 -08:00
commit 34c804a139
32 changed files with 478 additions and 310 deletions

View file

@ -863,6 +863,12 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false)) obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
if err != nil { 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)) obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
if err != nil { if err != nil {
if strings.Contains(err.Error(), "No such") { if strings.Contains(err.Error(), "No such") {

View file

@ -1114,7 +1114,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
w.Header().Set("Content-Type", "application/x-tar") w.Header().Set("Content-Type", "application/x-tar")
if err := job.Run(); err != nil { if err := job.Run(); err != nil {
log.Errorf("%s", err.Error()) 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) w.WriteHeader(http.StatusNotFound)
} else if strings.Contains(err.Error(), "no such file or directory") { } 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"]) return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])

View file

@ -87,9 +87,9 @@ func (b *Builder) commit(id string, autoCmd []string, comment string) error {
} }
defer container.Unmount() defer container.Unmount()
} }
container := b.Daemon.Get(id) container, err := b.Daemon.Get(id)
if container == nil { if err != nil {
return fmt.Errorf("An error occured while creating the container") return err
} }
// Note: Actually copy the struct // Note: Actually copy the struct
@ -710,7 +710,11 @@ func fixPermissions(source, destination string, uid, gid int, destExisted bool)
func (b *Builder) clearTmp() { func (b *Builder) clearTmp() {
for c := range b.TmpContainers { 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 { if err := b.Daemon.Destroy(tmp); err != nil {
fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error()) fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
return return

View file

@ -28,9 +28,9 @@ func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status {
stderr = job.GetenvBool("stderr") stderr = job.GetenvBool("stderr")
) )
container := daemon.Get(name) container, err := daemon.Get(name)
if container == nil { if err != nil {
return job.Errorf("No such container: %s", name) return job.Error(err)
} }
//logs //logs

View file

@ -9,24 +9,29 @@ func (daemon *Daemon) ContainerChanges(job *engine.Job) engine.Status {
return job.Errorf("Usage: %s CONTAINER", job.Name) return job.Errorf("Usage: %s CONTAINER", job.Name)
} }
name := job.Args[0] name := job.Args[0]
if container := daemon.Get(name); container != nil {
outs := engine.NewTable("", 0) container, error := daemon.Get(name)
changes, err := container.Changes() if error != nil {
if err != nil { return job.Error(error)
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)
} }
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 return engine.StatusOK
} }

View file

@ -12,9 +12,9 @@ func (daemon *Daemon) ContainerCommit(job *engine.Job) engine.Status {
} }
name := job.Args[0] name := job.Args[0]
container := daemon.Get(name) container, err := daemon.Get(name)
if container == nil { if err != nil {
return job.Errorf("No such container: %s", name) return job.Error(err)
} }
var ( var (

View file

@ -1126,7 +1126,12 @@ func (container *Container) updateParentsHosts() error {
if ref.ParentID == "0" { if ref.ParentID == "0" {
continue 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() { 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) 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 { 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) { func (container *Container) getIpcContainer() (*Container, error) {
containerID := container.hostConfig.IpcMode.Container() containerID := container.hostConfig.IpcMode.Container()
c := container.daemon.Get(containerID) c, err := container.daemon.Get(containerID)
if c == nil { if err != nil {
return nil, fmt.Errorf("no such container to join IPC: %s", containerID) return nil, err
} }
if !c.IsRunning() { if !c.IsRunning() {
return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID) 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 { if len(parts) != 2 {
return nil, fmt.Errorf("no container specified to join network") return nil, fmt.Errorf("no container specified to join network")
} }
nc := container.daemon.Get(parts[1]) nc, err := container.daemon.Get(parts[1])
if nc == nil { if err != nil {
return nil, fmt.Errorf("no such container to join network: %s", parts[1]) return nil, err
} }
if !nc.IsRunning() { if !nc.IsRunning() {
return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1]) return nil, fmt.Errorf("cannot join network of a non running container: %s", parts[1])

View file

@ -16,18 +16,19 @@ func (daemon *Daemon) ContainerCopy(job *engine.Job) engine.Status {
resource = job.Args[1] resource = job.Args[1]
) )
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
if err != nil {
data, err := container.Copy(resource) return job.Error(err)
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
} }
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
} }

View file

@ -132,9 +132,9 @@ func (daemon *Daemon) GenerateSecurityOpt(ipcMode runconfig.IpcMode, pidMode run
return label.DisableSecOpt(), nil return label.DisableSecOpt(), nil
} }
if ipcContainer := ipcMode.Container(); ipcContainer != "" { if ipcContainer := ipcMode.Container(); ipcContainer != "" {
c := daemon.Get(ipcContainer) c, err := daemon.Get(ipcContainer)
if c == nil { if err != nil {
return nil, fmt.Errorf("no such container to join IPC: %s", ipcContainer) return nil, err
} }
if !c.IsRunning() { if !c.IsRunning() {
return nil, fmt.Errorf("cannot join IPC of a non running container: %s", ipcContainer) return nil, fmt.Errorf("cannot join IPC of a non running container: %s", ipcContainer)

View file

@ -155,28 +155,39 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
return nil return nil
} }
// Get looks for a container by the specified ID or name, and returns it. // Get looks for a container with the provided prefix
// If the container is not found, or if an error occurs, nil is returned. func (daemon *Daemon) Get(prefix string) (*Container, error) {
func (daemon *Daemon) Get(name string) *Container { if containerByID := daemon.containers.Get(prefix); containerByID != nil {
id, err := daemon.idIndex.Get(name)
if err == nil { // prefix is an exact match to a full container ID
return daemon.containers.Get(id) return containerByID, nil
} }
if c, _ := daemon.GetByName(name); c != nil { // Either GetByName finds an entity matching prefix exactly, or it doesn't.
return c // 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 { if containerId != "" {
log.Errorf("Short ID %s is ambiguous: please retry with more characters or use the full ID.\n", name)
// 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, // Exists returns a true if a container of the specified ID or name exists,
// false otherwise. // false otherwise.
func (daemon *Daemon) Exists(id string) bool { 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 { 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) children := make(map[string]*Container)
err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error { err = daemon.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
c := daemon.Get(e.ID()) c, err := daemon.Get(e.ID())
if c == nil { if err != nil {
return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p) return err
} }
children[p] = c children[p] = c
return nil return nil
@ -754,7 +765,10 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.
if err != nil { if err != nil {
return err return err
} }
child := daemon.Get(parts["name"]) child, err := daemon.Get(parts["name"])
if err != nil {
return err
}
if child == nil { if child == nil {
return fmt.Errorf("Could not get container for %s", parts["name"]) 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) { func (daemon *Daemon) SubscribeToContainerStats(name string) (chan interface{}, error) {
c := daemon.Get(name) c, err := daemon.Get(name)
if c == nil { if err != nil {
return nil, fmt.Errorf("no such container") return nil, err
} }
ch := daemon.statsCollector.collect(c) ch := daemon.statsCollector.collect(c)
return ch, nil return ch, nil
} }
func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface{}) error { func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface{}) error {
c := daemon.Get(name) c, err := daemon.Get(name)
if c == nil { if err != nil {
return fmt.Errorf("no such container") return err
} }
daemon.statsCollector.unsubscribe(c, ch) daemon.statsCollector.unsubscribe(c, ch)
return nil return nil

101
daemon/daemon_test.go Normal file
View 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)
}

View file

@ -17,10 +17,10 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
removeVolume := job.GetenvBool("removeVolume") removeVolume := job.GetenvBool("removeVolume")
removeLink := job.GetenvBool("removeLink") removeLink := job.GetenvBool("removeLink")
forceRemove := job.GetenvBool("forceRemove") forceRemove := job.GetenvBool("forceRemove")
container := daemon.Get(name)
if container == nil { container, err := daemon.Get(name)
return job.Errorf("No such container: %s", name) if err != nil {
return job.Error(err)
} }
if removeLink { if removeLink {
@ -36,7 +36,7 @@ func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
if pe == nil { if pe == nil {
return job.Errorf("Cannot get parent %s for name %s", parent, name) 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 { if parentContainer != nil {
parentContainer.DisableLink(n) parentContainer.DisableLink(n)

View file

@ -97,10 +97,9 @@ func (d *Daemon) unregisterExecCommand(execConfig *execConfig) {
} }
func (d *Daemon) getActiveContainer(name string) (*Container, error) { func (d *Daemon) getActiveContainer(name string) (*Container, error) {
container := d.Get(name) container, err := d.Get(name)
if err != nil {
if container == nil { return nil, err
return nil, fmt.Errorf("No such container: %s", name)
} }
if !container.IsRunning() { if !container.IsRunning() {

View file

@ -11,20 +11,23 @@ func (daemon *Daemon) ContainerExport(job *engine.Job) engine.Status {
return job.Errorf("Usage: %s container_id", job.Name) return job.Errorf("Usage: %s container_id", job.Name)
} }
name := job.Args[0] 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) container, err := daemon.Get(name)
if _, err := io.Copy(job.Stdout, data); err != nil { if err != nil {
return job.Errorf("%s: %s", name, err) return job.Error(err)
}
// FIXME: factor job-specific LogEvent to engine.Job.Run()
container.LogEvent("export")
return engine.StatusOK
} }
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
} }

View file

@ -13,60 +13,62 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status {
return job.Errorf("usage: %s NAME", job.Name) return job.Errorf("usage: %s NAME", job.Name)
} }
name := job.Args[0] name := job.Args[0]
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
container.Lock() if err != nil {
defer container.Unlock() return job.Error(err)
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
}
out := &engine.Env{} container.Lock()
out.SetJson("Id", container.ID) defer container.Unlock()
out.SetAuto("Created", container.Created) if job.GetenvBool("raw") {
out.SetJson("Path", container.Path) b, err := json.Marshal(&struct {
out.SetList("Args", container.Args) *Container
out.SetJson("Config", container.Config) HostConfig *runconfig.HostConfig
out.SetJson("State", container.State) }{container, container.hostConfig})
out.Set("Image", container.ImageID) if err != nil {
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 job.Error(err)
} }
job.Stdout.Write(b)
return engine.StatusOK 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 { func (daemon *Daemon) ContainerExecInspect(job *engine.Job) engine.Status {

View file

@ -38,22 +38,23 @@ func (daemon *Daemon) ContainerKill(job *engine.Job) engine.Status {
} }
} }
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait()) if err != nil {
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL { return job.Error(err)
if err := container.Kill(); err != nil { }
return job.Errorf("Cannot kill container %s: %s", name, err)
} // If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
container.LogEvent("kill") if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
} else { if err := container.Kill(); err != nil {
// Otherwise, just send the requested signal return job.Errorf("Cannot kill container %s: %s", name, err)
if err := container.KillSig(int(sig)); err != nil {
return job.Errorf("Cannot kill container %s: %s", name, err)
}
// FIXME: Add event for signals
} }
container.LogEvent("kill")
} else { } 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 return engine.StatusOK
} }

View file

@ -63,16 +63,16 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
var beforeCont, sinceCont *Container var beforeCont, sinceCont *Container
if before != "" { if before != "" {
beforeCont = daemon.Get(before) beforeCont, err = daemon.Get(before)
if beforeCont == nil { if err != nil {
return job.Error(fmt.Errorf("Could not find container with name or id %s", before)) return job.Error(err)
} }
} }
if since != "" { if since != "" {
sinceCont = daemon.Get(since) sinceCont, err = daemon.Get(since)
if sinceCont == nil { if err != nil {
return job.Error(fmt.Errorf("Could not find container with name or id %s", since)) return job.Error(err)
} }
} }

View file

@ -40,9 +40,9 @@ func (daemon *Daemon) ContainerLogs(job *engine.Job) engine.Status {
if tail == "" { if tail == "" {
tail = "all" tail = "all"
} }
container := daemon.Get(name) container, err := daemon.Get(name)
if container == nil { if err != nil {
return job.Errorf("No such container: %s", name) return job.Error(err)
} }
cLog, err := container.ReadLog("json") cLog, err := container.ReadLog("json")
if err != nil && os.IsNotExist(err) { if err != nil && os.IsNotExist(err) {

View file

@ -9,9 +9,9 @@ func (daemon *Daemon) ContainerPause(job *engine.Job) engine.Status {
return job.Errorf("Usage: %s CONTAINER", job.Name) return job.Errorf("Usage: %s CONTAINER", job.Name)
} }
name := job.Args[0] name := job.Args[0]
container := daemon.Get(name) container, err := daemon.Get(name)
if container == nil { if err != nil {
return job.Errorf("No such container: %s", name) return job.Error(err)
} }
if err := container.Pause(); err != nil { if err := container.Pause(); err != nil {
return job.Errorf("Cannot pause container %s: %s", name, err) 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) return job.Errorf("Usage: %s CONTAINER", job.Name)
} }
name := job.Args[0] name := job.Args[0]
container := daemon.Get(name) container, err := daemon.Get(name)
if container == nil { if err != nil {
return job.Errorf("No such container: %s", name) return job.Error(err)
} }
if err := container.Unpause(); err != nil { if err := container.Unpause(); err != nil {
return job.Errorf("Cannot unpause container %s: %s", name, err) return job.Errorf("Cannot unpause container %s: %s", name, err)

View file

@ -11,9 +11,9 @@ func (daemon *Daemon) ContainerRename(job *engine.Job) engine.Status {
oldName := job.Args[0] oldName := job.Args[0]
newName := job.Args[1] newName := job.Args[1]
container := daemon.Get(oldName) container, err := daemon.Get(oldName)
if container == nil { if err != nil {
return job.Errorf("No such container: %s", oldName) return job.Error(err)
} }
oldName = container.Name oldName = container.Name

View file

@ -19,14 +19,14 @@ func (daemon *Daemon) ContainerResize(job *engine.Job) engine.Status {
if err != nil { if err != nil {
return job.Error(err) return job.Error(err)
} }
container, err := daemon.Get(name)
if container := daemon.Get(name); container != nil { if err != nil {
if err := container.Resize(height, width); err != nil { return job.Error(err)
return job.Error(err)
}
return engine.StatusOK
} }
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 { func (daemon *Daemon) ContainerExecResize(job *engine.Job) engine.Status {

View file

@ -15,13 +15,13 @@ func (daemon *Daemon) ContainerRestart(job *engine.Job) engine.Status {
if job.EnvExists("t") { if job.EnvExists("t") {
t = job.GetenvInt("t") t = job.GetenvInt("t")
} }
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
if err := container.Restart(int(t)); err != nil { if err != nil {
return job.Errorf("Cannot restart container %s: %s\n", name, err) return job.Error(err)
}
container.LogEvent("restart")
} else {
return job.Errorf("No such container: %s\n", name)
} }
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 return engine.StatusOK
} }

View file

@ -14,12 +14,12 @@ func (daemon *Daemon) ContainerStart(job *engine.Job) engine.Status {
return job.Errorf("Usage: %s container_id", job.Name) return job.Errorf("Usage: %s container_id", job.Name)
} }
var ( var (
name = job.Args[0] name = job.Args[0]
container = daemon.Get(name)
) )
if container == nil { container, err := daemon.Get(name)
return job.Errorf("No such container: %s", name) if err != nil {
return job.Error(err)
} }
if container.IsPaused() { if container.IsPaused() {

View file

@ -15,16 +15,16 @@ func (daemon *Daemon) ContainerStop(job *engine.Job) engine.Status {
if job.EnvExists("t") { if job.EnvExists("t") {
t = job.GetenvInt("t") t = job.GetenvInt("t")
} }
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
if !container.IsRunning() { if err != nil {
return job.Errorf("Container already stopped") return job.Error(err)
}
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)
} }
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 return engine.StatusOK
} }

View file

@ -21,59 +21,59 @@ func (daemon *Daemon) ContainerTop(job *engine.Job) engine.Status {
psArgs = job.Args[1] psArgs = job.Args[1]
} }
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
if !container.IsRunning() { if err != nil {
return job.Errorf("Container %s is not running", name) return job.Error(err)
}
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
} }
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
} }

View file

@ -266,9 +266,9 @@ func (container *Container) applyVolumesFrom() error {
continue continue
} }
c := container.daemon.Get(id) c, err := container.daemon.Get(id)
if c == nil { if err != nil {
return fmt.Errorf("container %s not found, impossible to mount its volumes", id) return err
} }
var ( var (

View file

@ -11,10 +11,11 @@ func (daemon *Daemon) ContainerWait(job *engine.Job) engine.Status {
return job.Errorf("Usage: %s", job.Name) return job.Errorf("Usage: %s", job.Name)
} }
name := job.Args[0] name := job.Args[0]
if container := daemon.Get(name); container != nil { container, err := daemon.Get(name)
status, _ := container.WaitStop(-1 * time.Second) if err != nil {
job.Printf("%d\n", status) return job.Errorf("%s: %s", job.Name, err.Error())
return engine.StatusOK
} }
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
} }

View file

@ -325,7 +325,7 @@ func TestPostCreateNull(t *testing.T) {
containerAssertExists(eng, containerID, t) containerAssertExists(eng, containerID, t)
c := daemon.Get(containerID) c, _ := daemon.Get(containerID)
if c.Config.Cpuset != "" { if c.Config.Cpuset != "" {
t.Fatalf("Cpuset should have been empty - instead its:" + c.Config.Cpuset) t.Fatalf("Cpuset should have been empty - instead its:" + c.Config.Cpuset)
} }

View file

@ -282,12 +282,12 @@ func TestDaemonCreate(t *testing.T) {
} }
// Make sure we can get the container with Get() // 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") t.Errorf("Unable to get newly created container")
} }
// Make sure it is the right 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") 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 // Make sure daemon.Get() refuses to return the unexisting container
if daemon.Get(container.ID) != nil { if c, _ := daemon.Get(container.ID); c != nil {
t.Errorf("Unable to get newly created container") t.Errorf("Got a container that should not exist")
} }
// Test double destroy // Test double destroy
@ -407,16 +407,16 @@ func TestGet(t *testing.T) {
container3, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t) container3, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t)
defer daemon.Destroy(container3) defer daemon.Destroy(container3)
if daemon.Get(container1.ID) != container1 { if c, _ := daemon.Get(container1.ID); c != container1 {
t.Errorf("Get(test1) returned %v while expecting %v", daemon.Get(container1.ID), container1) t.Errorf("Get(test1) returned %v while expecting %v", c, container1)
} }
if daemon.Get(container2.ID) != container2 { if c, _ := daemon.Get(container2.ID); c != container2 {
t.Errorf("Get(test2) returned %v while expecting %v", daemon.Get(container2.ID), container2) t.Errorf("Get(test2) returned %v while expecting %v", c, container2)
} }
if daemon.Get(container3.ID) != container3 { if c, _ := daemon.Get(container3.ID); c != container3 {
t.Errorf("Get(test3) returned %v while expecting %v", daemon.Get(container3.ID), 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) t.Fatal(err)
} }
container := daemon.Get(id) container, err := daemon.Get(id)
if container == nil { if err != nil {
t.Fatalf("Couldn't fetch test container %s", id) t.Fatal(err)
} }
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() { 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 { if runningCount != 0 {
t.Fatalf("Expected 0 container alive, %d found", runningCount) t.Fatalf("Expected 0 container alive, %d found", runningCount)
} }
container3 := daemon2.Get(container1.ID) container3, err := daemon2.Get(container1.ID)
if container3 == nil { if err != nil {
t.Fatal("Unable to Get container") t.Fatal("Unable to Get container")
} }
if err := container3.Run(); err != nil { if err := container3.Run(); err != nil {
@ -666,16 +666,21 @@ func TestDefaultContainerName(t *testing.T) {
t.Fatal(err) 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 containerID := container.ID
if container.Name != "/some_name" { if container.Name != "/some_name" {
t.Fatalf("Expect /some_name got %s", container.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") 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) 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) 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 containerID := container.ID
if container.Name == "" { if container.Name == "" {
t.Fatalf("Expected not empty 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) log.Fatalf("Could not lookup container %s by its name", container.Name)
} else if c.ID != containerID { } else if c.ID != containerID {
log.Fatalf("Looking up container name %s returned id %s instead of %s", container.Name, 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) 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 { if container.Name != "/"+test.Name {
t.Fatalf("Expect /%s got %s", test.Name, container.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) t.Fatalf("Couldn't retrieve test container as /%s", test.Name)
} else if c.ID != container.ID { } else if c.ID != container.ID {
t.Fatalf("Container /%s has ID %s instead of %s", test.Name, 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) 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") webapp, err := daemon.GetByName("/webapp")
if err != nil { if err != nil {
@ -778,7 +792,10 @@ func TestLinkChildContainer(t *testing.T) {
t.Fatal(err) 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 { if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
t.Fatal(err) t.Fatal(err)
@ -804,7 +821,10 @@ func TestGetAllChildren(t *testing.T) {
t.Fatal(err) 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") webapp, err := daemon.GetByName("/webapp")
if err != nil { if err != nil {
@ -820,7 +840,10 @@ func TestGetAllChildren(t *testing.T) {
t.Fatal(err) 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 { if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -117,7 +117,7 @@ func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) { func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
daemon := mkDaemonFromEngine(eng, t) 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)) 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 { func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
daemon := mkDaemonFromEngine(eng, t) daemon := mkDaemonFromEngine(eng, t)
c := daemon.Get(id) c, err := daemon.Get(id)
if c == nil { if err != nil {
t.Fatal(fmt.Errorf("No such container: %s", id)) t.Fatal(err)
} }
return c return c
} }

View file

@ -10,10 +10,8 @@ import (
) )
var ( var (
// ErrNoID is thrown when attempting to use empty prefixes ErrEmptyPrefix = errors.New("Prefix can't be empty")
ErrNoID = errors.New("prefix can't be empty") ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix")
// ErrDuplicateID is thrown when a duplicated id was found
ErrDuplicateID = errors.New("multiple IDs were found")
) )
func init() { func init() {
@ -47,7 +45,7 @@ func (idx *TruncIndex) addID(id string) error {
return fmt.Errorf("illegal character: ' '") return fmt.Errorf("illegal character: ' '")
} }
if id == "" { if id == "" {
return ErrNoID return ErrEmptyPrefix
} }
if _, exists := idx.ids[id]; exists { if _, exists := idx.ids[id]; exists {
return fmt.Errorf("id already exists: '%s'", id) 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 // Get retrieves an ID from the TruncIndex. If there are multiple IDs
// with the given prefix, an error is thrown. // with the given prefix, an error is thrown.
func (idx *TruncIndex) Get(s string) (string, error) { func (idx *TruncIndex) Get(s string) (string, error) {
idx.RLock() if s == "" {
defer idx.RUnlock() return "", ErrEmptyPrefix
}
var ( var (
id string id string
) )
if s == "" {
return "", ErrNoID
}
subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error { subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
if id != "" { if id != "" {
// we haven't found the ID if there are two or more IDs // we haven't found the ID if there are two or more IDs
id = "" id = ""
return ErrDuplicateID return ErrAmbiguousPrefix
} }
id = string(prefix) id = string(prefix)
return nil return nil
} }
idx.RLock()
defer idx.RUnlock()
if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil { if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
return "", fmt.Errorf("no such id: %s", s) return "", err
} }
if id != "" { if id != "" {
return id, nil return id, nil

View file

@ -59,6 +59,11 @@ func TestTruncIndex(t *testing.T) {
assertIndexGet(t, index, id[:4], "", true) assertIndexGet(t, index, id[:4], "", true)
assertIndexGet(t, index, id[:1], "", 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 // 7 characters should NOT conflict
assertIndexGet(t, index, id[:7], id, false) assertIndexGet(t, index, id[:7], id, false)
assertIndexGet(t, index, id2[:7], id2, false) assertIndexGet(t, index, id2[:7], id2, false)