Add backend code for generic dependencies

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #577
Approved by: rhatdan
This commit is contained in:
Matthew Heon 2018-03-31 19:58:31 -04:00 committed by Atomic Bot
parent 838df4eec4
commit a1c0f18bca
3 changed files with 162 additions and 0 deletions

View File

@ -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{}
}

View File

@ -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

View File

@ -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")