podman/libpod/options.go
Matthew Heon 259c79963f Improve robustness of podman system reset
Firstly, reset is now managed by the runtime itself as a part of
initialization. This ensures that it can be used even with
runtimes that would otherwise fail to be created - most notably,
when the user has changed a core path
(runroot/root/tmpdir/staticdir).

Secondly, we now attempt a best-effort removal even if the store
completely fails to be configured.

Third, we now hold the alive lock for the entire reset operation.
This ensures that no other Podman process can start while we are
running a system reset, and removes any possibility of a race
where a user tries to create containers or pull images while we
are trying to perform a reset.

[NO NEW TESTS NEEDED] we do not test reset last I checked.

Fixes #9075

Signed-off-by: Matthew Heon <mheon@redhat.com>
2022-06-03 12:54:08 -04:00

2177 lines
54 KiB
Go

package libpod
import (
"fmt"
"net"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/containers/buildah/pkg/parse"
nettypes "github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// Runtime Creation Options
var (
// SdNotifyModeValues describes the only values that SdNotifyMode can be
SdNotifyModeValues = []string{define.SdNotifyModeContainer, define.SdNotifyModeConmon, define.SdNotifyModeIgnore}
)
// WithStorageConfig uses the given configuration to set up container storage.
// If this is not specified, the system default configuration will be used
// instead.
func WithStorageConfig(config storage.StoreOptions) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
setField := false
if config.RunRoot != "" {
rt.storageConfig.RunRoot = config.RunRoot
rt.storageSet.RunRootSet = true
setField = true
}
if config.GraphRoot != "" {
rt.storageConfig.GraphRoot = config.GraphRoot
rt.storageSet.GraphRootSet = true
// Also set libpod static dir, so we are a subdirectory
// of the c/storage store by default
rt.config.Engine.StaticDir = filepath.Join(config.GraphRoot, "libpod")
rt.storageSet.StaticDirSet = true
// Also set libpod volume path, so we are a subdirectory
// of the c/storage store by default
rt.config.Engine.VolumePath = filepath.Join(config.GraphRoot, "volumes")
rt.storageSet.VolumePathSet = true
setField = true
}
graphDriverChanged := false
if config.GraphDriverName != "" {
rt.storageConfig.GraphDriverName = config.GraphDriverName
rt.storageSet.GraphDriverNameSet = true
setField = true
graphDriverChanged = true
}
if config.GraphDriverOptions != nil {
if graphDriverChanged {
rt.storageConfig.GraphDriverOptions = make([]string, len(config.GraphDriverOptions))
copy(rt.storageConfig.GraphDriverOptions, config.GraphDriverOptions)
} else {
rt.storageConfig.GraphDriverOptions = config.GraphDriverOptions
}
setField = true
}
if config.UIDMap != nil {
rt.storageConfig.UIDMap = make([]idtools.IDMap, len(config.UIDMap))
copy(rt.storageConfig.UIDMap, config.UIDMap)
}
if config.GIDMap != nil {
rt.storageConfig.GIDMap = make([]idtools.IDMap, len(config.GIDMap))
copy(rt.storageConfig.GIDMap, config.GIDMap)
}
// If any one of runroot, graphroot, graphdrivername,
// or graphdriveroptions are set, then GraphRoot and RunRoot
// must be set
if setField {
storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID())
if err != nil {
return err
}
if rt.storageConfig.GraphRoot == "" {
rt.storageConfig.GraphRoot = storeOpts.GraphRoot
}
if rt.storageConfig.RunRoot == "" {
rt.storageConfig.RunRoot = storeOpts.RunRoot
}
}
return nil
}
}
// WithSignaturePolicy specifies the path of a file which decides how trust is
// managed for images we've pulled.
// If this is not specified, the system default configuration will be used
// instead.
func WithSignaturePolicy(path string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.SignaturePolicyPath = path
return nil
}
}
// WithOCIRuntime specifies an OCI runtime to use for running containers.
func WithOCIRuntime(runtime string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
if runtime == "" {
return errors.Wrapf(define.ErrInvalidArg, "must provide a valid path")
}
rt.config.Engine.OCIRuntime = runtime
return nil
}
}
// WithConmonPath specifies the path to the conmon binary which manages the
// runtime.
func WithConmonPath(path string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
if path == "" {
return errors.Wrapf(define.ErrInvalidArg, "must provide a valid path")
}
rt.config.Engine.ConmonPath = []string{path}
return nil
}
}
// WithConmonEnv specifies the environment variable list for the conmon process.
func WithConmonEnv(environment []string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.ConmonEnvVars = make([]string, len(environment))
copy(rt.config.Engine.ConmonEnvVars, environment)
return nil
}
}
// WithNetworkCmdPath specifies the path to the slirp4netns binary which manages the
// runtime.
func WithNetworkCmdPath(path string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.NetworkCmdPath = path
return nil
}
}
// WithNetworkBackend specifies the name of the network backend.
func WithNetworkBackend(name string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Network.NetworkBackend = name
return nil
}
}
// WithCgroupManager specifies the manager implementation name which is used to
// handle cgroups for containers.
// Current valid values are "cgroupfs" and "systemd".
func WithCgroupManager(manager string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
if manager != config.CgroupfsCgroupsManager && manager != config.SystemdCgroupsManager {
return errors.Wrapf(define.ErrInvalidArg, "Cgroup manager must be one of %s and %s",
config.CgroupfsCgroupsManager, config.SystemdCgroupsManager)
}
rt.config.Engine.CgroupManager = manager
return nil
}
}
// WithStaticDir sets the directory that static runtime files which persist
// across reboots will be stored.
func WithStaticDir(dir string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.StaticDir = dir
rt.config.Engine.StaticDirSet = true
return nil
}
}
// WithRegistriesConf configures the runtime to always use specified
// registries.conf for image processing.
func WithRegistriesConf(path string) RuntimeOption {
logrus.Debugf("Setting custom registries.conf: %q", path)
return func(rt *Runtime) error {
if _, err := os.Stat(path); err != nil {
return errors.Wrap(err, "locating specified registries.conf")
}
if rt.imageContext == nil {
rt.imageContext = &types.SystemContext{
BigFilesTemporaryDir: parse.GetTempDir(),
}
}
rt.imageContext.SystemRegistriesConfPath = path
return nil
}
}
// WithHooksDir sets the directories to look for OCI runtime hook configuration.
func WithHooksDir(hooksDirs ...string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
for _, hooksDir := range hooksDirs {
if hooksDir == "" {
return errors.Wrap(define.ErrInvalidArg, "empty-string hook directories are not supported")
}
}
rt.config.Engine.HooksDir = hooksDirs
return nil
}
}
// WithCDI sets the devices to check for for CDI configuration.
func WithCDI(devices []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.CDIDevices = devices
return nil
}
}
// WithStorageOpts sets the devices to check for for CDI configuration.
func WithStorageOpts(storageOpts map[string]string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.StorageOpts = storageOpts
return nil
}
}
// WithDefaultMountsFile sets the file to look at for default mounts (mainly
// secrets).
// Note we are not saving this in the database as it is for testing purposes
// only.
func WithDefaultMountsFile(mountsFile string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
if mountsFile == "" {
return define.ErrInvalidArg
}
rt.config.Containers.DefaultMountsFile = mountsFile
return nil
}
}
// WithTmpDir sets the directory that temporary runtime files which are not
// expected to survive across reboots will be stored.
// This should be located on a tmpfs mount (/tmp or /run for example).
func WithTmpDir(dir string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.TmpDir = dir
rt.config.Engine.TmpDirSet = true
return nil
}
}
// WithNoStore sets a bool on the runtime that we do not need
// any containers storage.
func WithNoStore() RuntimeOption {
return func(rt *Runtime) error {
rt.noStore = true
return nil
}
}
// WithNoPivotRoot sets the runtime to use MS_MOVE instead of PIVOT_ROOT when
// starting containers.
func WithNoPivotRoot() RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.NoPivotRoot = true
return nil
}
}
// WithCNIConfigDir sets the CNI configuration directory.
func WithCNIConfigDir(dir string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Network.NetworkConfigDir = dir
return nil
}
}
// WithCNIPluginDir sets the CNI plugins directory.
func WithCNIPluginDir(dir string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Network.CNIPluginDirs = []string{dir}
return nil
}
}
// WithNamespace sets the namespace for libpod.
// Namespaces are used to create scopes to separate containers and pods
// in the state.
// When namespace is set, libpod will only view containers and pods in
// the same namespace. All containers and pods created will default to
// the namespace set here.
// A namespace of "", the empty string, is equivalent to no namespace,
// and all containers and pods will be visible.
func WithNamespace(ns string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.Namespace = ns
return nil
}
}
// WithVolumePath sets the path under which all named volumes
// should be created.
// The path changes based on whether the user is running as root or not.
func WithVolumePath(volPath string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.VolumePath = volPath
rt.config.Engine.VolumePathSet = true
return nil
}
}
// WithDefaultInfraCommand sets the command to
// run on pause container start up.
func WithDefaultInfraCommand(cmd string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.config.Engine.InfraCommand = cmd
return nil
}
}
// WithReset instructs libpod to reset all storage to factory defaults.
// All containers, pods, volumes, images, and networks will be removed.
// All directories created by Libpod will be removed.
func WithReset() RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.doReset = true
return nil
}
}
// WithRenumber instructs libpod to perform a lock renumbering while
// initializing. This will handle migrations from early versions of libpod with
// file locks to newer versions with SHM locking, as well as changes in the
// number of configured locks.
func WithRenumber() RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.doRenumber = true
return nil
}
}
// WithMigrate instructs libpod to migrate container configurations to account
// for changes between Engine versions. All running containers will be stopped
// during a migration, then restarted after the migration is complete.
func WithMigrate() RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.doMigrate = true
return nil
}
}
// WithMigrateRuntime instructs Engine to change the default OCI runtime on all
// containers during a migration. This is not used if `MigrateRuntime()` is not
// also passed.
// Engine makes no promises that your containers continue to work with the new
// runtime - migrations between dissimilar runtimes may well break things.
// Use with caution.
func WithMigrateRuntime(requestedRuntime string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
if requestedRuntime == "" {
return errors.Wrapf(define.ErrInvalidArg, "must provide a non-empty name for new runtime")
}
rt.migrateRuntime = requestedRuntime
return nil
}
}
// WithEventsLogger sets the events backend to use.
// Currently supported values are "file" for file backend and "journald" for
// journald backend.
func WithEventsLogger(logger string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
if !events.IsValidEventer(logger) {
return errors.Wrapf(define.ErrInvalidArg, "%q is not a valid events backend", logger)
}
rt.config.Engine.EventsLogger = logger
return nil
}
}
// WithEnableSDNotify sets a runtime option so we know whether to disable socket/FD
// listening
func WithEnableSDNotify() RuntimeOption {
return func(rt *Runtime) error {
rt.config.Engine.SDNotify = true
return nil
}
}
// WithSyslog sets a runtime option so we know that we have to log to the syslog as well
func WithSyslog() RuntimeOption {
return func(rt *Runtime) error {
rt.syslog = true
return nil
}
}
// WithRuntimeFlags adds the global runtime flags to the container config
func WithRuntimeFlags(runtimeFlags []string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}
rt.runtimeFlags = runtimeFlags
return nil
}
}
// Container Creation Options
// WithMaxLogSize sets the maximum size of container logs.
// Positive sizes are limits in bytes, -1 is unlimited.
func WithMaxLogSize(limit int64) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrRuntimeFinalized
}
ctr.config.LogSize = limit
return nil
}
}
// WithShmDir sets the directory that should be mounted on /dev/shm.
func WithShmDir(dir string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.ShmDir = dir
return nil
}
}
// WithNOShmMount tells libpod whether to mount /dev/shm
func WithNoShm(mount bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.NoShm = mount
return nil
}
}
// WithNoShmShare tells libpod whether to share containers /dev/shm with other containers
func WithNoShmShare(share bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.NoShmShare = share
return nil
}
}
// WithSystemd turns on systemd mode in the container
func WithSystemd() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
t := true
ctr.config.Systemd = &t
return nil
}
}
// WithSdNotifyMode sets the sd-notify method
func WithSdNotifyMode(mode string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
// verify values
if len(mode) > 0 && !cutil.StringInSlice(strings.ToLower(mode), SdNotifyModeValues) {
return errors.Wrapf(define.ErrInvalidArg, "--sdnotify values must be one of %q", strings.Join(SdNotifyModeValues, ", "))
}
ctr.config.SdNotifyMode = mode
return nil
}
}
// WithShmSize sets the size of /dev/shm tmpfs mount.
func WithShmSize(size int64) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.ShmSize = size
return nil
}
}
// WithPrivileged sets the privileged flag in the container runtime.
func WithPrivileged(privileged bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Privileged = privileged
return nil
}
}
// WithSecLabels sets the labels for SELinux.
func WithSecLabels(labelOpts []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.LabelOpts = labelOpts
return nil
}
}
// WithUser sets the user identity field in configuration.
// Valid uses [user | user:group | uid | uid:gid | user:gid | uid:group ].
func WithUser(user string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.User = user
return nil
}
}
// WithRootFSFromImage sets up a fresh root filesystem using the given image.
// If useImageConfig is specified, image volumes, environment variables, and
// other configuration from the image will be added to the config.
// TODO: Replace image name and ID with a libpod.Image struct when that is
// finished.
func WithRootFSFromImage(imageID, imageName, rawImageName string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.RootfsImageID = imageID
ctr.config.RootfsImageName = imageName
ctr.config.RawImageName = rawImageName
return nil
}
}
// WithStdin keeps stdin on the container open to allow interaction.
func WithStdin() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Stdin = true
return nil
}
}
// WithPod adds the container to a pod.
// Containers which join a pod can only join the Linux namespaces of other
// containers in the same pod.
// Containers can only join pods in the same libpod namespace.
func (r *Runtime) WithPod(pod *Pod) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if pod == nil {
return define.ErrInvalidArg
}
ctr.config.Pod = pod.ID()
return nil
}
}
// WithLabels adds labels to the container.
func WithLabels(labels map[string]string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Labels = make(map[string]string)
for key, value := range labels {
ctr.config.Labels[key] = value
}
return nil
}
}
// WithName sets the container's name.
func WithName(name string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
// Check the name against a regex
if !define.NameRegex.MatchString(name) {
return define.RegexError
}
ctr.config.Name = name
return nil
}
}
// WithStopSignal sets the signal that will be sent to stop the container.
func WithStopSignal(signal syscall.Signal) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if signal == 0 {
return errors.Wrapf(define.ErrInvalidArg, "stop signal cannot be 0")
} else if signal > 64 {
return errors.Wrapf(define.ErrInvalidArg, "stop signal cannot be greater than 64 (SIGRTMAX)")
}
ctr.config.StopSignal = uint(signal)
return nil
}
}
// WithStopTimeout sets the time to after initial stop signal is sent to the
// container, before sending the kill signal.
func WithStopTimeout(timeout uint) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.StopTimeout = timeout
return nil
}
}
// WithTimeout sets the maximum time a container is allowed to run"
func WithTimeout(timeout uint) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Timeout = timeout
return nil
}
}
// WithIDMappings sets the idmappings for the container
func WithIDMappings(idmappings storage.IDMappingOptions) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.IDMappings = idmappings
return nil
}
}
// WithUTSNSFromPod indicates the the container should join the UTS namespace of
// its pod
func WithUTSNSFromPod(p *Pod) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := validPodNSOption(p, ctr.config.Pod); err != nil {
return err
}
infraContainer, err := p.InfraContainerID()
if err != nil {
return err
}
ctr.config.UTSNsCtr = infraContainer
return nil
}
}
// WithIPCNSFrom indicates the the container should join the IPC namespace of
// the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithIPCNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.IPCNsCtr = nsCtr.ID()
return nil
}
}
// WithMountNSFrom indicates the the container should join the mount namespace
// of the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithMountNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.MountNsCtr = nsCtr.ID()
return nil
}
}
// WithNetNSFrom indicates the the container should join the network namespace
// of the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithNetNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.NetNsCtr = nsCtr.ID()
return nil
}
}
// WithPIDNSFrom indicates the the container should join the PID namespace of
// the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithPIDNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.PIDNsCtr = nsCtr.ID()
return nil
}
}
// WithAddCurrentUserPasswdEntry indicates that container should add current
// user entry to /etc/passwd, since the UID will be mapped into the container,
// via user namespace
func WithAddCurrentUserPasswdEntry() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.AddCurrentUserPasswdEntry = true
return nil
}
}
// WithUserNSFrom indicates the the container should join the user namespace of
// the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithUserNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.UserNsCtr = nsCtr.ID()
if err := JSONDeepCopy(nsCtr.IDMappings(), &ctr.config.IDMappings); err != nil {
return err
}
// NewFromSpec() is deprecated according to its comment
// however the recommended replace just causes a nil map panic
//nolint:staticcheck
g := generate.NewFromSpec(ctr.config.Spec)
g.ClearLinuxUIDMappings()
for _, uidmap := range nsCtr.config.IDMappings.UIDMap {
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
}
g.ClearLinuxGIDMappings()
for _, gidmap := range nsCtr.config.IDMappings.GIDMap {
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
}
return nil
}
}
// WithUTSNSFrom indicates the the container should join the UTS namespace of
// the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithUTSNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.UTSNsCtr = nsCtr.ID()
return nil
}
}
// WithCgroupNSFrom indicates the the container should join the Cgroup namespace
// of the given container.
// If the container has joined a pod, it can only join the namespaces of
// containers in the same pod.
func WithCgroupNSFrom(nsCtr *Container) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if err := checkDependencyContainer(nsCtr, ctr); err != nil {
return err
}
ctr.config.CgroupNsCtr = nsCtr.ID()
return nil
}
}
// WithDependencyCtrs 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 define.ErrCtrFinalized
}
deps := make([]string, 0, len(ctrs))
for _, dep := range ctrs {
if err := checkDependencyContainer(dep, ctr); err != nil {
return err
}
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.
// Conflicts with WithNetNSFrom().
func WithNetNS(portMappings []nettypes.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks map[string]nettypes.PerNetworkOptions) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.PostConfigureNetNS = postConfigureNetNS
ctr.config.NetMode = namespaces.NetworkMode(netmode)
ctr.config.CreateNetNS = true
ctr.config.PortMappings = portMappings
ctr.config.ExposedPorts = exposedPorts
if !ctr.config.NetMode.IsBridge() && len(networks) > 0 {
return errors.New("cannot use networks when network mode is not bridge")
}
ctr.config.Networks = networks
return nil
}
}
// WithNetworkOptions sets additional options for the networks.
func WithNetworkOptions(options map[string][]string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.NetworkOptions = options
return nil
}
}
// WithLogDriver sets the log driver for the container
func WithLogDriver(driver string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
switch driver {
case "":
return errors.Wrapf(define.ErrInvalidArg, "log driver must be set")
case define.JournaldLogging, define.KubernetesLogging, define.JSONLogging, define.NoLogging, define.PassthroughLogging:
break
default:
return errors.Wrapf(define.ErrInvalidArg, "invalid log driver")
}
ctr.config.LogDriver = driver
return nil
}
}
// WithLogPath sets the path to the log file.
func WithLogPath(path string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if path == "" {
return errors.Wrapf(define.ErrInvalidArg, "log path must be set")
}
ctr.config.LogPath = path
return nil
}
}
// WithLogTag sets the tag to the log file.
func WithLogTag(tag string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if tag == "" {
return errors.Wrapf(define.ErrInvalidArg, "log tag must be set")
}
ctr.config.LogTag = tag
return nil
}
}
// WithCgroupsMode disables the creation of Cgroups for the conmon process.
func WithCgroupsMode(mode string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
switch mode {
case "disabled":
ctr.config.NoCgroups = true
ctr.config.CgroupsMode = mode
case "enabled", "no-conmon", cgroupSplit:
ctr.config.CgroupsMode = mode
default:
return errors.Wrapf(define.ErrInvalidArg, "Invalid cgroup mode %q", mode)
}
return nil
}
}
// WithCgroupParent sets the Cgroup Parent of the new container.
func WithCgroupParent(parent string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if parent == "" {
return errors.Wrapf(define.ErrInvalidArg, "cgroup parent cannot be empty")
}
ctr.config.CgroupParent = parent
return nil
}
}
// WithDNSSearch sets the additional search domains of a container.
func WithDNSSearch(searchDomains []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.DNSSearch = searchDomains
return nil
}
}
// WithDNS sets additional name servers for the container.
func WithDNS(dnsServers []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
var dns []net.IP
for _, i := range dnsServers {
result := net.ParseIP(i)
if result == nil {
return errors.Wrapf(define.ErrInvalidArg, "invalid IP address %s", i)
}
dns = append(dns, result)
}
ctr.config.DNSServer = append(ctr.config.DNSServer, dns...)
return nil
}
}
// WithDNSOption sets addition dns options for the container.
func WithDNSOption(dnsOptions []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if ctr.config.UseImageResolvConf {
return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS options if container will not create /etc/resolv.conf")
}
ctr.config.DNSOption = append(ctr.config.DNSOption, dnsOptions...)
return nil
}
}
// WithHosts sets additional host:IP for the hosts file.
func WithHosts(hosts []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.HostAdd = hosts
return nil
}
}
// WithConmonPidFile specifies the path to the file that receives the pid of
// conmon.
func WithConmonPidFile(path string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.ConmonPidFile = path
return nil
}
}
// WithGroups sets additional groups for the container, which are defined by
// the user.
func WithGroups(groups []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Groups = groups
return nil
}
}
// WithUserVolumes sets the user-added volumes of the container.
// These are not added to the container's spec, but will instead be used during
// commit to populate the volumes of the new image, and to trigger some OCI
// hooks that are only added if volume mounts are present.
// Furthermore, they are used in the output of inspect, to filter volumes -
// only volumes included in this list will be included in the output.
// Unless explicitly set, committed images will have no volumes.
// The given volumes slice must not be nil.
func WithUserVolumes(volumes []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if volumes == nil {
return define.ErrInvalidArg
}
ctr.config.UserVolumes = make([]string, 0, len(volumes))
ctr.config.UserVolumes = append(ctr.config.UserVolumes, volumes...)
return nil
}
}
// WithEntrypoint sets the entrypoint of the container.
// This is not used to change the container's spec, but will instead be used
// during commit to populate the entrypoint of the new image.
// If not explicitly set it will default to the image's entrypoint.
// A nil entrypoint is allowed, and will clear entrypoint on the created image.
func WithEntrypoint(entrypoint []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Entrypoint = make([]string, 0, len(entrypoint))
ctr.config.Entrypoint = append(ctr.config.Entrypoint, entrypoint...)
return nil
}
}
// WithCommand sets the command of the container.
// This is not used to change the container's spec, but will instead be used
// during commit to populate the command of the new image.
// If not explicitly set it will default to the image's command.
// A nil command is allowed, and will clear command on the created image.
func WithCommand(command []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Command = make([]string, 0, len(command))
ctr.config.Command = append(ctr.config.Command, command...)
return nil
}
}
// WithRootFS sets the rootfs for the container.
// This creates a container from a directory on disk and not an image.
func WithRootFS(rootfs string, overlay bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if _, err := os.Stat(rootfs); err != nil {
return err
}
ctr.config.Rootfs = rootfs
ctr.config.RootfsOverlay = overlay
return nil
}
}
// WithCtrNamespace sets the namespace the container will be created in.
// Namespaces are used to create separate views of Podman's state - runtimes can
// join a specific namespace and see only containers and pods in that namespace.
// Empty string namespaces are allowed, and correspond to a lack of namespace.
func WithCtrNamespace(ns string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Namespace = ns
return nil
}
}
// WithUseImageResolvConf tells the container not to bind-mount resolv.conf in.
// This conflicts with other DNS-related options.
func WithUseImageResolvConf() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.UseImageResolvConf = true
return nil
}
}
// WithUseImageHosts tells the container not to bind-mount /etc/hosts in.
// This conflicts with WithHosts().
func WithUseImageHosts() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.UseImageHosts = true
return nil
}
}
// WithRestartPolicy sets the container's restart policy. Valid values are
// "no", "on-failure", and "always". The empty string is allowed, and will be
// equivalent to "no".
func WithRestartPolicy(policy string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
switch policy {
case define.RestartPolicyNone, define.RestartPolicyNo, define.RestartPolicyOnFailure, define.RestartPolicyAlways, define.RestartPolicyUnlessStopped:
ctr.config.RestartPolicy = policy
default:
return errors.Wrapf(define.ErrInvalidArg, "%q is not a valid restart policy", policy)
}
return nil
}
}
// WithRestartRetries sets the number of retries to use when restarting a
// container with the "on-failure" restart policy.
// 0 is an allowed value, and indicates infinite retries.
func WithRestartRetries(tries uint) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.RestartRetries = tries
return nil
}
}
// WithNamedVolumes adds the given named volumes to the container.
func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
for _, vol := range volumes {
mountOpts, err := util.ProcessOptions(vol.Options, false, "")
if err != nil {
return errors.Wrapf(err, "processing options for named volume %q mounted at %q", vol.Name, vol.Dest)
}
ctr.config.NamedVolumes = append(ctr.config.NamedVolumes, &ContainerNamedVolume{
Name: vol.Name,
Dest: vol.Dest,
Options: mountOpts,
})
}
return nil
}
}
// WithOverlayVolumes adds the given overlay volumes to the container.
func WithOverlayVolumes(volumes []*ContainerOverlayVolume) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
for _, vol := range volumes {
ctr.config.OverlayVolumes = append(ctr.config.OverlayVolumes, &ContainerOverlayVolume{
Dest: vol.Dest,
Source: vol.Source,
Options: vol.Options,
})
}
return nil
}
}
// WithImageVolumes adds the given image volumes to the container.
func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
for _, vol := range volumes {
ctr.config.ImageVolumes = append(ctr.config.ImageVolumes, &ContainerImageVolume{
Dest: vol.Dest,
Source: vol.Source,
ReadWrite: vol.ReadWrite,
})
}
return nil
}
}
// WithHealthCheck adds the healthcheck to the container config
func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.HealthCheckConfig = healthCheck
return nil
}
}
// WithPreserveFDs forwards from the process running Libpod into the container
// the given number of extra FDs (starting after the standard streams) to the created container
func WithPreserveFDs(fd uint) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.PreserveFDs = fd
return nil
}
}
// WithCreateCommand adds the full command plus arguments of the current
// process to the container config.
func WithCreateCommand(cmd []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.CreateCommand = cmd
return nil
}
}
// withIsInfra allows us to dfferentiate between infra containers and other containers
// within the container config
func withIsInfra() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.IsInfra = true
return nil
}
}
// WithIsService allows us to dfferentiate between service containers and other container
// within the container config
func WithIsService() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.IsService = true
return nil
}
}
// WithCreateWorkingDir tells Podman to create the container's working directory
// if it does not exist.
func WithCreateWorkingDir() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.CreateWorkingDir = true
return nil
}
}
// Volume Creation Options
// WithVolumeName sets the name of the volume.
func WithVolumeName(name string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
// Check the name against a regex
if !define.NameRegex.MatchString(name) {
return define.RegexError
}
volume.config.Name = name
return nil
}
}
// WithVolumeDriver sets the volume's driver.
// It is presently not implemented, but will be supported in a future Podman
// release.
func WithVolumeDriver(driver string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.Driver = driver
return nil
}
}
// WithVolumeLabels sets the labels of the volume.
func WithVolumeLabels(labels map[string]string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.Labels = make(map[string]string)
for key, value := range labels {
volume.config.Labels[key] = value
}
return nil
}
}
// WithVolumeOptions sets the options of the volume.
func WithVolumeOptions(options map[string]string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.Options = make(map[string]string)
for key, value := range options {
volume.config.Options[key] = value
}
return nil
}
}
// WithVolumeUID sets the UID that the volume will be created as.
func WithVolumeUID(uid int) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.UID = uid
return nil
}
}
// WithVolumeSize sets the maximum size of the volume
func WithVolumeSize(size uint64) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.Size = size
return nil
}
}
// WithVolumeInodes sets the maximum inodes of the volume
func WithVolumeInodes(inodes uint64) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.Inodes = inodes
return nil
}
}
// WithVolumeGID sets the GID that the volume will be created as.
func WithVolumeGID(gid int) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.GID = gid
return nil
}
}
// WithVolumeNoChown prevents the volume from being chowned to the process uid at first use.
func WithVolumeNoChown() VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.state.NeedsChown = false
return nil
}
}
// WithVolumeDisableQuota prevents the volume from being assigned a quota.
func WithVolumeDisableQuota() VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.DisableQuota = true
return nil
}
}
// withSetAnon sets a bool notifying libpod that this volume is anonymous and
// should be removed when containers using it are removed and volumes are
// specified for removal.
func withSetAnon() VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}
volume.config.IsAnon = true
return nil
}
}
// WithTimezone sets the timezone in the container
func WithTimezone(path string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if path != "local" {
zone := filepath.Join("/usr/share/zoneinfo", path)
file, err := os.Stat(zone)
if err != nil {
return err
}
// We don't want to mount a timezone directory
if file.IsDir() {
return errors.New("Invalid timezone: is a directory")
}
}
ctr.config.Timezone = path
return nil
}
}
// WithUmask sets the umask in the container
func WithUmask(umask string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
if !define.UmaskRegex.MatchString(umask) {
return errors.Wrapf(define.ErrInvalidArg, "Invalid umask string %s", umask)
}
ctr.config.Umask = umask
return nil
}
}
// WithSecrets adds secrets to the container
func WithSecrets(containerSecrets []*ContainerSecret) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Secrets = containerSecrets
return nil
}
}
// WithSecrets adds environment variable secrets to the container
func WithEnvSecrets(envSecrets map[string]string) CtrCreateOption {
return func(ctr *Container) error {
ctr.config.EnvSecrets = make(map[string]*secrets.Secret)
if ctr.valid {
return define.ErrCtrFinalized
}
manager, err := ctr.runtime.SecretsManager()
if err != nil {
return err
}
for target, src := range envSecrets {
secr, err := manager.Lookup(src)
if err != nil {
return err
}
ctr.config.EnvSecrets[target] = secr
}
return nil
}
}
// WithPidFile adds pidFile to the container
func WithPidFile(pidFile string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.PidFile = pidFile
return nil
}
}
// WithHostUsers indicates host users to add to /etc/passwd
func WithHostUsers(hostUsers []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.HostUsers = hostUsers
return nil
}
}
// 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)
}
}
// WithHostDevice adds the original host src to the config
func WithHostDevice(dev []specs.LinuxDevice) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.DeviceHostSrc = dev
return nil
}
}
// WithSelectedPasswordManagement makes it so that the container either does or does not setup /etc/passwd or /etc/group
func WithSelectedPasswordManagement(passwd *bool) CtrCreateOption {
return func(c *Container) error {
if c.valid {
return define.ErrCtrFinalized
}
c.config.Passwd = passwd
return nil
}
}
// WithInfraConfig allows for inheritance of compatible config entities from the infra container
func WithInfraConfig(compatibleOptions InfraInherit) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
compatMarshal, err := json.Marshal(compatibleOptions)
if err != nil {
return errors.New("Could not marshal compatible options")
}
err = json.Unmarshal(compatMarshal, ctr.config)
if err != nil {
return errors.New("Could not unmarshal compatible options into contrainer config")
}
return nil
}
}
// Pod Creation Options
// WithPodCreateCommand adds the full command plus arguments of the current
// process to the pod config.
func WithPodCreateCommand(createCmd []string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.CreateCommand = createCmd
return nil
}
}
// WithPodName sets the name of the pod.
func WithPodName(name string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
// Check the name against a regex
if !define.NameRegex.MatchString(name) {
return define.RegexError
}
pod.config.Name = name
return nil
}
}
// WithPodExitPolicy sets the exit policy of the pod.
func WithPodExitPolicy(policy string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
parsed, err := config.ParsePodExitPolicy(policy)
if err != nil {
return err
}
pod.config.ExitPolicy = parsed
return nil
}
}
// WithPodHostname sets the hostname of the pod.
func WithPodHostname(hostname string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
// Check the hostname against a regex
if !define.NameRegex.MatchString(hostname) {
return define.RegexError
}
pod.config.Hostname = hostname
return nil
}
}
// WithInfraConmonPidFile sets the path to a custom conmon PID file for the
// infra container.
func WithInfraConmonPidFile(path string, infraSpec *specgen.SpecGenerator) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
infraSpec.ConmonPidFile = path
return nil
}
}
// WithPodLabels sets the labels of a pod.
func WithPodLabels(labels map[string]string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.Labels = make(map[string]string)
for key, value := range labels {
pod.config.Labels[key] = value
}
return nil
}
}
// WithPodCgroupParent sets the Cgroup Parent of the pod.
func WithPodCgroupParent(path string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.CgroupParent = path
return nil
}
}
// WithPodCgroups tells containers in this pod to use the cgroup created for
// this pod.
// This can still be overridden at the container level by explicitly specifying
// a Cgroup parent.
func WithPodParent() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodCgroup = true
return nil
}
}
// WithPodNamespace sets the namespace for the created pod.
// Namespaces are used to create separate views of Podman's state - runtimes can
// join a specific namespace and see only containers and pods in that namespace.
// Empty string namespaces are allowed, and correspond to a lack of namespace.
// Containers must belong to the same namespace as the pod they join.
func WithPodNamespace(ns string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.Namespace = ns
return nil
}
}
// WithPodIPC tells containers in this pod to use the ipc namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the
// first container added.
func WithPodIPC() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodIPC = true
return nil
}
}
// WithPodNet tells containers in this pod to use the network namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the
// first container added.
func WithPodNet() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodNet = true
return nil
}
}
// WithPodMount tells containers in this pod to use the mount namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the
// first container added.
// TODO implement WithMountNSFrom, so WithMountNsFromPod functions properly
// Then this option can be added on the pod level
func WithPodMount() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodMount = true
return nil
}
}
// WithPodUser tells containers in this pod to use the user namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the
// first container added.
// TODO implement WithUserNSFrom, so WithUserNsFromPod functions properly
// Then this option can be added on the pod level
func WithPodUser() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodUser = true
return nil
}
}
// WithPodPID tells containers in this pod to use the pid namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the
// first container added.
func WithPodPID() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodPID = true
return nil
}
}
// WithPodUTS tells containers in this pod to use the uts namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the
// first container added.
func WithPodUTS() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodUTS = true
return nil
}
}
// WithPodCgroup tells containers in this pod to use the cgroup namespace
// created for this pod.
// Containers in a pod will inherit the kernel namespaces from the first
// container added.
func WithPodCgroup() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.UsePodCgroupNS = true
return nil
}
}
// WithInfraContainer tells the pod to create a pause container
func WithInfraContainer() PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
pod.config.HasInfra = true
return nil
}
}
// WithServiceContainer associates the specified service container ID with the pod.
func WithServiceContainer(id string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
ctr, err := pod.runtime.LookupContainer(id)
if err != nil {
return fmt.Errorf("looking up service container: %w", err)
}
if err := ctr.addServicePodLocked(pod.ID()); err != nil {
return fmt.Errorf("associating service container %s with pod %s: %w", id, pod.ID(), err)
}
pod.config.ServiceContainerID = id
return nil
}
}
// WithVolatile sets the volatile flag for the container storage.
// The option can potentially cause data loss when used on a container that must survive a machine reboot.
func WithVolatile() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.Volatile = true
return nil
}
}
// WithChrootDirs is an additional set of directories that need to be
// treated as root directories. Standard bind mounts will be mounted
// into paths relative to these directories.
func WithChrootDirs(dirs []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.ChrootDirs = dirs
return nil
}
}
// WithPasswdEntry sets the entry to write to the /etc/passwd file.
func WithPasswdEntry(passwdEntry string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.PasswdEntry = passwdEntry
return nil
}
}