Skip to content

Commit

Permalink
Change LookupContainer logic to match Docker
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
mheon committed Mar 6, 2019
1 parent cfe4c0c commit 6c1630c
Showing 1 changed file with 105 additions and 62 deletions.
167 changes: 105 additions & 62 deletions libpod/boltdb_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
return s.getContainerFromDB(id, ctr, ctrBucket)
}

// 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 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
}
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 {
return errors.Wrapf(ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
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)
Expand Down Expand Up @@ -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
Expand All @@ -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
}
return s.getPodFromDB(id, pod, podBkt)
}

// 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 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
}
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 {
return errors.Wrapf(ErrNoSuchPod, "no pod with name or ID %s found", idOrName)
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
Expand Down

0 comments on commit 6c1630c

Please sign in to comment.