Change LookupContainer logic to match Docker

When looking up a container or pod by from user input, we handle
collisions between names and IDs differently than Docker at
present.

In Docker, when there is a container with an ID starting with
"c1" and a container named "c1", commands on "c1" will always act
on the container named "c1". For the same scenario in podman, we
throw an error about name collision.

Change Podman to follow Docker, by returning the named container
or pod instead of erroring.

This should also have a positive effect on performance in the
lookup-by-full-name case, which no longer needs to fully traverse
the list of all pods or containers.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2019-03-06 14:26:57 -05:00
parent cfe4c0c540
commit 6c1630c682

View File

@ -382,6 +382,11 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
return err
}
namesBucket, err := getNamesBucket(tx)
if err != nil {
return err
}
nsBucket, err := getNSBucket(tx)
if err != nil {
return err
@ -395,41 +400,59 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
// It might not be in our namespace, but
// getContainerFromDB() will handle that case.
id = []byte(idOrName)
} else {
// They did not give us a full container ID.
// Search for partial ID or full name matches
// Use else-if in case the name is set to a partial ID
exists := false
err = idBucket.ForEach(func(checkID, checkName []byte) error {
// If the container isn't in our namespace, we
// can't match it
if s.namespaceBytes != nil {
ns := nsBucket.Get(checkID)
if !bytes.Equal(ns, s.namespaceBytes) {
return nil
}
}
if string(checkName) == idOrName {
if exists {
return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
}
id = checkID
exists = true
} else if strings.HasPrefix(string(checkID), idOrName) {
if exists {
return errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
}
id = checkID
exists = true
}
return s.getContainerFromDB(id, ctr, ctrBucket)
}
return nil
})
if err != nil {
return err
} else if !exists {
return errors.Wrapf(ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
// Next, check if the full name was given
isPod := false
fullID := namesBucket.Get([]byte(idOrName))
if fullID != nil {
// The name exists and maps to an ID.
// However, we are not yet certain the ID is a
// container.
ctrExists = ctrBucket.Bucket(fullID)
if ctrExists != nil {
// A container bucket matching the full ID was
// found.
return s.getContainerFromDB(fullID, ctr, ctrBucket)
}
// Don't error if we have a name match but it's not a
// container - there's a chance we have a container with
// an ID starting with those characters.
// However, so we can return a good error, note whether
// this is a pod.
isPod = true
}
// We were not given a full container ID or name.
// Search for partial ID matches.
exists := false
err = idBucket.ForEach(func(checkID, checkName []byte) error {
// If the container isn't in our namespace, we
// can't match it
if s.namespaceBytes != nil {
ns := nsBucket.Get(checkID)
if !bytes.Equal(ns, s.namespaceBytes) {
return nil
}
}
if strings.HasPrefix(string(checkID), idOrName) {
if exists {
return errors.Wrapf(ErrCtrExists, "more than one result for container ID %s", idOrName)
}
id = checkID
exists = true
}
return nil
})
if err != nil {
return err
} else if !exists {
if isPod {
return errors.Wrapf(ErrNoSuchCtr, "%s is a pod, not a container", idOrName)
}
return errors.Wrapf(ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
}
return s.getContainerFromDB(id, ctr, ctrBucket)
@ -941,6 +964,11 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
return err
}
namesBkt, err := getNamesBucket(tx)
if err != nil {
return err
}
nsBkt, err := getNSBucket(tx)
if err != nil {
return err
@ -954,41 +982,56 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
// It might not be in our namespace, but getPodFromDB()
// will handle that case.
id = []byte(idOrName)
} else {
// They did not give us a full pod ID.
// Search for partial ID or full name matches
// Use else-if in case the name is set to a partial ID
exists := false
err = idBucket.ForEach(func(checkID, checkName []byte) error {
// If the pod isn't in our namespace, we
// can't match it
if s.namespaceBytes != nil {
ns := nsBkt.Get(checkID)
if !bytes.Equal(ns, s.namespaceBytes) {
return nil
}
}
if string(checkName) == idOrName {
if exists {
return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName)
}
id = checkID
exists = true
} else if strings.HasPrefix(string(checkID), idOrName) {
if exists {
return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName)
}
id = checkID
exists = true
}
return s.getPodFromDB(id, pod, podBkt)
}
return nil
})
if err != nil {
return err
} else if !exists {
return errors.Wrapf(ErrNoSuchPod, "no pod with name or ID %s found", idOrName)
// Next, check if the full name was given
isCtr := false
fullID := namesBkt.Get([]byte(idOrName))
if fullID != nil {
// The name exists and maps to an ID.
// However, we aren't yet sure if the ID is a pod.
podExists = podBkt.Bucket(fullID)
if podExists != nil {
// A pod bucket matching the full ID was found.
return s.getPodFromDB(fullID, pod, podBkt)
}
// Don't error if we have a name match but it's not a
// pod - there's a chance we have a pod with an ID
// starting with those characters.
// However, so we can return a good error, note whether
// this is a container.
isCtr = true
}
// They did not give us a full pod name or ID.
// Search for partial ID matches.
exists := false
err = idBucket.ForEach(func(checkID, checkName []byte) error {
// If the pod isn't in our namespace, we
// can't match it
if s.namespaceBytes != nil {
ns := nsBkt.Get(checkID)
if !bytes.Equal(ns, s.namespaceBytes) {
return nil
}
}
if strings.HasPrefix(string(checkID), idOrName) {
if exists {
return errors.Wrapf(ErrPodExists, "more than one result for ID or name %s", idOrName)
}
id = checkID
exists = true
}
return nil
})
if err != nil {
return err
} else if !exists {
if isCtr {
return errors.Wrapf(ErrNoSuchPod, "%s is a container, not a pod", idOrName)
}
return errors.Wrapf(ErrNoSuchPod, "no pod with name or ID %s found", idOrName)
}
// We might have found a container ID, but it's OK