diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal.go | 24 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 19 | ||||
-rw-r--r-- | libpod/container_log.go | 4 | ||||
-rw-r--r-- | libpod/define/errors.go | 3 | ||||
-rw-r--r-- | libpod/define/pod_inspect.go | 3 | ||||
-rw-r--r-- | libpod/filters/pods.go | 8 | ||||
-rw-r--r-- | libpod/image/filters.go | 6 | ||||
-rw-r--r-- | libpod/oci.go | 7 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 25 | ||||
-rw-r--r-- | libpod/oci_missing.go | 5 | ||||
-rw-r--r-- | libpod/oci_util.go | 36 | ||||
-rw-r--r-- | libpod/options.go | 24 | ||||
-rw-r--r-- | libpod/pod.go | 25 | ||||
-rw-r--r-- | libpod/pod_api.go | 1 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 6 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 3 |
16 files changed, 178 insertions, 21 deletions
diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 43e873bd6..f6fc3c1a4 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1209,13 +1209,35 @@ func (c *Container) stop(timeout uint) error { } } + // Check if conmon is still alive. + // If it is not, we won't be getting an exit file. + conmonAlive, err := c.ociRuntime.CheckConmonRunning(c) + if err != nil { + return err + } + if err := c.ociRuntime.StopContainer(c, timeout, all); err != nil { return err } + c.newContainerEvent(events.Stop) + c.state.PID = 0 c.state.ConmonPID = 0 c.state.StoppedByUser = true + + if !conmonAlive { + // Conmon is dead, so we can't epect an exit code. + c.state.ExitCode = -1 + c.state.FinishedTime = time.Now() + c.state.State = define.ContainerStateStopped + if err := c.save(); err != nil { + logrus.Errorf("Error saving container %s status: %v", c.ID(), err) + } + + return errors.Wrapf(define.ErrConmonDead, "container %s conmon process missing, cannot retrieve exit code", c.ID()) + } + if err := c.save(); err != nil { return errors.Wrapf(err, "error saving container %s state after stopping", c.ID()) } @@ -1225,8 +1247,6 @@ func (c *Container) stop(timeout uint) error { return err } - c.newContainerEvent(events.Stop) - return nil } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index d08e012a6..9afe11b2b 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -79,7 +79,8 @@ func (c *Container) prepare() error { go func() { defer wg.Done() // Set up network namespace if not already set up - if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS { + noNetNS := c.state.NetNS == nil + if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS { netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c) if createNetNSErr != nil { return @@ -94,7 +95,7 @@ func (c *Container) prepare() error { } // handle rootless network namespace setup - if c.state.NetNS != nil && c.config.NetMode.IsSlirp4netns() && !c.config.PostConfigureNetNS { + if noNetNS && c.config.NetMode.IsSlirp4netns() && !c.config.PostConfigureNetNS { createNetNSErr = c.runtime.setupRootlessNetNS(c) } }() @@ -392,7 +393,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } for _, i := range c.config.Spec.Linux.Namespaces { - if i.Type == spec.UTSNamespace { + if i.Type == spec.UTSNamespace && i.Path == "" { hostname := c.Hostname() g.SetHostname(hostname) g.AddProcessEnv("HOSTNAME", hostname) @@ -591,7 +592,8 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr if specNS == spec.UTSNamespace { hostname := nsCtr.Hostname() - g.SetHostname(hostname) + // Joining an existing namespace, cannot set the hostname + g.SetHostname("") g.AddProcessEnv("HOSTNAME", hostname) } @@ -1171,6 +1173,15 @@ func (c *Container) makeBindMounts() error { // finally, save it in the new container c.state.BindMounts["/etc/hosts"] = hostsPath } + + if !hasCurrentUserMapped(c) { + if err := makeAccessible(resolvPath, c.RootUID(), c.RootGID()); err != nil { + return err + } + if err := makeAccessible(hostsPath, c.RootUID(), c.RootGID()); err != nil { + return err + } + } } else { if !c.config.UseImageResolvConf { newResolv, err := c.generateResolvConf() diff --git a/libpod/container_log.go b/libpod/container_log.go index bfa303e84..c3a84d048 100644 --- a/libpod/container_log.go +++ b/libpod/container_log.go @@ -19,7 +19,7 @@ func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChan return nil } -// ReadLog reads a containers log based on the input options and returns loglines over a channel +// ReadLog reads a containers log based on the input options and returns loglines over a channel. func (c *Container) ReadLog(options *logs.LogOptions, logChannel chan *logs.LogLine) error { // TODO Skip sending logs until journald logs can be read // TODO make this not a magic string @@ -61,7 +61,7 @@ func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *l partial += nll.Msg continue } else if !nll.Partial() && len(partial) > 1 { - nll.Msg = partial + nll.Msg = partial + nll.Msg partial = "" } nll.CID = c.ID() diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 16df2a1cc..083553b7e 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -141,6 +141,9 @@ var ( // ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH) // is out of date for the current podman version ErrConmonOutdated = errors.New("outdated conmon version") + // ErrConmonDead indicates that the container's conmon process has been + // killed, preventing normal operation. + ErrConmonDead = errors.New("conmon process killed") // ErrImageInUse indicates the requested operation failed because the image was in use ErrImageInUse = errors.New("image is being used") diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 26fd2cab4..7f06e16fc 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -18,6 +18,9 @@ type InspectPodData struct { Namespace string `json:"Namespace,omitempty"` // Created is the time when the pod was created. Created time.Time + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string `json:"CreateCommand,omitempty"` // State represents the current state of the pod. State string `json:"State"` // Hostname is the hostname that the pod will set. diff --git a/libpod/filters/pods.go b/libpod/filters/pods.go index 9bf436eab..0edb9fbf2 100644 --- a/libpod/filters/pods.go +++ b/libpod/filters/pods.go @@ -57,13 +57,13 @@ func GeneratePodFilterFunc(filter, filterValue string) ( return nil, errors.Errorf("%s is not a valid status", filterValue) } return func(p *libpod.Pod) bool { - ctr_statuses, err := p.Status() + ctrStatuses, err := p.Status() if err != nil { return false } - for _, ctr_status := range ctr_statuses { - state := ctr_status.String() - if ctr_status == define.ContainerStateConfigured { + for _, ctrStatus := range ctrStatuses { + state := ctrStatus.String() + if ctrStatus == define.ContainerStateConfigured { state = "created" } if state == filterValue { diff --git a/libpod/image/filters.go b/libpod/image/filters.go index 747eba165..9d99fb344 100644 --- a/libpod/image/filters.go +++ b/libpod/image/filters.go @@ -102,8 +102,8 @@ func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter { } } -// IdFilter allows you to filter by image Id -func IdFilter(idFilter string) ResultFilter { +// IDFilter allows you to filter by image Id +func IDFilter(idFilter string) ResultFilter { return func(i *Image) bool { return i.ID() == idFilter } @@ -172,7 +172,7 @@ func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilt case "reference": filterFuncs = append(filterFuncs, ReferenceFilter(ctx, splitFilter[1])) case "id": - filterFuncs = append(filterFuncs, IdFilter(splitFilter[1])) + filterFuncs = append(filterFuncs, IDFilter(splitFilter[1])) default: return nil, errors.Errorf("invalid filter %s ", splitFilter[0]) } diff --git a/libpod/oci.go b/libpod/oci.go index 684a7ba42..c2f0041b1 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -107,6 +107,13 @@ type OCIRuntime interface { // error. CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error + // CheckConmonRunning verifies that the given container's Conmon + // instance is still running. Runtimes without Conmon, or systems where + // the PID of conmon is not available, should mock this as True. + // True indicates that Conmon for the instance is running, False + // indicates it is not. + CheckConmonRunning(ctr *Container) (bool, error) + // SupportsCheckpoint returns whether this OCI runtime // implementation supports the CheckpointContainer() operation. SupportsCheckpoint() bool diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 9c92b036e..0921a532b 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -669,6 +669,31 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...) } +func (r *ConmonOCIRuntime) CheckConmonRunning(ctr *Container) (bool, error) { + if ctr.state.ConmonPID == 0 { + // If the container is running or paused, assume Conmon is + // running. We didn't record Conmon PID on some old versions, so + // that is likely what's going on... + // Unusual enough that we should print a warning message though. + if ctr.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { + logrus.Warnf("Conmon PID is not set, but container is running!") + return true, nil + } + // Container's not running, so conmon PID being unset is + // expected. Conmon is not running. + return false, nil + } + + // We have a conmon PID. Ping it with signal 0. + if err := unix.Kill(ctr.state.ConmonPID, 0); err != nil { + if err == unix.ESRCH { + return false, nil + } + return false, errors.Wrapf(err, "error pinging container %s conmon with signal 0", ctr.ID()) + } + return true, nil +} + // SupportsCheckpoint checks if the OCI runtime supports checkpointing // containers. func (r *ConmonOCIRuntime) SupportsCheckpoint() bool { diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 4da16876c..90e90cc6c 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -163,6 +163,11 @@ func (r *MissingRuntime) CheckpointContainer(ctr *Container, options ContainerCh return r.printError() } +// CheckConmonRunning is not available as the runtime is missing +func (r *MissingRuntime) CheckConmonRunning(ctr *Container) (bool, error) { + return false, r.printError() +} + // SupportsCheckpoint returns false as checkpointing requires a working runtime func (r *MissingRuntime) SupportsCheckpoint() bool { return false diff --git a/libpod/oci_util.go b/libpod/oci_util.go index 53567d2d0..8b40dad81 100644 --- a/libpod/oci_util.go +++ b/libpod/oci_util.go @@ -36,14 +36,30 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { var files []*os.File notifySCTP := false for _, i := range ports { + isV6 := net.ParseIP(i.HostIP).To4() == nil + if i.HostIP == "" { + isV6 = false + } switch i.Protocol { case "udp": - addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + var ( + addr *net.UDPAddr + err error + ) + if isV6 { + addr, err = net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort)) + } else { + addr, err = net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + } if err != nil { return nil, errors.Wrapf(err, "cannot resolve the UDP address") } - server, err := net.ListenUDP("udp", addr) + proto := "udp4" + if isV6 { + proto = "udp6" + } + server, err := net.ListenUDP(proto, addr) if err != nil { return nil, errors.Wrapf(err, "cannot listen on the UDP port") } @@ -54,12 +70,24 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { files = append(files, f) case "tcp": - addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + var ( + addr *net.TCPAddr + err error + ) + if isV6 { + addr, err = net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort)) + } else { + addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + } if err != nil { return nil, errors.Wrapf(err, "cannot resolve the TCP address") } - server, err := net.ListenTCP("tcp4", addr) + proto := "tcp4" + if isV6 { + proto = "tcp6" + } + server, err := net.ListenTCP(proto, addr) if err != nil { return nil, errors.Wrapf(err, "cannot listen on the TCP port") } diff --git a/libpod/options.go b/libpod/options.go index 8e0d3df86..5a0f60093 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1538,6 +1538,30 @@ func WithPodHostname(hostname string) PodCreateOption { } } +// WithPodCreateCommand adds the full command plus arguments of the current +// process to the pod config. +func WithPodCreateCommand() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + pod.config.CreateCommand = os.Args + return nil + } +} + +// WithInfraConmonPidFile sets the path to a custom conmon PID file for the +// infra container. +func WithInfraConmonPidFile(path string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + pod.config.InfraContainer.ConmonPidFile = path + return nil + } +} + // WithPodLabels sets the labels of a pod. func WithPodLabels(labels map[string]string) PodCreateOption { return func(pod *Pod) error { diff --git a/libpod/pod.go b/libpod/pod.go index 8afaa6052..bf0d7a397 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -64,6 +64,10 @@ type PodConfig struct { // Time pod was created CreatedTime time.Time `json:"created"` + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string `json:"CreateCommand,omitempty"` + // ID of the pod's lock LockID uint32 `json:"lockID"` } @@ -79,6 +83,7 @@ type podState struct { // InfraContainerConfig is the configuration for the pod's infra container type InfraContainerConfig struct { + ConmonPidFile string `json:"conmonPidFile"` HasInfraContainer bool `json:"makeInfraContainer"` HostNetwork bool `json:"infraHostNetwork,omitempty"` PortBindings []ocicni.PortMapping `json:"infraPortBindings"` @@ -124,6 +129,12 @@ func (p *Pod) CreatedTime() time.Time { return p.config.CreatedTime } +// CreateCommand returns the os.Args of the process with which the pod has been +// created. +func (p *Pod) CreateCommand() []string { + return p.config.CreateCommand +} + // CgroupParent returns the pod's CGroup parent func (p *Pod) CgroupParent() string { return p.config.CgroupParent @@ -246,6 +257,20 @@ func (p *Pod) InfraContainerID() (string, error) { return p.state.InfraContainerID, nil } +// InfraContainer returns the infra container. +func (p *Pod) InfraContainer() (*Container, error) { + if !p.HasInfraContainer() { + return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") + } + + id, err := p.InfraContainerID() + if err != nil { + return nil, err + } + + return p.runtime.state.Container(id) +} + // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) diff --git a/libpod/pod_api.go b/libpod/pod_api.go index e2c4b515d..c8605eb69 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -489,6 +489,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { Name: p.Name(), Namespace: p.Namespace(), Created: p.CreatedTime(), + CreateCommand: p.config.CreateCommand, State: podState, Hostname: p.config.Hostname, Labels: p.Labels(), diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 655b42e51..aa91dff03 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -464,9 +464,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } - // Check that the container's in a good state to be removed + // Check that the container's in a good state to be removed. if c.state.State == define.ContainerStateRunning { - if err := c.stop(c.StopTimeout()); err != nil { + // Ignore ErrConmonDead - we couldn't retrieve the container's + // exit code properly, but it's still stopped. + if err := c.stop(c.StopTimeout()); err != nil && errors.Cause(err) != define.ErrConmonDead { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } } diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 06a7b3936..a0dee3aa1 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -130,6 +130,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) options = append(options, WithName(containerName)) options = append(options, withIsInfra()) + if len(p.config.InfraContainer.ConmonPidFile) > 0 { + options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) + } return r.newContainer(ctx, g.Config, options...) } |