diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 3 | ||||
-rw-r--r-- | libpod/container_api.go | 5 | ||||
-rw-r--r-- | libpod/container_internal.go | 18 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 31 | ||||
-rw-r--r-- | libpod/events/config.go | 10 | ||||
-rw-r--r-- | libpod/events/journal_linux.go | 5 | ||||
-rw-r--r-- | libpod/events/logfile.go | 5 | ||||
-rw-r--r-- | libpod/events/nullout.go | 8 | ||||
-rw-r--r-- | libpod/info.go | 2 | ||||
-rw-r--r-- | libpod/kube.go | 12 | ||||
-rw-r--r-- | libpod/networking_linux.go | 60 | ||||
-rw-r--r-- | libpod/oci_internal_linux.go | 16 | ||||
-rw-r--r-- | libpod/oci_linux.go | 8 | ||||
-rw-r--r-- | libpod/runtime.go | 2 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 4 |
15 files changed, 145 insertions, 44 deletions
diff --git a/libpod/container.go b/libpod/container.go index 2d96b1120..9c01d2adf 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -377,6 +377,9 @@ type ContainerConfig struct { RestartRetries uint `json:"restart_retries,omitempty"` // TODO log options for log drivers + // PostConfigureNetNS needed when a user namespace is created by an OCI runtime + // if the network namespace is created before the user namespace it will be + // owned by the wrong user namespace. PostConfigureNetNS bool `json:"postConfigureNetNS"` // OCIRuntime used to create the container diff --git a/libpod/container_api.go b/libpod/container_api.go index ef9c3f006..abcfcb271 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -773,6 +773,11 @@ type ContainerCheckpointOptions struct { // IgnoreRootfs tells the API to not export changes to // the container's root file-system (or to not import) IgnoreRootfs bool + // IgnoreStaticIP tells the API to ignore the IP set + // during 'podman run' with '--ip'. This is especially + // important to be able to restore a container multiple + // times with '--import --name'. + IgnoreStaticIP bool } // Checkpoint checkpoints a container diff --git a/libpod/container_internal.go b/libpod/container_internal.go index aba9c5b93..313f67963 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1152,9 +1152,27 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e c.newContainerEvent(events.Restart) if c.state.State == define.ContainerStateRunning { + conmonPID := c.state.ConmonPID if err := c.stop(timeout); err != nil { return err } + // Old versions of conmon have a bug where they create the exit file before + // closing open file descriptors causing a race condition when restarting + // containers with open ports since we cannot bind the ports as they're not + // yet closed by conmon. + // + // Killing the old conmon PID is ~okay since it forces the FDs of old conmons + // to be closed, while it's a NOP for newer versions which should have + // exited already. + if conmonPID != 0 { + // Ignore errors from FindProcess() as conmon could already have exited. + p, err := os.FindProcess(conmonPID) + if p != nil && err == nil { + if err = p.Kill(); err != nil { + logrus.Debugf("error killing conmon process: %v", err) + } + } + } } defer func() { if err != nil { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 6dbd53fbf..d06c19b8c 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -63,12 +63,12 @@ func (c *Container) unmountSHM(mount string) error { // namespaces func (c *Container) prepare() (err error) { var ( - wg sync.WaitGroup - netNS ns.NetNS - networkStatus []*cnitypes.Result - createNetNSErr, mountStorageErr error - mountPoint string - tmpStateLock sync.Mutex + wg sync.WaitGroup + netNS ns.NetNS + networkStatus []*cnitypes.Result + createNetNSErr, mountStorageErr, rootlessSetupErr error + mountPoint string + tmpStateLock sync.Mutex ) wg.Add(2) @@ -87,6 +87,11 @@ func (c *Container) prepare() (err error) { c.state.NetNS = netNS c.state.NetworkStatus = networkStatus } + + // Setup rootless networking, requires c.state.NetNS to be set + if rootless.IsRootless() { + rootlessSetupErr = c.runtime.setupRootlessNetNS(c) + } } }() // Mount storage if not mounted @@ -132,6 +137,10 @@ func (c *Container) prepare() (err error) { return mountStorageErr } + if rootlessSetupErr != nil { + return rootlessSetupErr + } + // Save the container return c.save() } @@ -743,6 +752,14 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti return err } + // If a container is restored multiple times from an exported checkpoint with + // the help of '--import --name', the restore will fail if during 'podman run' + // a static container IP was set with '--ip'. The user can tell the restore + // process to ignore the static IP with '--ignore-static-ip' + if options.IgnoreStaticIP { + c.config.StaticIP = nil + } + // Read network configuration from checkpoint // Currently only one interface with one IP is supported. networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status")) @@ -752,7 +769,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti // TODO: This implicit restoring with or without IP depending on an // unrelated restore parameter (--name) does not seem like the // best solution. - if err == nil && options.Name == "" { + if err == nil && options.Name == "" && !options.IgnoreStaticIP { // The file with the network.status does exist. Let's restore the // container with the same IP address as during checkpointing. defer networkStatusFile.Close() diff --git a/libpod/events/config.go b/libpod/events/config.go index 96172d47b..453c64f8c 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -22,13 +22,13 @@ const ( type Event struct { // ContainerExitCode is for storing the exit code of a container which can // be used for "internal" event notification - ContainerExitCode int + ContainerExitCode int `json:",omitempty"` // ID can be for the container, image, volume, etc - ID string + ID string `json:",omitempty"` // Image used where applicable - Image string + Image string `json:",omitempty"` // Name where applicable - Name string + Name string `json:",omitempty"` // Status describes the event that occurred Status Status // Time the event occurred @@ -53,6 +53,8 @@ type Eventer interface { Write(event Event) error // Read an event from the backend Read(options ReadOptions) error + // String returns the type of event logger + String() string } // ReadOptions describe the attributes needed to read event logs diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index d5bce4334..ae96e3b3b 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -146,3 +146,8 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { / } return &newEvent, nil } + +// String returns a string representation of the logger +func (e EventJournalD) String() string { + return Journald.String() +} diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go index 30d72b9fc..4b65b0ad0 100644 --- a/libpod/events/logfile.go +++ b/libpod/events/logfile.go @@ -71,3 +71,8 @@ func (e EventLogFile) Read(options ReadOptions) error { close(options.EventChannel) return nil } + +// String returns a string representation of the logger +func (e EventLogFile) String() string { + return LogFile.String() +} diff --git a/libpod/events/nullout.go b/libpod/events/nullout.go index b11afcf80..f3b36e609 100644 --- a/libpod/events/nullout.go +++ b/libpod/events/nullout.go @@ -17,6 +17,10 @@ func (e EventToNull) Read(options ReadOptions) error { // NewNullEventer returns a new null eventer. You should only do this for // the purposes on internal libpod testing. func NewNullEventer() Eventer { - e := EventToNull{} - return e + return EventToNull{} +} + +// String returns a string representation of the logger +func (e EventToNull) String() string { + return "none" } diff --git a/libpod/info.go b/libpod/info.go index 4a89fa648..9ab6f7e52 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -102,7 +102,7 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) { return nil, errors.Wrapf(err, "error getting hostname") } info["hostname"] = host - + info["eventlogger"] = r.eventer.String() return info, nil } diff --git a/libpod/kube.go b/libpod/kube.go index 084a3df4f..d0e7baf95 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -406,18 +406,26 @@ func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v drop []v1.Capability add []v1.Capability ) + dedupDrop := make(map[string]bool) + dedupAdd := make(map[string]bool) // Find caps in the defaultCaps but not in the container's // those indicate a dropped cap for _, capability := range defaultCaps { if !util.StringInSlice(capability, containerCaps) { - drop = append(drop, v1.Capability(capability)) + if _, ok := dedupDrop[capability]; !ok { + drop = append(drop, v1.Capability(capability)) + dedupDrop[capability] = true + } } } // Find caps in the container but not in the defaults; those indicate // an added cap for _, capability := range containerCaps { if !util.StringInSlice(capability, defaultCaps) { - add = append(add, v1.Capability(capability)) + if _, ok := dedupAdd[capability]; !ok { + add = append(add, v1.Capability(capability)) + dedupAdd[capability] = true + } } } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index bef3f7739..73e839bda 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -103,9 +103,6 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re // Create and configure a new network namespace for a container func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, err error) { - if rootless.IsRootless() { - return nil, nil, errors.New("cannot configure a new network namespace in rootless mode, only --network=slirp4netns is supported") - } ctrNS, err := netns.NewNS() if err != nil { return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) @@ -123,7 +120,10 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID()) - networkStatus, err := r.configureNetNS(ctr, ctrNS) + networkStatus := []*cnitypes.Result{} + if !rootless.IsRootless() { + networkStatus, err = r.configureNetNS(ctr, ctrNS) + } return ctrNS, networkStatus, err } @@ -151,9 +151,6 @@ func checkSlirpFlags(path string) (bool, bool, error) { // Configure the network namespace for a rootless container func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { - defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR) - defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW) - path := r.config.NetworkCmdPath if path == "" { @@ -177,7 +174,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { cmdArgs := []string{} if havePortMapping { - cmdArgs = append(cmdArgs, "--api-socket", apiSocket, fmt.Sprintf("%d", ctr.state.PID)) + cmdArgs = append(cmdArgs, "--api-socket", apiSocket) } dhp, mtu, err := checkSlirpFlags(path) if err != nil { @@ -189,13 +186,27 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { if mtu { cmdArgs = append(cmdArgs, "--mtu", "65520") } - cmdArgs = append(cmdArgs, "-c", "-e", "3", "-r", "4", fmt.Sprintf("%d", ctr.state.PID), "tap0") - cmd := exec.Command(path, cmdArgs...) + cmdArgs = append(cmdArgs, "-c", "-e", "3", "-r", "4") + if !ctr.config.PostConfigureNetNS { + ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe() + if err != nil { + return errors.Wrapf(err, "failed to create rootless network sync pipe") + } + cmdArgs = append(cmdArgs, "--netns-type=path", ctr.state.NetNS.Path(), "tap0") + } else { + defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR) + defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW) + cmdArgs = append(cmdArgs, fmt.Sprintf("%d", ctr.state.PID), "tap0") + } + cmd := exec.Command(path, cmdArgs...) + logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " ")) cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, } + + // Leak one end of the pipe in slirp4netns, the other will be sent to conmon cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncR, syncW) if err := cmd.Start(); err != nil { @@ -410,22 +421,25 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { } } - logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) + logrus.Debugf("Tearing down network namespace for container %s", ctr.ID()) - var requestedIP net.IP - if ctr.requestedIP != nil { - requestedIP = ctr.requestedIP - // cancel request for a specific IP in case the container is reused later - ctr.requestedIP = nil - } else { - requestedIP = ctr.config.StaticIP - } + // rootless containers do not use the CNI plugin + if !rootless.IsRootless() { + var requestedIP net.IP + if ctr.requestedIP != nil { + requestedIP = ctr.requestedIP + // cancel request for a specific IP in case the container is reused later + ctr.requestedIP = nil + } else { + requestedIP = ctr.config.StaticIP + } - podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP) + podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP) - // The network may have already been torn down, so don't fail here, just log - if err := r.netPlugin.TearDownPod(podNetwork); err != nil { - return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID()) + // The network may have already been torn down, so don't fail here, just log + if err := r.netPlugin.TearDownPod(podNetwork); err != nil { + return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID()) + } } // First unmount the namespace diff --git a/libpod/oci_internal_linux.go b/libpod/oci_internal_linux.go index 0bcd021db..28e4b5b82 100644 --- a/libpod/oci_internal_linux.go +++ b/libpod/oci_internal_linux.go @@ -130,10 +130,16 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Containe } if ctr.config.NetMode.IsSlirp4netns() { - ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe() - if err != nil { - return errors.Wrapf(err, "failed to create rootless network sync pipe") + if ctr.config.PostConfigureNetNS { + ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe() + if err != nil { + return errors.Wrapf(err, "failed to create rootless network sync pipe") + } + } else { + defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR) + defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW) } + // Leak one end in conmon, the other one will be leaked into slirp4netns cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncW) } @@ -278,6 +284,10 @@ func (r *OCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, pidPath // No case here should happen except JSONLogging, but keep this here in case the options are extended logrus.Errorf("%s logging specified but not supported. Choosing k8s-file logging instead", ctr.LogDriver()) fallthrough + case "": + // to get here, either a user would specify `--log-driver ""`, or this came from another place in libpod + // since the former case is obscure, and the latter case isn't an error, let's silently fallthrough + fallthrough case KubernetesLogging: logDriver = fmt.Sprintf("%s:%s", KubernetesLogging, logPath) } diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 45365203e..4d8e36516 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -405,6 +405,14 @@ func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error { stopSignal = uint(syscall.SIGTERM) } + defer func() { + // cleanup container networking + err = ctr.cleanupNetwork() + if err != nil { + logrus.Errorf("Error cleaning up container: %s network: %v", ctr.ID(), err) + } + }() + if timeout > 0 { if err := r.killContainer(ctr, stopSignal); err != nil { // Is the container gone? diff --git a/libpod/runtime.go b/libpod/runtime.go index ffdbc32f1..b66b68cfd 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -52,7 +52,7 @@ const ( var ( // InstallPrefix is the prefix where podman will be installed. // It can be overridden at build time. - installPrefix = "/usr/local" + installPrefix = "/usr" // EtcDir is the sysconfdir where podman should look for system config files. // It can be overridden at build time. etcDir = "/etc" diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index da35b7f93..20b841296 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -95,7 +95,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID if isRootless { netmode = "slirp4netns" } - options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, netmode, networks)) + // PostConfigureNetNS should not be set since user namespace sharing is not implemented + // and rootless networking no longer supports post configuration setup + options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, networks)) return r.newContainer(ctx, g.Config, options...) } |