summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2018-05-14 19:30:11 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-05-17 23:10:12 +0000
commit018d2c6b1d23acf7fe67e809498bc354eaf6becf (patch)
tree7e4a605898905c0e0b259717d642ecbabf2516d3
parentc45d4c6d5ce83a89f4c536e529c2a6e7a770837e (diff)
downloadpodman-018d2c6b1d23acf7fe67e809498bc354eaf6becf.tar.gz
podman-018d2c6b1d23acf7fe67e809498bc354eaf6becf.tar.bz2
podman-018d2c6b1d23acf7fe67e809498bc354eaf6becf.zip
Add pod state
Add a mutable state to pods, and database backend sutable for modifying and updating said state. Signed-off-by: Matthew Heon <matthew.heon@gmail.com> Closes: #784 Approved by: rhatdan
-rw-r--r--libpod/boltdb_state.go120
-rw-r--r--libpod/boltdb_state_internal.go17
-rw-r--r--libpod/common_test.go30
-rw-r--r--libpod/in_memory_state.go30
-rw-r--r--libpod/pod.go23
-rw-r--r--libpod/runtime_pod.go21
-rw-r--r--libpod/state.go4
-rw-r--r--libpod/state_test.go82
8 files changed, 299 insertions, 28 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index e1f29b16a..77b2fe5b7 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -623,6 +623,7 @@ func (s *BoltState) Pod(id string) (*Pod, error) {
pod := new(Pod)
pod.config = new(PodConfig)
+ pod.state = new(podState)
db, err := s.getDBCon()
if err != nil {
@@ -657,6 +658,7 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) {
pod := new(Pod)
pod.config = new(PodConfig)
+ pod.state = new(podState)
db, err := s.getDBCon()
if err != nil {
@@ -957,9 +959,14 @@ func (s *BoltState) AddPod(pod *Pod) error {
podID := []byte(pod.ID())
podName := []byte(pod.Name())
- podJSON, err := json.Marshal(pod.config)
+ podConfigJSON, err := json.Marshal(pod.config)
if err != nil {
- return errors.Wrapf(err, "error marshalling pod %s JSON", pod.ID())
+ return errors.Wrapf(err, "error marshalling pod %s config to JSON", pod.ID())
+ }
+
+ podStateJSON, err := json.Marshal(pod.state)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling pod %s state to JSON", pod.ID())
}
db, err := s.getDBCon()
@@ -1011,10 +1018,14 @@ func (s *BoltState) AddPod(pod *Pod) error {
return errors.Wrapf(err, "error creating bucket for pod %s containers", pod.ID())
}
- if err := newPod.Put(configKey, podJSON); err != nil {
+ if err := newPod.Put(configKey, podConfigJSON); err != nil {
return errors.Wrapf(err, "error storing pod %s configuration in DB", pod.ID())
}
+ if err := newPod.Put(stateKey, podStateJSON); err != nil {
+ return errors.Wrapf(err, "error storing pod %s state JSON in DB", pod.ID())
+ }
+
// Add us to the ID and names buckets
if err := idsBkt.Put(podID, podName); err != nil {
return errors.Wrapf(err, "error storing pod %s ID in DB", pod.ID())
@@ -1296,6 +1307,108 @@ func (s *BoltState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
return err
}
+// UpdatePod updates a pod's state from the database
+func (s *BoltState) UpdatePod(pod *Pod) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ if !pod.valid {
+ return ErrPodRemoved
+ }
+
+ newState := new(podState)
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ podID := []byte(pod.ID())
+
+ err = db.View(func(tx *bolt.Tx) error {
+ podBkt, err := getPodBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ podDB := podBkt.Bucket(podID)
+ if podDB == nil {
+ pod.valid = false
+ return errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in database", pod.ID())
+ }
+
+ // Get the pod state JSON
+ podStateBytes := podDB.Get(stateKey)
+ if podStateBytes == nil {
+ return errors.Wrapf(ErrInternal, "pod %s is missing state key in DB", pod.ID())
+ }
+
+ if err := json.Unmarshal(podStateBytes, newState); err != nil {
+ return errors.Wrapf(err, "error unmarshalling pod %s state JSON", pod.ID())
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ pod.state = newState
+
+ return nil
+}
+
+// SavePod saves a pod's state to the database
+func (s *BoltState) SavePod(pod *Pod) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ if !pod.valid {
+ return ErrPodRemoved
+ }
+
+ stateJSON, err := json.Marshal(pod.state)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling pod %s state to JSON", pod.ID())
+ }
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ podID := []byte(pod.ID())
+
+ err = db.Update(func(tx *bolt.Tx) error {
+ podBkt, err := getPodBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ podDB := podBkt.Bucket(podID)
+ if podDB == nil {
+ pod.valid = false
+ return errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in database", pod.ID())
+ }
+
+ // Set the pod state JSON
+ if err := podDB.Put(stateKey, stateJSON); err != nil {
+ return errors.Wrapf(err, "error updating pod %s state in database", pod.ID())
+ }
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
// AllPods returns all pods present in the state
func (s *BoltState) AllPods() ([]*Pod, error) {
if !s.valid {
@@ -1331,6 +1444,7 @@ func (s *BoltState) AllPods() ([]*Pod, error) {
pod := new(Pod)
pod.config = new(PodConfig)
+ pod.state = new(podState)
pods = append(pods, pod)
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index 25dd216a3..29ed42f1e 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -256,13 +256,22 @@ func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error
return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found", string(id))
}
- podBytes := podDB.Get(configKey)
- if podBytes == nil {
+ podConfigBytes := podDB.Get(configKey)
+ if podConfigBytes == nil {
return errors.Wrapf(ErrInternal, "pod %s is missing configuration key in DB", string(id))
}
- if err := json.Unmarshal(podBytes, pod.config); err != nil {
- return errors.Wrapf(err, "error unmarshalling pod %s from DB", string(id))
+ if err := json.Unmarshal(podConfigBytes, pod.config); err != nil {
+ return errors.Wrapf(err, "error unmarshalling pod %s config from DB", string(id))
+ }
+
+ podStateBytes := podDB.Get(stateKey)
+ if podStateBytes == nil {
+ return errors.Wrapf(ErrInternal, "pod %s is missing state key in DB", string(id))
+ }
+
+ if err := json.Unmarshal(podStateBytes, pod.state); err != nil {
+ return errors.Wrapf(err, "error unmarshalling pod %s state from DB", string(id))
}
// Get the lock
diff --git a/libpod/common_test.go b/libpod/common_test.go
index 01dca7acb..a95d73926 100644
--- a/libpod/common_test.go
+++ b/libpod/common_test.go
@@ -94,9 +94,13 @@ func getTestContainer(id, name, locksDir string) (*Container, error) {
func getTestPod(id, name, locksDir string) (*Pod, error) {
pod := &Pod{
config: &PodConfig{
- ID: id,
- Name: name,
- Labels: map[string]string{"a": "b", "c": "d"},
+ ID: id,
+ Name: name,
+ Labels: map[string]string{"a": "b", "c": "d"},
+ CgroupParent: "/hello/world/cgroup/parent",
+ },
+ state: &podState{
+ CgroupPath: "/path/to/cgroups/hello/",
},
valid: true,
}
@@ -180,3 +184,23 @@ func testContainersEqual(t *testing.T, a, b *Container) {
assert.EqualValues(t, aState, bState)
}
+
+// Test if pods are equal
+func testPodsEqual(t *testing.T, a, b *Pod) {
+ if a == nil && b == nil {
+ return
+ }
+
+ assert.NotNil(t, a)
+ assert.NotNil(t, b)
+
+ assert.NotNil(t, a.config)
+ assert.NotNil(t, b.config)
+ assert.NotNil(t, a.state)
+ assert.NotNil(t, b.state)
+
+ assert.Equal(t, a.valid, b.valid)
+
+ assert.EqualValues(t, a.config, b.config)
+ assert.EqualValues(t, a.state, b.state)
+}
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 386ace5b6..36077b9d1 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -604,6 +604,36 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
return nil
}
+// UpdatePod updates a pod in the state
+// This is a no-op as there is no backing store
+func (s *InMemoryState) UpdatePod(pod *Pod) error {
+ if !pod.valid {
+ return ErrPodRemoved
+ }
+
+ if _, ok := s.pods[pod.ID()]; !ok {
+ pod.valid = false
+ return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID())
+ }
+
+ return nil
+}
+
+// SavePod updates a pod in the state
+// This is a no-op at there is no backing store
+func (s *InMemoryState) SavePod(pod *Pod) error {
+ if !pod.valid {
+ return ErrPodRemoved
+ }
+
+ if _, ok := s.pods[pod.ID()]; !ok {
+ pod.valid = false
+ return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID())
+ }
+
+ return nil
+}
+
// AllPods retrieves all pods currently in the state
func (s *InMemoryState) AllPods() ([]*Pod, error) {
pods := make([]*Pod, 0, len(s.pods))
diff --git a/libpod/pod.go b/libpod/pod.go
index 3cad3f817..e082ef807 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -15,6 +15,7 @@ import (
// ffjson: skip
type Pod struct {
config *PodConfig
+ state *podState
valid bool
runtime *Runtime
@@ -23,9 +24,19 @@ type Pod struct {
// PodConfig represents a pod's static configuration
type PodConfig struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Labels map[string]string `json:""`
+ ID string `json:"id"`
+ Name string `json:"name"`
+
+ // Labels contains labels applied to the pod
+ Labels map[string]string `json:"labels"`
+ // CgroupParent contains the pod's CGroup parent
+ CgroupParent string `json:"cgroupParent"`
+}
+
+// podState represents a pod's state
+type podState struct {
+ // CgroupPath is the path to the pod's CGroup
+ CgroupPath string
}
// ID retrieves the pod's ID
@@ -48,12 +59,18 @@ func (p *Pod) Labels() map[string]string {
return labels
}
+// CgroupParent returns the pod's CGroup parent
+func (p *Pod) CgroupParent() string {
+ return p.config.CgroupParent
+}
+
// Creates a new, empty pod
func newPod(lockDir string, runtime *Runtime) (*Pod, error) {
pod := new(Pod)
pod.config = new(PodConfig)
pod.config.ID = stringid.GenerateNonCryptoID()
pod.config.Labels = make(map[string]string)
+ pod.state = new(podState)
pod.runtime = runtime
// Path our lock file will reside at
diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go
index 257b980a2..4ca8da9ee 100644
--- a/libpod/runtime_pod.go
+++ b/libpod/runtime_pod.go
@@ -1,6 +1,9 @@
package libpod
import (
+ "path"
+ "strings"
+
"github.com/pkg/errors"
)
@@ -45,6 +48,24 @@ func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) {
pod.valid = true
+ // Check CGroup parent sanity, and set it if it was not set
+ switch r.config.CgroupManager {
+ case CgroupfsCgroupsManager:
+ if pod.config.CgroupParent == "" {
+ pod.config.CgroupParent = CgroupfsDefaultCgroupParent
+ } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
+ return nil, errors.Wrapf(ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs")
+ }
+ case SystemdCgroupsManager:
+ if pod.config.CgroupParent == "" {
+ pod.config.CgroupParent = SystemdDefaultCgroupParent
+ } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
+ return nil, errors.Wrapf(ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups")
+ }
+ default:
+ return nil, errors.Wrapf(ErrInvalidArg, "unsupported CGroup manager: %s - cannot validate cgroup parent", r.config.CgroupManager)
+ }
+
if err := r.state.AddPod(pod); err != nil {
return nil, errors.Wrapf(err, "error adding pod to state")
}
diff --git a/libpod/state.go b/libpod/state.go
index ed52e3d46..9333c1724 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -66,6 +66,10 @@ type State interface {
// RemoveContainerFromPod removes a container from an existing pod
// The container will also be removed from the state
RemoveContainerFromPod(pod *Pod, ctr *Container) error
+ // UpdatePod updates a pod's state from the database
+ UpdatePod(pod *Pod) error
+ // SavePod saves a pod's state to the database
+ SavePod(pod *Pod) error
// Retrieves all pods presently in state
AllPods() ([]*Pod, error)
}
diff --git a/libpod/state_test.go b/libpod/state_test.go
index f0c65b134..4d5eb9713 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -915,8 +915,7 @@ func TestGetPodOnePod(t *testing.T) {
statePod, err := state.Pod(testPod.ID())
assert.NoError(t, err)
- assert.EqualValues(t, testPod.config, statePod.config)
- assert.Equal(t, testPod.valid, statePod.valid)
+ testPodsEqual(t, testPod, statePod)
})
}
@@ -937,8 +936,7 @@ func TestGetOnePodFromTwo(t *testing.T) {
statePod, err := state.Pod(testPod1.ID())
assert.NoError(t, err)
- assert.EqualValues(t, testPod1.config, statePod.config)
- assert.Equal(t, testPod1.valid, statePod.valid)
+ testPodsEqual(t, testPod1, statePod)
})
}
@@ -999,8 +997,7 @@ func TestLookupPodFullID(t *testing.T) {
statePod, err := state.LookupPod(testPod.ID())
assert.NoError(t, err)
- assert.EqualValues(t, testPod.config, statePod.config)
- assert.Equal(t, testPod.valid, statePod.valid)
+ testPodsEqual(t, testPod, statePod)
})
}
@@ -1015,8 +1012,7 @@ func TestLookupPodUniquePartialID(t *testing.T) {
statePod, err := state.LookupPod(testPod.ID()[0:8])
assert.NoError(t, err)
- assert.EqualValues(t, testPod.config, statePod.config)
- assert.Equal(t, testPod.valid, statePod.valid)
+ testPodsEqual(t, testPod, statePod)
})
}
@@ -1050,8 +1046,7 @@ func TestLookupPodByName(t *testing.T) {
statePod, err := state.LookupPod(testPod.Name())
assert.NoError(t, err)
- assert.EqualValues(t, testPod.config, statePod.config)
- assert.Equal(t, testPod.valid, statePod.valid)
+ testPodsEqual(t, testPod, statePod)
})
}
@@ -1157,7 +1152,7 @@ func TestAddPodValidPodSucceeds(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 1, len(allPods))
- assert.EqualValues(t, testPod.config, allPods[0].config)
+ testPodsEqual(t, testPod, allPods[0])
assert.Equal(t, testPod.valid, allPods[0].valid)
})
}
@@ -1318,8 +1313,7 @@ func TestRemovePodFromPods(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 1, len(allPods))
- assert.EqualValues(t, testPod2.config, allPods[0].config)
- assert.Equal(t, testPod2.valid, allPods[0].valid)
+ testPodsEqual(t, testPod2, allPods[0])
})
}
@@ -1394,8 +1388,7 @@ func TestAllPodsFindsPod(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 1, len(allPods))
- assert.EqualValues(t, testPod.config, allPods[0].config)
- assert.Equal(t, testPod.valid, allPods[0].valid)
+ testPodsEqual(t, testPod, allPods[0])
})
}
@@ -2403,3 +2396,62 @@ func TestRemoveContainerFromPodWithDependencySucceedsAfterDepRemoved(t *testing.
assert.Equal(t, 0, len(allCtrs))
})
}
+
+func TestUpdatePodInvalidPod(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ err := state.UpdatePod(&Pod{config: &PodConfig{}})
+ assert.Error(t, err)
+ })
+}
+
+func TestUpdatePodPodNotInStateFails(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testPod, err := getTestPod1(lockPath)
+ assert.NoError(t, err)
+
+ err = state.UpdatePod(testPod)
+ assert.Error(t, err)
+ })
+}
+
+func TestSavePodInvalidPod(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ err := state.SavePod(&Pod{config: &PodConfig{}})
+ assert.Error(t, err)
+ })
+}
+
+func TestSavePodPodNotInStateFails(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testPod, err := getTestPod1(lockPath)
+ assert.NoError(t, err)
+
+ err = state.SavePod(testPod)
+ assert.Error(t, err)
+ })
+}
+
+func TestSaveAndUpdatePod(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testPod, err := getTestPod1(lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddPod(testPod)
+ assert.NoError(t, err)
+
+ statePod, err := state.Pod(testPod.ID())
+ assert.NoError(t, err)
+
+ testPodsEqual(t, testPod, statePod)
+
+ testPod.state.CgroupPath = "/new/path/for/test"
+
+ err = state.SavePod(testPod)
+ assert.NoError(t, err)
+
+ err = state.UpdatePod(statePod)
+ assert.NoError(t, err)
+
+ testPodsEqual(t, testPod, statePod)
+ })
+}