mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Add NetMode, UTSMode and IPCMode
Allow kpod create/run to create contianers in different network namespaces, uts namespaces and IPC Namespaces. This patch just handles the simple join the host, or another containers namespaces. Lots more work needed to full integrate --net Signed-off-by: Daniel J Walsh <dwalsh@redhat.com> Closes: #64 Approved by: mheon
This commit is contained in:

committed by
Atomic Bot

parent
1f01faf437
commit
adf8809521
@ -81,20 +81,20 @@ type createConfig struct {
|
|||||||
groupAdd []uint32 // group-add
|
groupAdd []uint32 // group-add
|
||||||
hostname string //hostname
|
hostname string //hostname
|
||||||
image string
|
image string
|
||||||
interactive bool //interactive
|
interactive bool //interactive
|
||||||
ip6Address string //ipv6
|
ipcMode container.IpcMode //ipc
|
||||||
ipAddress string //ip
|
ip6Address string //ipv6
|
||||||
labels map[string]string //label
|
ipAddress string //ip
|
||||||
linkLocalIP []string // link-local-ip
|
labels map[string]string //label
|
||||||
logDriver string // log-driver
|
linkLocalIP []string // link-local-ip
|
||||||
logDriverOpt []string // log-opt
|
logDriver string // log-driver
|
||||||
macAddress string //mac-address
|
logDriverOpt []string // log-opt
|
||||||
name string //name
|
macAddress string //mac-address
|
||||||
network string //network
|
name string //name
|
||||||
networkAlias []string //network-alias
|
netMode container.NetworkMode //net
|
||||||
nsIPC string // ipc
|
network string //network
|
||||||
nsNET string //net
|
networkAlias []string //network-alias
|
||||||
pidMode container.PidMode //pid
|
pidMode container.PidMode //pid
|
||||||
nsUser string
|
nsUser string
|
||||||
pod string //pod
|
pod string //pod
|
||||||
privileged bool //privileged
|
privileged bool //privileged
|
||||||
@ -102,7 +102,8 @@ type createConfig struct {
|
|||||||
publishAll bool //publish-all
|
publishAll bool //publish-all
|
||||||
readOnlyRootfs bool //read-only
|
readOnlyRootfs bool //read-only
|
||||||
resources createResourceConfig
|
resources createResourceConfig
|
||||||
rm bool //rm
|
rm bool //rm
|
||||||
|
shmDir string
|
||||||
sigProxy bool //sig-proxy
|
sigProxy bool //sig-proxy
|
||||||
stopSignal string // stop-signal
|
stopSignal string // stop-signal
|
||||||
stopTimeout int64 // stop-timeout
|
stopTimeout int64 // stop-timeout
|
||||||
@ -112,6 +113,7 @@ type createConfig struct {
|
|||||||
tty bool //tty
|
tty bool //tty
|
||||||
user uint32 //user
|
user uint32 //user
|
||||||
group uint32 // group
|
group uint32 // group
|
||||||
|
utsMode container.UTSMode //uts
|
||||||
volumes []string //volume
|
volumes []string //volume
|
||||||
volumesFrom []string //volumes-from
|
volumesFrom []string //volumes-from
|
||||||
workDir string //workdir
|
workDir string //workdir
|
||||||
@ -201,7 +203,8 @@ func createCmd(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
// Gather up the options for NewContainer which consist of With... funcs
|
// Gather up the options for NewContainer which consist of With... funcs
|
||||||
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
||||||
options = append(options, libpod.WithSELinuxMountLabel(createConfig.mountLabel))
|
options = append(options, libpod.WithSELinuxLabels(createConfig.processLabel, createConfig.mountLabel))
|
||||||
|
options = append(options, libpod.WithShmDir(createConfig.shmDir))
|
||||||
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -230,6 +233,26 @@ func parseSecurityOpt(config *createConfig, securityOpts []string) error {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if config.pidMode.IsHost() {
|
||||||
|
labelOpts = append(labelOpts, label.DisableSecOpt()...)
|
||||||
|
} else if config.pidMode.IsContainer() {
|
||||||
|
ctr, err := config.runtime.LookupContainer(config.pidMode.Container())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "container %q not found", config.pidMode.Container())
|
||||||
|
}
|
||||||
|
labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ipcMode.IsHost() {
|
||||||
|
labelOpts = append(labelOpts, label.DisableSecOpt()...)
|
||||||
|
} else if config.ipcMode.IsContainer() {
|
||||||
|
ctr, err := config.runtime.LookupContainer(config.ipcMode.Container())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "container %q not found", config.ipcMode.Container())
|
||||||
|
}
|
||||||
|
labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
|
||||||
|
}
|
||||||
|
|
||||||
for _, opt := range securityOpts {
|
for _, opt := range securityOpts {
|
||||||
if opt == "no-new-privileges" {
|
if opt == "no-new-privileges" {
|
||||||
config.noNewPrivileges = true
|
config.noNewPrivileges = true
|
||||||
@ -354,6 +377,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
|
|||||||
if !c.Bool("detach") && !tty {
|
if !c.Bool("detach") && !tty {
|
||||||
tty = true
|
tty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
pidMode := container.PidMode(c.String("pid"))
|
pidMode := container.PidMode(c.String("pid"))
|
||||||
if !pidMode.Valid() {
|
if !pidMode.Valid() {
|
||||||
return nil, errors.Errorf("--pid %q is not valid", c.String("pid"))
|
return nil, errors.Errorf("--pid %q is not valid", c.String("pid"))
|
||||||
@ -363,6 +387,25 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
|
|||||||
return nil, errors.Errorf("--rm and --detach can not be specified together")
|
return nil, errors.Errorf("--rm and --detach can not be specified together")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utsMode := container.UTSMode(c.String("uts"))
|
||||||
|
if !utsMode.Valid() {
|
||||||
|
return nil, errors.Errorf("--uts %q is not valid", c.String("uts"))
|
||||||
|
}
|
||||||
|
ipcMode := container.IpcMode(c.String("ipc"))
|
||||||
|
if !ipcMode.Valid() {
|
||||||
|
return nil, errors.Errorf("--ipc %q is not valid", ipcMode)
|
||||||
|
}
|
||||||
|
shmDir := ""
|
||||||
|
if ipcMode.IsHost() {
|
||||||
|
shmDir = "/dev/shm"
|
||||||
|
} else if ipcMode.IsContainer() {
|
||||||
|
ctr, err := runtime.LookupContainer(ipcMode.Container())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "container %q not found", ipcMode.Container())
|
||||||
|
}
|
||||||
|
shmDir = ctr.ShmDir()
|
||||||
|
}
|
||||||
|
|
||||||
config := &createConfig{
|
config := &createConfig{
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
capAdd: c.StringSlice("cap-add"),
|
capAdd: c.StringSlice("cap-add"),
|
||||||
@ -390,8 +433,9 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
|
|||||||
name: c.String("name"),
|
name: c.String("name"),
|
||||||
network: c.String("network"),
|
network: c.String("network"),
|
||||||
networkAlias: c.StringSlice("network-alias"),
|
networkAlias: c.StringSlice("network-alias"),
|
||||||
nsIPC: c.String("ipc"),
|
ipcMode: ipcMode,
|
||||||
nsNET: c.String("net"),
|
netMode: container.NetworkMode(c.String("network")),
|
||||||
|
utsMode: utsMode,
|
||||||
pidMode: pidMode,
|
pidMode: pidMode,
|
||||||
pod: c.String("pod"),
|
pod: c.String("pod"),
|
||||||
privileged: c.Bool("privileged"),
|
privileged: c.Bool("privileged"),
|
||||||
@ -426,6 +470,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
|
|||||||
ulimit: c.StringSlice("ulimit"),
|
ulimit: c.StringSlice("ulimit"),
|
||||||
},
|
},
|
||||||
rm: c.Bool("rm"),
|
rm: c.Bool("rm"),
|
||||||
|
shmDir: shmDir,
|
||||||
sigProxy: c.Bool("sig-proxy"),
|
sigProxy: c.Bool("sig-proxy"),
|
||||||
stopSignal: c.String("stop-signal"),
|
stopSignal: c.String("stop-signal"),
|
||||||
stopTimeout: c.Int64("stop-timeout"),
|
stopTimeout: c.Int64("stop-timeout"),
|
||||||
|
@ -85,9 +85,11 @@ func runCmd(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "unable to parse new container options")
|
return errors.Wrapf(err, "unable to parse new container options")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather up the options for NewContainer which consist of With... funcs
|
// Gather up the options for NewContainer which consist of With... funcs
|
||||||
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
||||||
options = append(options, libpod.WithSELinuxMountLabel(createConfig.mountLabel))
|
options = append(options, libpod.WithSELinuxLabels(createConfig.processLabel, createConfig.mountLabel))
|
||||||
|
options = append(options, libpod.WithShmDir(createConfig.shmDir))
|
||||||
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -130,14 +132,14 @@ func runCmd(c *cli.Context) error {
|
|||||||
if err := ctr.Start(); err != nil {
|
if err := ctr.Start(); err != nil {
|
||||||
return errors.Wrapf(err, "unable to start container %q", ctr.ID())
|
return errors.Wrapf(err, "unable to start container %q", ctr.ID())
|
||||||
}
|
}
|
||||||
logrus.Debug("started container ", ctr.ID())
|
|
||||||
|
|
||||||
if createConfig.detach {
|
if createConfig.detach {
|
||||||
fmt.Printf("%s\n", ctr.ID())
|
fmt.Printf("%s\n", ctr.ID())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
if createConfig.rm {
|
if createConfig.rm {
|
||||||
return runtime.RemoveContainer(ctr, true)
|
return runtime.RemoveContainer(ctr, true)
|
||||||
}
|
}
|
||||||
return nil
|
return ctr.CleanupStorage()
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ func blockAccessToKernelFilesystems(config *createConfig, g *generate.Generator)
|
|||||||
func addPidNS(config *createConfig, g *generate.Generator) error {
|
func addPidNS(config *createConfig, g *generate.Generator) error {
|
||||||
pidMode := config.pidMode
|
pidMode := config.pidMode
|
||||||
if pidMode.IsHost() {
|
if pidMode.IsHost() {
|
||||||
return g.RemoveLinuxNamespace("pid")
|
return g.RemoveLinuxNamespace(libpod.PIDNamespace)
|
||||||
}
|
}
|
||||||
if pidMode.IsContainer() {
|
if pidMode.IsContainer() {
|
||||||
ctr, err := config.runtime.LookupContainer(pidMode.Container())
|
ctr, err := config.runtime.LookupContainer(pidMode.Container())
|
||||||
@ -68,6 +68,65 @@ func addPidNS(config *createConfig, g *generate.Generator) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addNetNS(config *createConfig, g *generate.Generator) error {
|
||||||
|
netMode := config.netMode
|
||||||
|
if netMode.IsHost() {
|
||||||
|
return g.RemoveLinuxNamespace(libpod.NetNamespace)
|
||||||
|
}
|
||||||
|
if netMode.IsNone() {
|
||||||
|
return libpod.ErrNotImplemented
|
||||||
|
}
|
||||||
|
if netMode.IsBridge() {
|
||||||
|
return libpod.ErrNotImplemented
|
||||||
|
}
|
||||||
|
if netMode.IsContainer() {
|
||||||
|
ctr, err := config.runtime.LookupContainer(netMode.ConnectedContainer())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "container %q not found", netMode.ConnectedContainer())
|
||||||
|
}
|
||||||
|
pid, err := ctr.PID()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to get pid of container %q", netMode.ConnectedContainer())
|
||||||
|
}
|
||||||
|
nsPath := fmt.Sprintf("/proc/%d/ns/net", pid)
|
||||||
|
if err := g.AddOrReplaceLinuxNamespace(libpod.NetNamespace, nsPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addUTSNS(config *createConfig, g *generate.Generator) error {
|
||||||
|
utsMode := config.utsMode
|
||||||
|
if utsMode.IsHost() {
|
||||||
|
return g.RemoveLinuxNamespace(libpod.UTSNamespace)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addIpcNS(config *createConfig, g *generate.Generator) error {
|
||||||
|
ipcMode := config.ipcMode
|
||||||
|
if ipcMode.IsHost() {
|
||||||
|
return g.RemoveLinuxNamespace(libpod.IPCNamespace)
|
||||||
|
}
|
||||||
|
if ipcMode.IsContainer() {
|
||||||
|
ctr, err := config.runtime.LookupContainer(ipcMode.Container())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "container %q not found", ipcMode.Container())
|
||||||
|
}
|
||||||
|
pid, err := ctr.PID()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to get pid of container %q", ipcMode.Container())
|
||||||
|
}
|
||||||
|
nsPath := fmt.Sprintf("/proc/%d/ns/ipc", pid)
|
||||||
|
if err := g.AddOrReplaceLinuxNamespace(libpod.IPCNamespace, nsPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func addRlimits(config *createConfig, g *generate.Generator) error {
|
func addRlimits(config *createConfig, g *generate.Generator) error {
|
||||||
var (
|
var (
|
||||||
ul *units.Ulimit
|
ul *units.Ulimit
|
||||||
@ -210,6 +269,17 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := addNetNS(config, &g); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := addUTSNS(config, &g); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := addIpcNS(config, &g); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
configSpec := g.Spec()
|
configSpec := g.Spec()
|
||||||
|
|
||||||
if config.seccompProfilePath != "" && config.seccompProfilePath != "unconfined" {
|
if config.seccompProfilePath != "" && config.seccompProfilePath != "unconfined" {
|
||||||
|
@ -7,19 +7,25 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/namesgenerator"
|
"github.com/docker/docker/pkg/namesgenerator"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
crioAnnotations "github.com/projectatomic/libpod/pkg/annotations"
|
crioAnnotations "github.com/projectatomic/libpod/pkg/annotations"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/ulule/deepcopier"
|
"github.com/ulule/deepcopier"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
)
|
)
|
||||||
@ -86,7 +92,6 @@ type containerRuntimeInfo struct {
|
|||||||
OOMKilled bool `json:"oomKilled,omitempty"`
|
OOMKilled bool `json:"oomKilled,omitempty"`
|
||||||
// PID is the PID of a running container
|
// PID is the PID of a running container
|
||||||
PID int `json:"pid,omitempty"`
|
PID int `json:"pid,omitempty"`
|
||||||
|
|
||||||
// TODO: Save information about image used in container if one is used
|
// TODO: Save information about image used in container if one is used
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +106,12 @@ type ContainerConfig struct {
|
|||||||
RootfsImageID string `json:"rootfsImageID,omitempty"`
|
RootfsImageID string `json:"rootfsImageID,omitempty"`
|
||||||
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
||||||
UseImageConfig bool `json:"useImageConfig"`
|
UseImageConfig bool `json:"useImageConfig"`
|
||||||
|
// SELinux process label for container
|
||||||
|
ProcessLabel string `json:"ProcessLabel,omitempty"`
|
||||||
// SELinux mount label for root filesystem
|
// SELinux mount label for root filesystem
|
||||||
MountLabel string `json:"MountLabel,omitempty"`
|
MountLabel string `json:"MountLabel,omitempty"`
|
||||||
|
// Src path to be mounted on /dev/shm in container
|
||||||
|
ShmDir string `json:"ShmDir,omitempty"`
|
||||||
// Static directory for container content that will persist across
|
// Static directory for container content that will persist across
|
||||||
// reboot
|
// reboot
|
||||||
StaticDir string `json:"staticDir"`
|
StaticDir string `json:"staticDir"`
|
||||||
@ -113,6 +122,8 @@ type ContainerConfig struct {
|
|||||||
// Labels is a set of key-value pairs providing additional information
|
// Labels is a set of key-value pairs providing additional information
|
||||||
// about a container
|
// about a container
|
||||||
Labels map[string]string `json:"labels,omitempty"`
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
// Mounts list contains all additional mounts by the container runtime.
|
||||||
|
Mounts []string
|
||||||
// StopSignal is the signal that will be used to stop the container
|
// StopSignal is the signal that will be used to stop the container
|
||||||
StopSignal uint `json:"stopSignal,omitempty"`
|
StopSignal uint `json:"stopSignal,omitempty"`
|
||||||
// Shared namespaces with container
|
// Shared namespaces with container
|
||||||
@ -120,7 +131,6 @@ type ContainerConfig struct {
|
|||||||
SharedNamespaceMap map[string]string `json:"sharedNamespaces"`
|
SharedNamespaceMap map[string]string `json:"sharedNamespaces"`
|
||||||
// Time container was created
|
// Time container was created
|
||||||
CreatedTime time.Time `json:"createdTime"`
|
CreatedTime time.Time `json:"createdTime"`
|
||||||
|
|
||||||
// TODO save log location here and pass into OCI code
|
// TODO save log location here and pass into OCI code
|
||||||
// TODO allow overriding of log path
|
// TODO allow overriding of log path
|
||||||
}
|
}
|
||||||
@ -155,6 +165,16 @@ func (c *Container) Name() string {
|
|||||||
return c.config.Name
|
return c.config.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShmDir returns the sources path to be mounted on /dev/shm in container
|
||||||
|
func (c *Container) ShmDir() string {
|
||||||
|
return c.config.ShmDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessLabel returns the selinux ProcessLabel of the container
|
||||||
|
func (c *Container) ProcessLabel() string {
|
||||||
|
return c.config.ProcessLabel
|
||||||
|
}
|
||||||
|
|
||||||
// Spec returns the container's OCI runtime spec
|
// Spec returns the container's OCI runtime spec
|
||||||
// The spec returned is the one used to create the container. The running
|
// The spec returned is the one used to create the container. The running
|
||||||
// spec may differ slightly as mounts are added based on the image
|
// spec may differ slightly as mounts are added based on the image
|
||||||
@ -307,7 +327,7 @@ func (c *Container) syncContainer() error {
|
|||||||
if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
|
if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
if err := c.save(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,13 +396,8 @@ func (c *Container) teardownStorage() error {
|
|||||||
return errors.Wrapf(ErrCtrStateInvalid, "cannot remove storage for container %s as it is running or paused", c.ID())
|
return errors.Wrapf(ErrCtrStateInvalid, "cannot remove storage for container %s as it is running or paused", c.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.state.Mounted {
|
if err := c.cleanupStorage(); err != nil {
|
||||||
if err := c.runtime.storageService.StopContainer(c.ID()); err != nil {
|
return errors.Wrapf(err, "failed to cleanup container %s storage", c.ID())
|
||||||
return errors.Wrapf(err, "error unmounting container %s root filesystem", c.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
c.state.Mounted = false
|
|
||||||
c.state.Mountpoint = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.runtime.storageService.DeleteContainer(c.ID()); err != nil {
|
if err := c.runtime.storageService.DeleteContainer(c.ID()); err != nil {
|
||||||
@ -426,8 +441,10 @@ func (c *Container) Init() (err error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Make the OCI runtime spec we will use
|
// Make the OCI runtime spec we will use
|
||||||
c.runningSpec = new(spec.Spec)
|
g := generate.NewFromSpec(c.config.Spec)
|
||||||
deepcopier.Copy(c.config.Spec).To(c.runningSpec)
|
// Mount ShmDir from host into container
|
||||||
|
g.AddBindMount(c.config.ShmDir, "/dev/shm", []string{"rw"})
|
||||||
|
c.runningSpec = g.Spec()
|
||||||
c.runningSpec.Root.Path = c.state.Mountpoint
|
c.runningSpec.Root.Path = c.state.Mountpoint
|
||||||
c.runningSpec.Annotations[crioAnnotations.Created] = c.config.CreatedTime.Format(time.RFC3339Nano)
|
c.runningSpec.Annotations[crioAnnotations.Created] = c.config.CreatedTime.Format(time.RFC3339Nano)
|
||||||
c.runningSpec.Annotations["org.opencontainers.image.stopSignal"] = fmt.Sprintf("%d", c.config.StopSignal)
|
c.runningSpec.Annotations["org.opencontainers.image.stopSignal"] = fmt.Sprintf("%d", c.config.StopSignal)
|
||||||
@ -455,11 +472,7 @@ func (c *Container) Init() (err error) {
|
|||||||
|
|
||||||
c.state.State = ContainerStateCreated
|
c.state.State = ContainerStateCreated
|
||||||
|
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
return c.save()
|
||||||
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts a container
|
// Start starts a container
|
||||||
@ -471,6 +484,19 @@ func (c *Container) Start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mounted, err := mount.Mounted(c.config.ShmDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mounted {
|
||||||
|
shmOptions := "mode=1777,size=" + strconv.Itoa(DefaultShmSize)
|
||||||
|
if err := unix.Mount("shm", c.config.ShmDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV,
|
||||||
|
label.FormatMountLabel(shmOptions, c.config.MountLabel)); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to mount shm tmpfs %q", c.config.ShmDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Container must be created or stopped to be started
|
// Container must be created or stopped to be started
|
||||||
if !(c.state.State == ContainerStateCreated || c.state.State == ContainerStateStopped) {
|
if !(c.state.State == ContainerStateCreated || c.state.State == ContainerStateStopped) {
|
||||||
return errors.Wrapf(ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
|
return errors.Wrapf(ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
|
||||||
@ -510,11 +536,7 @@ func (c *Container) Start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
return c.save()
|
||||||
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop uses the container's stop signal (or SIGTERM if no signal was specified)
|
// Stop uses the container's stop signal (or SIGTERM if no signal was specified)
|
||||||
@ -544,18 +566,7 @@ func (c *Container) Stop(timeout int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also unmount storage
|
return c.cleanupStorage()
|
||||||
if err := c.runtime.storageService.StopContainer(c.ID()); err != nil {
|
|
||||||
return errors.Wrapf(err, "error unmounting container %s root filesystem", c.ID())
|
|
||||||
}
|
|
||||||
c.state.Mountpoint = ""
|
|
||||||
c.state.Mounted = false
|
|
||||||
|
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
|
||||||
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill sends a signal to a container
|
// Kill sends a signal to a container
|
||||||
@ -638,8 +649,8 @@ func (c *Container) Mount(label string) (string, error) {
|
|||||||
c.state.Mounted = true
|
c.state.Mounted = true
|
||||||
c.config.MountLabel = mountLabel
|
c.config.MountLabel = mountLabel
|
||||||
|
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
if err := c.save(); err != nil {
|
||||||
return "", errors.Wrapf(err, "error saving container %s state", c.ID())
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mountPoint, nil
|
return mountPoint, nil
|
||||||
@ -669,7 +680,7 @@ func (c *Container) Unmount() error {
|
|||||||
c.state.Mountpoint = ""
|
c.state.Mountpoint = ""
|
||||||
c.state.Mounted = false
|
c.state.Mounted = false
|
||||||
|
|
||||||
return c.runtime.state.SaveContainer(c)
|
return c.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause pauses a container
|
// Pause pauses a container
|
||||||
@ -698,10 +709,7 @@ func (c *Container) Pause() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
return c.save()
|
||||||
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpause unpauses a container
|
// Unpause unpauses a container
|
||||||
@ -727,10 +735,7 @@ func (c *Container) Unpause() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.runtime.state.SaveContainer(c); err != nil {
|
return c.save()
|
||||||
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export exports a container's root filesystem as a tar archive
|
// Export exports a container's root filesystem as a tar archive
|
||||||
@ -809,3 +814,48 @@ func (c *Container) isStopped() (bool, error) {
|
|||||||
}
|
}
|
||||||
return c.state.State == ContainerStateStopped, nil
|
return c.state.State == ContainerStateStopped, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nil container state to the database
|
||||||
|
func (c *Container) save() error {
|
||||||
|
if err := c.runtime.state.SaveContainer(c); err != nil {
|
||||||
|
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanupStorage unmounts all mount points in container and cleans up container storage
|
||||||
|
func (c *Container) CleanupStorage() error {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
if err := c.syncContainer(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.cleanupStorage()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) cleanupStorage() error {
|
||||||
|
|
||||||
|
if c.state.Mounted {
|
||||||
|
for _, mount := range c.config.Mounts {
|
||||||
|
if err := unix.Unmount(mount, unix.MNT_DETACH); err != nil {
|
||||||
|
if err != syscall.EINVAL {
|
||||||
|
logrus.Warnf("container %s failed to unmount %s : %v", c.ID(), mount, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.config.Mounts = []string{}
|
||||||
|
|
||||||
|
// Also unmount storage
|
||||||
|
if err := c.runtime.storageService.StopContainer(c.ID()); err != nil {
|
||||||
|
return errors.Wrapf(err, "error unmounting container %s root filesystem", c.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.state.Mountpoint = ""
|
||||||
|
c.state.Mounted = false
|
||||||
|
|
||||||
|
if err := c.runtime.state.SaveContainer(c); err != nil {
|
||||||
|
return errors.Wrapf(err, "error saving container %s state", c.ID())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -37,6 +37,11 @@ const (
|
|||||||
|
|
||||||
// Timeout before declaring that runc has failed to kill a given container
|
// Timeout before declaring that runc has failed to kill a given container
|
||||||
killContainerTimeout = 5 * time.Second
|
killContainerTimeout = 5 * time.Second
|
||||||
|
// DefaultShmSize is the default shm size
|
||||||
|
DefaultShmSize = 64 * 1024 * 1024
|
||||||
|
// NsRunDir is the default directory in which running network namespaces
|
||||||
|
// are stored
|
||||||
|
NsRunDir = "/var/run/netns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OCIRuntime represents an OCI-compatible runtime that libpod can call into
|
// OCIRuntime represents an OCI-compatible runtime that libpod can call into
|
||||||
|
@ -21,7 +21,7 @@ const (
|
|||||||
// MountNamespace represents the mount namespace
|
// MountNamespace represents the mount namespace
|
||||||
MountNamespace = "mount"
|
MountNamespace = "mount"
|
||||||
// NetNamespace represents the network namespace
|
// NetNamespace represents the network namespace
|
||||||
NetNamespace = "net"
|
NetNamespace = "network"
|
||||||
// PIDNamespace represents the PID namespace
|
// PIDNamespace represents the PID namespace
|
||||||
PIDNamespace = "pid"
|
PIDNamespace = "pid"
|
||||||
// UserNamespace represents the user namespace
|
// UserNamespace represents the user namespace
|
||||||
@ -252,13 +252,26 @@ func WithNoPivotRoot(noPivot bool) RuntimeOption {
|
|||||||
|
|
||||||
// Container Creation Options
|
// Container Creation Options
|
||||||
|
|
||||||
// WithSELinuxMountLabel sets the mount label for SELinux
|
// WithShmDir sets the directory that should be mounted on /dev/shm
|
||||||
func WithSELinuxMountLabel(mountLabel string) CtrCreateOption {
|
func WithShmDir(dir string) CtrCreateOption {
|
||||||
return func(ctr *Container) error {
|
return func(ctr *Container) error {
|
||||||
if ctr.valid {
|
if ctr.valid {
|
||||||
return ErrCtrFinalized
|
return ErrCtrFinalized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctr.config.ShmDir = dir
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSELinuxLabels sets the mount label for SELinux
|
||||||
|
func WithSELinuxLabels(processLabel, mountLabel string) CtrCreateOption {
|
||||||
|
return func(ctr *Container) error {
|
||||||
|
if ctr.valid {
|
||||||
|
return ErrCtrFinalized
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr.config.ProcessLabel = processLabel
|
||||||
ctr.config.MountLabel = mountLabel
|
ctr.config.MountLabel = mountLabel
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package libpod
|
package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -54,6 +57,16 @@ func (r *Runtime) NewContainer(spec *spec.Spec, options ...CtrCreateOption) (c *
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if ctr.config.ShmDir == "" {
|
||||||
|
ctr.config.ShmDir = filepath.Join(ctr.bundlePath(), "shm")
|
||||||
|
if err := os.MkdirAll(ctr.config.ShmDir, 0700); err != nil {
|
||||||
|
if !os.IsExist(err) {
|
||||||
|
return nil, errors.Wrapf(err, "unable to create shm %q dir", ctr.config.ShmDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctr.config.Mounts = append(ctr.config.Mounts, ctr.config.ShmDir)
|
||||||
|
}
|
||||||
|
|
||||||
// If the container is in a pod, add it to the pod
|
// If the container is in a pod, add it to the pod
|
||||||
if ctr.pod != nil {
|
if ctr.pod != nil {
|
||||||
if err := ctr.pod.addContainer(ctr); err != nil {
|
if err := ctr.pod.addContainer(ctr); err != nil {
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
|
|
||||||
// DBSchema is the current DB schema version
|
// DBSchema is the current DB schema version
|
||||||
// Increments every time a change is made to the database's tables
|
// Increments every time a change is made to the database's tables
|
||||||
const DBSchema = 1
|
const DBSchema = 2
|
||||||
|
|
||||||
// SQLState is a state implementation backed by a persistent SQLite3 database
|
// SQLState is a state implementation backed by a persistent SQLite3 database
|
||||||
type SQLState struct {
|
type SQLState struct {
|
||||||
@ -230,7 +230,7 @@ func (s *SQLState) HasContainer(id string) (bool, error) {
|
|||||||
func (s *SQLState) AddContainer(ctr *Container) (err error) {
|
func (s *SQLState) AddContainer(ctr *Container) (err error) {
|
||||||
const (
|
const (
|
||||||
addCtr = `INSERT INTO containers VALUES (
|
addCtr = `INSERT INTO containers VALUES (
|
||||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||||
);`
|
);`
|
||||||
addCtrState = `INSERT INTO containerState VALUES (
|
addCtrState = `INSERT INTO containerState VALUES (
|
||||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||||
@ -265,11 +265,18 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
mounts, err := json.Marshal(ctr.config.Mounts)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s monunts to JSON", ctr.ID())
|
||||||
|
}
|
||||||
// Add static container information
|
// Add static container information
|
||||||
_, err = tx.Exec(addCtr,
|
_, err = tx.Exec(addCtr,
|
||||||
ctr.ID(),
|
ctr.ID(),
|
||||||
ctr.Name(),
|
ctr.Name(),
|
||||||
|
ctr.config.ProcessLabel,
|
||||||
ctr.config.MountLabel,
|
ctr.config.MountLabel,
|
||||||
|
string(mounts),
|
||||||
|
ctr.config.ShmDir,
|
||||||
ctr.config.StaticDir,
|
ctr.config.StaticDir,
|
||||||
boolToSQL(ctr.config.Stdin),
|
boolToSQL(ctr.config.Stdin),
|
||||||
string(labelsJSON),
|
string(labelsJSON),
|
||||||
|
@ -166,7 +166,10 @@ func prepareDB(db *sql.DB) (err error) {
|
|||||||
CREATE TABLE IF NOT EXISTS containers(
|
CREATE TABLE IF NOT EXISTS containers(
|
||||||
Id TEXT NOT NULL PRIMARY KEY,
|
Id TEXT NOT NULL PRIMARY KEY,
|
||||||
Name TEXT NOT NULL UNIQUE,
|
Name TEXT NOT NULL UNIQUE,
|
||||||
|
ProcessLabel TEXT NOT NULL,
|
||||||
MountLabel TEXT NOT NULL,
|
MountLabel TEXT NOT NULL,
|
||||||
|
Mounts TEXT NOT NULL,
|
||||||
|
ShmDir TEXT NOT NULL,
|
||||||
StaticDir TEXT NOT NULL,
|
StaticDir TEXT NOT NULL,
|
||||||
Stdin INTEGER NOT NULL,
|
Stdin INTEGER NOT NULL,
|
||||||
LabelsJSON TEXT NOT NULL,
|
LabelsJSON TEXT NOT NULL,
|
||||||
@ -267,7 +270,10 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string) (*Contai
|
|||||||
var (
|
var (
|
||||||
id string
|
id string
|
||||||
name string
|
name string
|
||||||
|
processLabel string
|
||||||
mountLabel string
|
mountLabel string
|
||||||
|
mounts string
|
||||||
|
shmDir string
|
||||||
staticDir string
|
staticDir string
|
||||||
stdin int
|
stdin int
|
||||||
labelsJSON string
|
labelsJSON string
|
||||||
@ -290,7 +296,10 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string) (*Contai
|
|||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&id,
|
&id,
|
||||||
&name,
|
&name,
|
||||||
|
&processLabel,
|
||||||
&mountLabel,
|
&mountLabel,
|
||||||
|
&mounts,
|
||||||
|
&shmDir,
|
||||||
&staticDir,
|
&staticDir,
|
||||||
&stdin,
|
&stdin,
|
||||||
&labelsJSON,
|
&labelsJSON,
|
||||||
@ -325,7 +334,12 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string) (*Contai
|
|||||||
ctr.config.RootfsImageID = rootfsImageID
|
ctr.config.RootfsImageID = rootfsImageID
|
||||||
ctr.config.RootfsImageName = rootfsImageName
|
ctr.config.RootfsImageName = rootfsImageName
|
||||||
ctr.config.UseImageConfig = boolFromSQL(useImageConfig)
|
ctr.config.UseImageConfig = boolFromSQL(useImageConfig)
|
||||||
|
ctr.config.ProcessLabel = processLabel
|
||||||
ctr.config.MountLabel = mountLabel
|
ctr.config.MountLabel = mountLabel
|
||||||
|
if err := json.Unmarshal([]byte(mounts), &ctr.config.Mounts); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error parsing container %s mounts JSON", id)
|
||||||
|
}
|
||||||
|
ctr.config.ShmDir = shmDir
|
||||||
ctr.config.StaticDir = staticDir
|
ctr.config.StaticDir = staticDir
|
||||||
ctr.config.Stdin = boolFromSQL(stdin)
|
ctr.config.Stdin = boolFromSQL(stdin)
|
||||||
ctr.config.StopSignal = stopSignal
|
ctr.config.StopSignal = stopSignal
|
||||||
|
@ -30,3 +30,21 @@ function setup() {
|
|||||||
echo $output
|
echo $output
|
||||||
[ "$status" -ne 0 ]
|
[ "$status" -ne 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "run ipcns test" {
|
||||||
|
|
||||||
|
${KPOD_BINARY} ${KPOD_OPTIONS} pull ${ALPINE}
|
||||||
|
|
||||||
|
tmp=$(mktemp /dev/shm/foo.XXXXX)
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} run --ipc=host ${ALPINE} ls $tmp
|
||||||
|
echo $output
|
||||||
|
out=$(echo $output | tr -d '\r')
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ $out != $tmp ]
|
||||||
|
|
||||||
|
rm -f $tmp
|
||||||
|
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} run --ipc=badpid ${ALPINE} sh -c 'echo $$'
|
||||||
|
echo $output
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user