summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpod/container.go13
-rw-r--r--libpod/options.go32
-rw-r--r--libpod/state_test.go117
3 files changed, 162 insertions, 0 deletions
diff --git a/libpod/container.go b/libpod/container.go
index 965ac67fe..cbfa09538 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -205,6 +205,8 @@ type ContainerConfig struct {
// Namespace Config
// IDs of container to share namespaces with
// NetNsCtr conflicts with the CreateNetNS bool
+ // These containers are considered dependencies of the given container
+ // They must be started before the given container is started
IPCNsCtr string `json:"ipcNsCtr,omitempty"`
MountNsCtr string `json:"mountNsCtr,omitempty"`
NetNsCtr string `json:"netNsCtr,omitempty"`
@@ -213,6 +215,10 @@ type ContainerConfig struct {
UTSNsCtr string `json:"utsNsCtr,omitempty"`
CgroupNsCtr string `json:"cgroupNsCtr,omitempty"`
+ // IDs of dependency containers
+ // These containers must be started before this container is started
+ Dependencies []string
+
// Network Config
// CreateNetNS indicates that libpod should create and configure a new
// network namespace for the container
@@ -363,6 +369,8 @@ func (c *Container) User() string {
func (c *Container) Dependencies() []string {
// Collect in a map first to remove dupes
dependsCtrs := map[string]bool{}
+
+ // First add all namespace containers
if c.config.IPCNsCtr != "" {
dependsCtrs[c.config.IPCNsCtr] = true
}
@@ -385,6 +393,11 @@ func (c *Container) Dependencies() []string {
dependsCtrs[c.config.CgroupNsCtr] = true
}
+ // Add all generic dependencies
+ for _, id := range c.config.Dependencies {
+ dependsCtrs[id] = true
+ }
+
if len(dependsCtrs) == 0 {
return []string{}
}
diff --git a/libpod/options.go b/libpod/options.go
index 987a37429..739d53a8b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -625,6 +625,38 @@ func WithCgroupNSFrom(nsCtr *Container) CtrCreateOption {
}
}
+// WithDependencies sets dependency containers of the given container
+// Dependency containers must be running before this container is started
+func WithDependencyCtrs(ctrs []*Container) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+
+ deps := make([]string, 0, len(ctrs))
+
+ for _, dep := range ctrs {
+ if !dep.valid {
+ return errors.Wrapf(ErrCtrRemoved, "container %s is not valid", dep.ID())
+ }
+
+ if dep.ID() == ctr.ID() {
+ return errors.Wrapf(ErrInvalidArg, "must specify another container")
+ }
+
+ if ctr.config.Pod != "" && dep.config.Pod != ctr.config.Pod {
+ return errors.Wrapf(ErrInvalidArg, "container has joined pod %s and dependency container %s is not a member of the pod", ctr.config.Pod, dep.ID())
+ }
+
+ deps = append(deps, dep.ID())
+ }
+
+ ctr.config.Dependencies = deps
+
+ return nil
+ }
+}
+
// WithNetNS indicates that the container should be given a new network
// namespace with a minimal configuration
// An optional array of port mappings can be provided
diff --git a/libpod/state_test.go b/libpod/state_test.go
index f989dfefd..f0c65b134 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -656,6 +656,83 @@ func TestContainerInUseOneContainerMultipleDependencies(t *testing.T) {
})
}
+func TestContainerInUseGenericDependency(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testCtr1, err := getTestCtr1(lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestCtr2(lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.Dependencies = []string{testCtr1.config.ID}
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ ids, err := state.ContainerInUse(testCtr1)
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(ids))
+ assert.Equal(t, testCtr2.config.ID, ids[0])
+ })
+}
+
+func TestContainerInUseMultipleGenericDependencies(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testCtr1, err := getTestCtr1(lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestCtr2(lockPath)
+ assert.NoError(t, err)
+ testCtr3, err := getTestCtrN("3", lockPath)
+ assert.NoError(t, err)
+
+ testCtr3.config.Dependencies = []string{testCtr1.config.ID, testCtr2.config.ID}
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr3)
+ assert.NoError(t, err)
+
+ ids1, err := state.ContainerInUse(testCtr1)
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(ids1))
+ assert.Equal(t, testCtr3.config.ID, ids1[0])
+
+ ids2, err := state.ContainerInUse(testCtr2)
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(ids2))
+ assert.Equal(t, testCtr3.config.ID, ids2[0])
+ })
+}
+
+func TestContainerInUseGenericAndNamespaceDependencies(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testCtr1, err := getTestCtr1(lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestCtr2(lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.Dependencies = []string{testCtr1.config.ID}
+ testCtr2.config.IPCNsCtr = testCtr1.config.ID
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ ids, err := state.ContainerInUse(testCtr1)
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(ids))
+ assert.Equal(t, testCtr2.config.ID, ids[0])
+ })
+}
+
func TestCannotRemoveContainerWithDependency(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
@@ -680,6 +757,30 @@ func TestCannotRemoveContainerWithDependency(t *testing.T) {
})
}
+func TestCannotRemoveContainerWithGenericDependency(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testCtr1, err := getTestCtr1(lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestCtr2(lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.Dependencies = []string{testCtr1.config.ID}
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ err = state.RemoveContainer(testCtr1)
+ assert.Error(t, err)
+
+ ctrs, err := state.AllContainers()
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(ctrs))
+ })
+}
+
func TestCanRemoveContainerAfterDependencyRemoved(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
@@ -773,6 +874,22 @@ func TestCannotUseBadIDAsDependency(t *testing.T) {
})
}
+func TestCannotUseBadIDAsGenericDependency(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, lockPath string) {
+ testCtr, err := getTestCtr1(lockPath)
+ assert.NoError(t, err)
+
+ testCtr.config.Dependencies = []string{strings.Repeat("5", 32)}
+
+ err = state.AddContainer(testCtr)
+ assert.Error(t, err)
+
+ ctrs, err := state.AllContainers()
+ assert.NoError(t, err)
+ assert.Equal(t, 0, len(ctrs))
+ })
+}
+
func TestGetPodDoesNotExist(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.Pod("doesnotexist")