Add ability to rewrite pod configs in the database

Necessary for rewriting lock IDs as part of renumber.

Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
Matthew Heon
2019-02-14 17:52:49 -05:00
parent 7fdd20ae5a
commit a3dbb7a837
4 changed files with 109 additions and 0 deletions

View File

@ -827,6 +827,50 @@ func (s *BoltState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConf
return err
}
// RewritePodConfig rewrites a pod's configuration.
// WARNING: This function is DANGEROUS. Do not use without reading the full
// comment on this function in state.go.
func (s *BoltState) RewritePodConfig(pod *Pod, newCfg *PodConfig) error {
if !s.valid {
return ErrDBClosed
}
if !pod.valid {
return ErrPodRemoved
}
newCfgJSON, err := json.Marshal(newCfg)
if err != nil {
return errors.Wrapf(err, "error marshalling new configuration JSON for container %s", pod.ID())
}
db, err := s.getDBCon()
if err != nil {
return err
}
defer s.closeDBCon(db)
err = db.Update(func(tx *bolt.Tx) error {
podBkt, err := getPodBucket(tx)
if err != nil {
return err
}
podDB := podBkt.Bucket([]byte(pod.ID()))
if podDB == nil {
pod.valid = false
return errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in DB", pod.ID())
}
if err := podDB.Put(configKey, newCfgJSON); err != nil {
return errors.Wrapf(err, "error updating pod %s config JSON", pod.ID())
}
return nil
})
return err
}
// Pod retrieves a pod given its full ID
func (s *BoltState) Pod(id string) (*Pod, error) {
if id == "" {

View File

@ -410,6 +410,26 @@ func (s *InMemoryState) RewriteContainerConfig(ctr *Container, newCfg *Container
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.
func (s *InMemoryState) RewritePodConfig(pod *Pod, newCfg *PodConfig) error {
if !pod.valid {
return ErrPodRemoved
}
// If the pod does not exist, return error
statePod, ok := s.pods[pod.ID()]
if !ok {
pod.valid = false
return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found in state", pod.ID())
}
statePod.config = newCfg
return nil
}
// Volume retrieves a volume from its full name
func (s *InMemoryState) Volume(name string) (*Volume, error) {
if name == "" {

View File

@ -107,10 +107,19 @@ type State interface {
// newer, but identical, configuration fields), or during libpod init
// WHILE HOLDING THE ALIVE LOCK (to prevent other libpod instances from
// being initialized).
// Most things in config can be changed by this, but container ID and
// name ABSOLUTELY CANNOT BE ALTERED. If you do so, there is a high
// potential for database corruption.
// There are a lot of capital letters and conditions here, but the short
// answer is this: use this only very sparingly, and only if you really
// know what you're doing.
RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error
// PLEASE READ THE ABOVE DESCRIPTION BEFORE USING.
// This function is identical to RewriteContainerConfig, save for the
// fact that it is used with pods instead.
// It is subject to the same conditions as RewriteContainerConfig.
// Please do not use this unless you know what you're doing.
RewritePodConfig(pod *Pod, newCfg *PodConfig) error
// Accepts full ID of pod.
// If the pod given is not in the set namespace, an error will be

View File

@ -1334,6 +1334,42 @@ func TestRewriteContainerConfigRewritesConfig(t *testing.T) {
})
}
func TestRewritePodConfigDoesNotExist(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
err := state.RewritePodConfig(&Pod{}, &PodConfig{})
assert.Error(t, err)
})
}
func TestRewritePodConfigNotInState(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
testPod, err := getTestPod1(manager)
assert.NoError(t, err)
err = state.RewritePodConfig(testPod, &PodConfig{})
assert.Error(t, err)
})
}
func TestRewritePodConfigRewritesConfig(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
testPod, err := getTestPod1(manager)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
testPod.config.CgroupParent = "/another_cgroup_parent"
err = state.RewritePodConfig(testPod, testPod.config)
assert.NoError(t, err)
testPodFromState, err := state.Pod(testPod.ID())
assert.NoError(t, err)
testPodsEqual(t, testPodFromState, testPod, true)
})
}
func TestGetPodDoesNotExist(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
_, err := state.Pod("doesnotexist")