Rewrite Rename backend in a more atomic fashion

Move the core of renaming logic into the DB. This guarantees a
lot more atomicity than we have right now (our current solution,
removing the container from the DB and re-creating it, is *VERY*
not atomic and prone to leaving a corrupted state behind if
things go wrong. Moving things into the DB allows us to remove
most, but not all, of this - there's still a potential scenario
where the c/storage rename fails but the Podman rename succeeds,
and we end up with a mismatched state.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2021-03-02 11:58:32 -05:00
parent 426178a499
commit 43e899c2ec
5 changed files with 188 additions and 99 deletions

View File

@ -822,6 +822,46 @@ func (s *InMemoryState) RewriteContainerConfig(ctr *Container, newCfg *Container
return nil
}
// SafeRewriteContainerConfig rewrites a container's configuration.
// It's safer than RewriteContainerConfig, but still has limitations. Please
// read the comment in state.go before using.
func (s *InMemoryState) SafeRewriteContainerConfig(ctr *Container, oldName, newName string, newCfg *ContainerConfig) error {
if !ctr.valid {
return define.ErrCtrRemoved
}
if _, err := s.nameIndex.Get(newName); err == nil {
return errors.Wrapf(define.ErrCtrExists, "name %s is in use", newName)
}
// If the container does not exist, return error
stateCtr, ok := s.containers[ctr.ID()]
if !ok {
ctr.valid = false
return errors.Wrapf(define.ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID())
}
// Change name in registry.
if s.namespace != "" {
nsIndex, ok := s.namespaceIndexes[s.namespace]
if !ok {
return define.ErrInternal
}
nsIndex.nameIndex.Release(oldName)
if err := nsIndex.nameIndex.Reserve(newName, ctr.ID()); err != nil {
return errors.Wrapf(err, "error registering name %s", newName)
}
}
s.nameIndex.Release(oldName)
if err := s.nameIndex.Reserve(newName, ctr.ID()); err != nil {
return errors.Wrapf(err, "error registering name %s", newName)
}
stateCtr.config = newCfg
return nil
}
// RewritePodConfig rewrites a pod's configuration.
// This function is DANGEROUS, even with in-memory state.
// Please read the full comment on it in state.go before using it.