summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
authorBrent Baude <bbaude@redhat.com>2021-07-14 16:03:55 -0500
committerBrent Baude <bbaude@redhat.com>2021-08-04 14:14:36 -0500
commit3c3fa6fac4d0f8e89181ea2d4e1fe0318d24b6f4 (patch)
treef087d0a772797a9028df514d8d0369835724b3a2 /libpod
parente93661f5e765d84893e2ad5a488682c0a67412d0 (diff)
downloadpodman-3c3fa6fac4d0f8e89181ea2d4e1fe0318d24b6f4.tar.gz
podman-3c3fa6fac4d0f8e89181ea2d4e1fe0318d24b6f4.tar.bz2
podman-3c3fa6fac4d0f8e89181ea2d4e1fe0318d24b6f4.zip
implement init containers in podman
this is the first pass at implementing init containers for podman pods. init containersare made popular by k8s as a way to run setup for pods before the pods standard containers run. unlike k8s, we support two styles of init containers: always and oneshot. always means the container stays in the pod and starts whenever a pod is started. this does not apply to pods restarting. oneshot means the container runs onetime when the pod starts and then is removed. Signed-off-by: Brent Baude <bbaude@redhat.com>
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_config.go3
-rw-r--r--libpod/container_graph.go2
-rw-r--r--libpod/container_validate.go6
-rw-r--r--libpod/define/container.go10
-rw-r--r--libpod/options.go15
-rw-r--r--libpod/pod.go37
-rw-r--r--libpod/pod_api.go67
7 files changed, 124 insertions, 16 deletions
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 0de79fde3..72a969fe6 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -375,4 +375,7 @@ type ContainerMiscConfig struct {
CDIDevices []string `json:"cdiDevices,omitempty"`
// EnvSecrets are secrets that are set as environment variables
EnvSecrets map[string]*secrets.Secret `json:"secret_env,omitempty"`
+ // InitContainerType specifies if the container is an initcontainer
+ // and if so, what type: always or oneshot are possible non-nil entries
+ InitContainerType string `json:"init_container_type,omitempty"`
}
diff --git a/libpod/container_graph.go b/libpod/container_graph.go
index 3ae7cfbc7..32fb264f1 100644
--- a/libpod/container_graph.go
+++ b/libpod/container_graph.go
@@ -259,7 +259,7 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError
}
// Start the container (only if it is not running)
- if !ctrErrored {
+ if !ctrErrored && len(node.container.config.InitContainerType) < 1 {
if !restart && node.container.state.State != define.ContainerStateRunning {
if err := node.container.initAndStart(ctx); err != nil {
ctrErrored = true
diff --git a/libpod/container_validate.go b/libpod/container_validate.go
index 6ff46f1b1..91ebe93fb 100644
--- a/libpod/container_validate.go
+++ b/libpod/container_validate.go
@@ -131,5 +131,11 @@ func (c *Container) validate() error {
if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) {
return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly")
}
+
+ // Init-ctrs must be used inside a Pod. Check if a init container type is
+ // passed and if no pod is passed
+ if len(c.config.InitContainerType) > 0 && len(c.config.Pod) < 1 {
+ return errors.Wrap(define.ErrInvalidArg, "init containers must be created in a pod")
+ }
return nil
}
diff --git a/libpod/define/container.go b/libpod/define/container.go
index f3125afa9..f0aca92aa 100644
--- a/libpod/define/container.go
+++ b/libpod/define/container.go
@@ -26,3 +26,13 @@ var RestartPolicyMap = map[string]string{
RestartPolicyOnFailure: RestartPolicyOnFailure,
RestartPolicyUnlessStopped: RestartPolicyUnlessStopped,
}
+
+// InitContainerTypes
+const (
+ // AlwaysInitContainer is an init container than runs on each
+ // pod start (including restart)
+ AlwaysInitContainer = "always"
+ // OneShotInitContainer is a container that only runs as init once
+ // and is then deleted.
+ OneShotInitContainer = "oneshot"
+)
diff --git a/libpod/options.go b/libpod/options.go
index 17a36008d..553206a8a 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1768,6 +1768,21 @@ func WithPidFile(pidFile string) CtrCreateOption {
}
}
+// WithInitCtrType indicates the container is a initcontainer
+func WithInitCtrType(containerType string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ // Make sure the type is valid
+ if containerType == define.OneShotInitContainer || containerType == define.AlwaysInitContainer {
+ ctr.config.InitContainerType = containerType
+ return nil
+ }
+ return errors.Errorf("%s is invalid init container type", containerType)
+ }
+}
+
// Pod Creation Options
// WithInfraImage sets the infra image for libpod.
diff --git a/libpod/pod.go b/libpod/pod.go
index 62f5c9e5b..0fef7f6f3 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -3,6 +3,7 @@ package libpod
import (
"context"
"net"
+ "sort"
"time"
"github.com/containers/podman/v3/libpod/define"
@@ -332,17 +333,20 @@ func (p *Pod) SharesNamespaces() bool {
return p.SharesPID() || p.SharesIPC() || p.SharesNet() || p.SharesMount() || p.SharesUser() || p.SharesUTS()
}
+// infraContainerID returns the infra ID without a lock
+func (p *Pod) infraContainerID() (string, error) {
+ if err := p.updatePod(); err != nil {
+ return "", err
+ }
+ return p.state.InfraContainerID, nil
+}
+
// InfraContainerID returns the infra container ID for a pod.
// If the container returned is "", the pod has no infra container.
func (p *Pod) InfraContainerID() (string, error) {
p.lock.Lock()
defer p.lock.Unlock()
-
- if err := p.updatePod(); err != nil {
- return "", err
- }
-
- return p.state.InfraContainerID, nil
+ return p.infraContainerID()
}
// InfraContainer returns the infra container.
@@ -350,7 +354,6 @@ func (p *Pod) InfraContainer() (*Container, error) {
if !p.HasInfraContainer() {
return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container")
}
-
id, err := p.InfraContainerID()
if err != nil {
return nil, err
@@ -420,3 +423,23 @@ func (p *Pod) ProcessLabel() (string, error) {
}
return ctr.ProcessLabel(), nil
}
+
+// initContainers returns the list of initcontainers
+// in a pod sorted by create time
+func (p *Pod) initContainers() ([]*Container, error) {
+ initCons := make([]*Container, 0)
+ // the pod is already locked when this is called
+ cons, err := p.allContainers()
+ if err != nil {
+ return nil, err
+ }
+ // Sort the pod containers by created time
+ sort.Slice(cons, func(i, j int) bool { return cons[i].CreatedTime().Before(cons[j].CreatedTime()) })
+ // Iterate sorted containers and add ids for any init containers
+ for _, c := range cons {
+ if len(c.config.InitContainerType) > 0 {
+ initCons = append(initCons, c)
+ }
+ }
+ return initCons, nil
+}
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index 1ab012a8b..90d67dbb0 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -12,6 +12,45 @@ import (
"github.com/sirupsen/logrus"
)
+// startInitContainers starts a pod's init containers.
+func (p *Pod) startInitContainers(ctx context.Context) error {
+ initCtrs, err := p.initContainers()
+ if err != nil {
+ return err
+ }
+ // Now iterate init containers
+ for _, initCon := range initCtrs {
+ if err := initCon.Start(ctx, true); err != nil {
+ return err
+ }
+ // Check that the init container waited correctly and the exit
+ // code is good
+ rc, err := initCon.Wait(ctx)
+ if err != nil {
+ return err
+ }
+ if rc != 0 {
+ return errors.Errorf("init container %s exited with code %d", initCon.ID(), rc)
+ }
+ // If the container is an oneshot init container, we need to remove it
+ // after it runs
+ if initCon.Config().InitContainerType == define.OneShotInitContainer {
+ icLock := initCon.lock
+ icLock.Lock()
+ if err := p.runtime.removeContainer(ctx, initCon, false, false, true); err != nil {
+ icLock.Unlock()
+ return errors.Wrapf(err, "failed to remove oneshot init container %s", initCon.ID())
+ }
+ // Removing a container this way requires an explicit call to clean up the db
+ if err := p.runtime.state.RemoveContainerFromPod(p, initCon); err != nil {
+ logrus.Errorf("Error removing container %s from database: %v", initCon.ID(), err)
+ }
+ icLock.Unlock()
+ }
+ }
+ return nil
+}
+
// Start starts all containers within a pod.
// It combines the effects of Init() and Start() on a container.
// If a container has already been initialized it will be started,
@@ -34,26 +73,29 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) {
return nil, define.ErrPodRemoved
}
+ // Before "regular" containers start in the pod, all init containers
+ // must have run and exited successfully.
+ if err := p.startInitContainers(ctx); err != nil {
+ return nil, err
+ }
allCtrs, err := p.runtime.state.PodContainers(p)
if err != nil {
return nil, err
}
-
// Build a dependency graph of containers in the pod
graph, err := BuildContainerGraph(allCtrs)
if err != nil {
return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID())
}
-
- ctrErrors := make(map[string]error)
- ctrsVisited := make(map[string]bool)
-
// If there are no containers without dependencies, we can't start
// Error out
if len(graph.noDepNodes) == 0 {
return nil, errors.Wrapf(define.ErrNoSuchCtr, "no containers in pod %s have no dependencies, cannot start pod", p.ID())
}
+ ctrErrors := make(map[string]error)
+ ctrsVisited := make(map[string]bool)
+
// Traverse the graph beginning at nodes with no dependencies
for _, node := range graph.noDepNodes {
startNode(ctx, node, false, ctrErrors, ctrsVisited, false)
@@ -449,12 +491,18 @@ func (p *Pod) Status() (map[string]define.ContainerStatus, error) {
if !p.valid {
return nil, define.ErrPodRemoved
}
-
allCtrs, err := p.runtime.state.PodContainers(p)
if err != nil {
return nil, err
}
- return containerStatusFromContainers(allCtrs)
+ noInitCtrs := make([]*Container, 0)
+ // Do not add init containers into status
+ for _, ctr := range allCtrs {
+ if ctrType := ctr.config.InitContainerType; len(ctrType) < 1 {
+ noInitCtrs = append(noInitCtrs, ctr)
+ }
+ }
+ return containerStatusFromContainers(noInitCtrs)
}
func containerStatusFromContainers(allCtrs []*Container) (map[string]define.ContainerStatus, error) {
@@ -504,7 +552,10 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
Name: c.Name(),
State: containerStatus,
})
- ctrStatuses[c.ID()] = c.state.State
+ // Do not add init containers fdr status
+ if len(c.config.InitContainerType) < 1 {
+ ctrStatuses[c.ID()] = c.state.State
+ }
}
podState, err := createPodStatusResults(ctrStatuses)
if err != nil {