aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpod/boltdb_state.go44
-rw-r--r--libpod/in_memory_state.go44
-rw-r--r--libpod/state.go15
-rw-r--r--libpod/state_test.go36
4 files changed, 127 insertions, 12 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 5bc15dd7f..104ec78e9 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -783,6 +783,50 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// RewriteContainerConfig rewrites a container's configuration.
+// WARNING: This function is DANGEROUS. Do not use without reading the full
+// comment on this function in state.go.
+func (s *BoltState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ newCfgJSON, err := json.Marshal(newCfg)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling new configuration JSON for container %s", ctr.ID())
+ }
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer s.closeDBCon(db)
+
+ err = db.Update(func(tx *bolt.Tx) error {
+ ctrBkt, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrDB := ctrBkt.Bucket([]byte(ctr.ID()))
+ if ctrDB == nil {
+ ctr.valid = false
+ return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID())
+ }
+
+ if err := ctrDB.Put(configKey, newCfgJSON); err != nil {
+ return errors.Wrapf(err, "error updating container %s config JSON", ctr.ID())
+ }
+
+ return nil
+ })
+ return err
+}
+
// Pod retrieves a pod given its full ID
func (s *BoltState) Pod(id string) (*Pod, error) {
if id == "" {
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 314799309..2f8fafa39 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -378,6 +378,38 @@ func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) {
return arr, nil
}
+// AllContainers retrieves all containers from the state
+func (s *InMemoryState) AllContainers() ([]*Container, error) {
+ ctrs := make([]*Container, 0, len(s.containers))
+ for _, ctr := range s.containers {
+ if s.namespace == "" || ctr.config.Namespace == s.namespace {
+ ctrs = append(ctrs, ctr)
+ }
+ }
+
+ return ctrs, nil
+}
+
+// RewriteContainerConfig rewrites a container's configuration.
+// This function is DANGEROUS, even with an in-memory state.
+// Please read the full comment on it in state.go before using it.
+func (s *InMemoryState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error {
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ // If the container does not exist, return error
+ stateCtr, ok := s.containers[ctr.ID()]
+ if !ok {
+ ctr.valid = false
+ return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID())
+ }
+
+ stateCtr.config = newCfg
+
+ return nil
+}
+
// Volume retrieves a volume from its full name
func (s *InMemoryState) Volume(name string) (*Volume, error) {
if name == "" {
@@ -486,18 +518,6 @@ func (s *InMemoryState) AllVolumes() ([]*Volume, error) {
return allVols, nil
}
-// AllContainers retrieves all containers from the state
-func (s *InMemoryState) AllContainers() ([]*Container, error) {
- ctrs := make([]*Container, 0, len(s.containers))
- for _, ctr := range s.containers {
- if s.namespace == "" || ctr.config.Namespace == s.namespace {
- ctrs = append(ctrs, ctr)
- }
- }
-
- return ctrs, nil
-}
-
// Pod retrieves a pod from the state from its full ID
func (s *InMemoryState) Pod(id string) (*Pod, error) {
if id == "" {
diff --git a/libpod/state.go b/libpod/state.go
index 88d89f673..42dbe9cc1 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -97,6 +97,21 @@ type State interface {
// returned.
AllContainers() ([]*Container, error)
+ // PLEASE READ FULL DESCRIPTION BEFORE USING.
+ // Rewrite a container's configuration.
+ // This function breaks libpod's normal prohibition on a read-only
+ // configuration, and as such should be used EXTREMELY SPARINGLY and
+ // only in very specific circumstances.
+ // Specifically, it is ONLY safe to use thing function to make changes
+ // that result in a functionally identical configuration (migrating to
+ // newer, but identical, configuration fields), or during libpod init
+ // WHILE HOLDING THE ALIVE LOCK (to prevent other libpod instances from
+ // being initialized).
+ // 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
+
// Accepts full ID of pod.
// If the pod given is not in the set namespace, an error will be
// returned.
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 4bd00ab55..dd03903ac 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -1298,6 +1298,42 @@ func TestCannotUseBadIDAsGenericDependency(t *testing.T) {
})
}
+func TestRewriteContainerConfigDoesNotExist(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ err := state.RewriteContainerConfig(&Container{}, &ContainerConfig{})
+ assert.Error(t, err)
+ })
+}
+
+func TestRewriteContainerConfigNotInState(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testCtr, err := getTestCtr1(manager)
+ assert.NoError(t, err)
+ err = state.RewriteContainerConfig(testCtr, &ContainerConfig{})
+ assert.Error(t, err)
+ })
+}
+
+func TestRewriteContainerConfigRewritesConfig(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testCtr, err := getTestCtr1(manager)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ testCtr.config.LogPath = "/another/path/"
+
+ err = state.RewriteContainerConfig(testCtr, testCtr.config)
+ assert.NoError(t, err)
+
+ testCtrFromState, err := state.Container(testCtr.ID())
+ assert.NoError(t, err)
+
+ testContainersEqual(t, testCtrFromState, testCtr, true)
+ })
+}
+
func TestGetPodDoesNotExist(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
_, err := state.Pod("doesnotexist")