diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_api.go | 4 | ||||
-rw-r--r-- | libpod/container_copy_linux.go | 2 | ||||
-rw-r--r-- | libpod/container_internal.go | 52 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 63 | ||||
-rw-r--r-- | libpod/container_log_linux.go | 6 | ||||
-rw-r--r-- | libpod/kube.go | 27 | ||||
-rw-r--r-- | libpod/network/cni/cni_conversion.go | 2 | ||||
-rw-r--r-- | libpod/network/cni/cni_types.go | 2 | ||||
-rw-r--r-- | libpod/network/cni/config_test.go | 42 | ||||
-rw-r--r-- | libpod/network/cni/network.go | 22 | ||||
-rw-r--r-- | libpod/network/types/network.go | 2 | ||||
-rw-r--r-- | libpod/networking_slirp4netns.go | 10 | ||||
-rw-r--r-- | libpod/oci_attach_linux.go | 17 | ||||
-rw-r--r-- | libpod/oci_conmon_exec_linux.go | 7 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 13 | ||||
-rw-r--r-- | libpod/pod.go | 2 | ||||
-rw-r--r-- | libpod/pod_api.go | 3 | ||||
-rw-r--r-- | libpod/reset.go | 7 | ||||
-rw-r--r-- | libpod/runtime.go | 3 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 22 | ||||
-rw-r--r-- | libpod/runtime_img.go | 3 | ||||
-rw-r--r-- | libpod/runtime_pod.go | 7 | ||||
-rw-r--r-- | libpod/runtime_pod_linux.go | 6 | ||||
-rw-r--r-- | libpod/runtime_volume.go | 7 | ||||
-rw-r--r-- | libpod/runtime_volume_linux.go | 8 | ||||
-rw-r--r-- | libpod/shutdown/handler.go | 22 |
26 files changed, 239 insertions, 122 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go index 50be0eea4..38223316e 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -184,7 +184,7 @@ func (c *Container) StopWithTimeout(timeout uint) error { return define.ErrCtrStopped } - if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { + if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopping) { return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created or running containers. %s is in state %s", c.ID(), c.state.State.String()) } @@ -690,7 +690,7 @@ func (c *Container) Sync() error { // If runtime knows about the container, update its status in runtime // And then save back to disk - if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped) { + if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped, define.ContainerStateStopping) { oldState := c.state.State if err := c.ociRuntime.UpdateContainerStatus(c); err != nil { return err diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go index 7d4dd0d46..954d54a1d 100644 --- a/libpod/container_copy_linux.go +++ b/libpod/container_copy_linux.go @@ -15,8 +15,8 @@ import ( "github.com/containers/buildah/util" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/idtools" - "github.com/docker/docker/pkg/archive" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1c3de526c..43f5398a2 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -17,12 +17,14 @@ import ( "github.com/containers/buildah/copier" "github.com/containers/buildah/pkg/overlay" butil "github.com/containers/buildah/util" + "github.com/containers/common/pkg/chown" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/ctime" "github.com/containers/podman/v3/pkg/hooks" "github.com/containers/podman/v3/pkg/hooks/exec" + "github.com/containers/podman/v3/pkg/lookup" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/selinux" "github.com/containers/podman/v3/pkg/util" @@ -485,8 +487,12 @@ func (c *Container) setupStorage(ctx context.Context) error { return errors.Wrapf(err, "error creating container storage") } - c.config.IDMappings.UIDMap = containerInfo.UIDMap - c.config.IDMappings.GIDMap = containerInfo.GIDMap + // only reconfig IDMappings if layer was mounted from storage + // if its a external overlay do not reset IDmappings + if !c.config.RootfsOverlay { + c.config.IDMappings.UIDMap = containerInfo.UIDMap + c.config.IDMappings.GIDMap = containerInfo.GIDMap + } processLabel, err := c.processLabel(containerInfo.ProcessLabel) if err != nil { @@ -1515,6 +1521,19 @@ func (c *Container) mountStorage() (_ string, deferredErr error) { } mountPoint = overlayMount.Source + execUser, err := lookup.GetUserGroupInfo(mountPoint, c.config.User, nil) + if err != nil { + return "", err + } + hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), uint32(execUser.Uid), uint32(execUser.Gid)) + if err != nil { + return "", errors.Wrap(err, "unable to get host UID and host GID") + } + + //note: this should not be recursive, if using external rootfs users should be responsible on configuring ownership. + if err := chown.ChangeHostPathOwnership(mountPoint, false, int(hostUID), int(hostGID)); err != nil { + return "", err + } } if mountPoint == "" { @@ -1690,9 +1709,23 @@ func (c *Container) cleanupStorage() error { var cleanupErr error + markUnmounted := func() { + c.state.Mountpoint = "" + c.state.Mounted = false + + if c.valid { + if err := c.save(); err != nil { + if cleanupErr != nil { + logrus.Errorf("Unmounting container %s: %v", c.ID(), cleanupErr) + } + cleanupErr = err + } + } + } + // umount rootfs overlay if it was created if c.config.RootfsOverlay { - overlayBasePath := c.runtime.store.GraphRoot() + overlayBasePath := filepath.Dir(c.config.StaticDir) overlayBasePath = filepath.Join(overlayBasePath, "rootfs") if err := overlay.Unmount(overlayBasePath); err != nil { // If the container can't remove content report the error @@ -1717,6 +1750,7 @@ func (c *Container) cleanupStorage() error { } if c.config.Rootfs != "" { + markUnmounted() return cleanupErr } @@ -1761,17 +1795,7 @@ func (c *Container) cleanupStorage() error { } } - c.state.Mountpoint = "" - c.state.Mounted = false - - if c.valid { - if err := c.save(); err != nil { - if cleanupErr != nil { - logrus.Errorf("Unmounting container %s: %v", c.ID(), cleanupErr) - } - cleanupErr = err - } - } + markUnmounted() return cleanupErr } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 867ecc2ad..27cc318b4 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -50,6 +50,7 @@ import ( runcuser "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" + "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -390,11 +391,11 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { for _, o := range namedVol.Options { switch o { case "U": - if err := chown.ChangeHostPathOwnership(mountPoint, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(mountPoint, true, int(hostUID), int(hostGID)); err != nil { return nil, err } - if err := chown.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { return nil, err } } @@ -423,14 +424,15 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { if m.Type == "tmpfs" { options = append(options, []string{fmt.Sprintf("uid=%d", execUser.Uid), fmt.Sprintf("gid=%d", execUser.Gid)}...) } else { - if err := chown.ChangeHostPathOwnership(m.Source, true, int(hostUID), int(hostGID)); err != nil { + // only chown on initial creation of container + if err := c.ChangeHostPathOwnership(m.Source, true, int(hostUID), int(hostGID)); err != nil { return nil, err } } case "z": fallthrough case "Z": - if err := label.Relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil { + if err := c.relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil { return nil, err } @@ -477,11 +479,11 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { for _, o := range overlayVol.Options { switch o { case "U": - if err := chown.ChangeHostPathOwnership(overlayVol.Source, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(overlayVol.Source, true, int(hostUID), int(hostGID)); err != nil { return nil, err } - if err := chown.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { + if err := c.ChangeHostPathOwnership(contentDir, true, int(hostUID), int(hostGID)); err != nil { return nil, err } } @@ -1008,12 +1010,15 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error { includeFiles := []string{ "artifacts", - "ctr.log", metadata.ConfigDumpFile, metadata.SpecDumpFile, metadata.NetworkStatusFile, } + if c.LogDriver() == define.KubernetesLogging || + c.LogDriver() == define.JSONLogging { + includeFiles = append(includeFiles, "ctr.log") + } if options.PreCheckPoint { includeFiles = append(includeFiles, preCheckpointDir) } else { @@ -1706,13 +1711,13 @@ func (c *Container) makeBindMounts() error { } if c.state.BindMounts["/etc/hosts"] != "" { - if err := label.Relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil { + if err := c.relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil { return err } } if c.state.BindMounts["/etc/resolv.conf"] != "" { - if err := label.Relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil { + if err := c.relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil { return err } } @@ -1994,7 +1999,7 @@ func (c *Container) generateResolvConf() (string, error) { } // Relabel resolv.conf for the container - if err := label.Relabel(destPath, c.config.MountLabel, true); err != nil { + if err := c.relabel(destPath, c.config.MountLabel, true); err != nil { return "", err } @@ -2016,7 +2021,7 @@ func (c *Container) generateHosts(path string) (string, error) { } // based on networking mode we may want to append the localhost -// if there isn't any record for it and also this shoud happen +// if there isn't any record for it and also this should happen // in slirp4netns and similar network modes. func (c *Container) appendLocalhost(hosts string) string { if !strings.Contains(hosts, "localhost") && @@ -2611,7 +2616,7 @@ func (c *Container) copyTimezoneFile(zonePath string) (string, error) { if err != nil { return "", err } - if err := label.Relabel(localtimeCopy, c.config.MountLabel, false); err != nil { + if err := c.relabel(localtimeCopy, c.config.MountLabel, false); err != nil { return "", err } if err := dest.Chown(c.RootUID(), c.RootGID()); err != nil { @@ -2746,3 +2751,37 @@ func (c *Container) fixVolumePermissions(v *ContainerNamedVolume) error { } return nil } + +func (c *Container) relabel(src, mountLabel string, recurse bool) error { + if !selinux.GetEnabled() || mountLabel == "" { + return nil + } + // only relabel on initial creation of container + if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateUnknown) { + label, err := label.FileLabel(src) + if err != nil { + return err + } + // If labels are different, might be on a tmpfs + if label == mountLabel { + return nil + } + } + return label.Relabel(src, mountLabel, recurse) +} + +func (c *Container) ChangeHostPathOwnership(src string, recurse bool, uid, gid int) error { + // only chown on initial creation of container + if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateUnknown) { + st, err := os.Stat(src) + if err != nil { + return err + } + + // If labels are different, might be on a tmpfs + if int(st.Sys().(*syscall.Stat_t).Uid) == uid && int(st.Sys().(*syscall.Stat_t).Gid) == gid { + return nil + } + } + return chown.ChangeHostPathOwnership(src, recurse, uid, gid) +} diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index ca1e11ef5..562169ce2 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -91,8 +91,12 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption var cursorError error for i := 1; i <= 3; i++ { cursor, cursorError = journal.GetCursor() + hundreds := 1 + for j := 1; j < i; j++ { + hundreds *= 2 + } if cursorError != nil { - time.Sleep(time.Duration(i*100) * time.Millisecond) + time.Sleep(time.Duration(hundreds*100) * time.Millisecond) continue } break diff --git a/libpod/kube.go b/libpod/kube.go index bf86a9d16..d47f47f1c 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -25,6 +25,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" ) // GenerateForKube takes a slice of libpod containers and generates @@ -196,10 +197,11 @@ func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.Servic for _, cp := range containerPorts { nodePort := 30000 + rand.Intn(32767-30000+1) servicePort := v1.ServicePort{ - Protocol: cp.Protocol, - Port: cp.ContainerPort, - NodePort: int32(nodePort), - Name: strconv.Itoa(int(cp.ContainerPort)), + Protocol: cp.Protocol, + Port: cp.ContainerPort, + NodePort: int32(nodePort), + Name: strconv.Itoa(int(cp.ContainerPort)), + TargetPort: intstr.Parse(strconv.Itoa(int(cp.ContainerPort))), } sps = append(sps, servicePort) } @@ -246,7 +248,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po return nil, err } for k, v := range annotations { - podAnnotations[define.BindMountPrefix+k] = v + podAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) } // Since port bindings for the pod are handled by the // infra container, wipe them here. @@ -366,7 +368,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, return nil, err } for k, v := range annotations { - kubeAnnotations[define.BindMountPrefix+k] = v + kubeAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) } if isInit { kubeInitCtrs = append(kubeInitCtrs, kubeCtr) @@ -477,14 +479,20 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] if err != nil { return kubeContainer, kubeVolumes, nil, annotations, err } - imgData, err := img.Inspect(ctx, false) + imgData, err := img.Inspect(ctx, nil) if err != nil { return kubeContainer, kubeVolumes, nil, annotations, err } - if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) { + // If the user doesn't set a command/entrypoint when creating the container with podman and + // is using the image command or entrypoint from the image, don't add it to the generated kube yaml + if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) || reflect.DeepEqual(imgData.Config.Entrypoint, kubeContainer.Command) { kubeContainer.Command = nil } + if imgData.User == c.User() { + kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil + } + kubeContainer.WorkingDir = c.WorkingDir() kubeContainer.Ports = ports // This should not be applicable @@ -572,7 +580,8 @@ func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([ var protocol v1.Protocol switch strings.ToUpper(p.Protocol) { case "TCP": - protocol = v1.ProtocolTCP + // do nothing as it is the default protocol in k8s, there is no need to explicitly + // add it to the generated yaml case "UDP": protocol = v1.ProtocolUDP default: diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go index 93d871767..01e149114 100644 --- a/libpod/network/cni/cni_conversion.go +++ b/libpod/network/cni/cni_conversion.go @@ -103,7 +103,7 @@ func createNetworkFromCNIConfigList(conf *libcni.NetworkConfigList, confPath str } default: - // A warning would be good but users would get this warning everytime so keep this at info level. + // A warning would be good but users would get this warning every time so keep this at info level. logrus.Infof("Unsupported CNI config type %s in %s, this network can still be used but inspect or list cannot show all information", firstPlugin.Network.Type, confPath) } diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go index fbf917c2d..87beceff3 100644 --- a/libpod/network/cni/cni_types.go +++ b/libpod/network/cni/cni_types.go @@ -182,7 +182,7 @@ func newIPAMLocalHostRange(subnet types.IPNet, leaseRange *types.LeaseRange, gw hostRange.RangeStart = leaseRange.StartIP.String() } if leaseRange.EndIP != nil { - hostRange.RangeStart = leaseRange.EndIP.String() + hostRange.RangeEnd = leaseRange.EndIP.String() } } diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go index 288cf4626..0dfc6173c 100644 --- a/libpod/network/cni/config_test.go +++ b/libpod/network/cni/config_test.go @@ -621,7 +621,7 @@ var _ = Describe("Config", func() { err = libpodNet.NetworkRemove(network1.Name) Expect(err).To(BeNil()) - endIP := "10.0.0.10" + endIP := "10.0.0.30" network = types.Network{ Driver: "bridge", Subnets: []types.Subnet{ @@ -665,6 +665,22 @@ var _ = Describe("Config", func() { Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) + + // create a new interface to force a config load from disk + libpodNet, err = getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + + network1, err = libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) + Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) + Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) }) It("create bridge with subnet and invalid lease range", func() { @@ -1020,28 +1036,6 @@ var _ = Describe("Config", func() { Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config")) }) - - It("remove network should not error when config file does not exists on disk", func() { - name := "mynet" - network := types.Network{Name: name} - _, err := libpodNet.NetworkCreate(network) - Expect(err).To(BeNil()) - - path := filepath.Join(cniConfDir, name+".conflist") - Expect(path).To(BeARegularFile()) - - err = os.Remove(path) - Expect(err).To(BeNil()) - Expect(path).ToNot(BeARegularFile()) - - err = libpodNet.NetworkRemove(name) - Expect(err).To(BeNil()) - - nets, err := libpodNet.NetworkList() - Expect(err).To(BeNil()) - Expect(nets).To(HaveLen(1)) - Expect(nets).ToNot(ContainElement(HaveNetworkName(name))) - }) }) Context("network load valid existing ones", func() { @@ -1335,7 +1329,7 @@ var _ = Describe("Config", func() { Expect(networks).To(HaveLen(0)) }) - It("crate bridge network with used interface name", func() { + It("create bridge network with used interface name", func() { network := types.Network{ NetworkInterface: "cni-podman9", } diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go index 02801641e..a37a84373 100644 --- a/libpod/network/cni/network.go +++ b/libpod/network/cni/network.go @@ -10,6 +10,7 @@ import ( "net" "os" "strings" + "time" "github.com/containernetworking/cni/libcni" "github.com/containers/podman/v3/libpod/define" @@ -40,6 +41,9 @@ type cniNetwork struct { // lock is a internal lock for critical operations lock lockfile.Locker + // modTime is the timestamp when the config dir was modified + modTime time.Time + // networks is a map with loaded networks, the key is the network name networks map[string]*network } @@ -113,10 +117,22 @@ func (n *cniNetwork) Drivers() []string { } func (n *cniNetwork) loadNetworks() error { - // skip loading networks if they are already loaded - if n.networks != nil { + // check the mod time of the config dir + f, err := os.Stat(n.cniConfigDir) + if err != nil { + return err + } + modTime := f.ModTime() + + // skip loading networks if they are already loaded and + // if the config dir was not modified since the last call + if n.networks != nil && modTime.Equal(n.modTime) { return nil } + // make sure the remove all networks before we reload them + n.networks = nil + n.modTime = modTime + // FIXME: do we have to support other file types as well, e.g. .conf? files, err := libcni.ConfFiles(n.cniConfigDir, []string{".conflist"}) if err != nil { @@ -153,7 +169,7 @@ func (n *cniNetwork) loadNetworks() error { logrus.Errorf("CNI config list %s could not be converted to a libpod config, skipping: %v", file, err) continue } - logrus.Tracef("Successfully loaded network %s: %v", net.Name, net) + logrus.Debugf("Successfully loaded network %s: %v", net.Name, net) networkInfo := network{ filename: file, cniNet: conf, diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go index 2fe4f3da2..657c1ca6a 100644 --- a/libpod/network/types/network.go +++ b/libpod/network/types/network.go @@ -137,7 +137,7 @@ type NetInterface struct { MacAddress net.HardwareAddr `json:"mac_address"` } -// NetAddress contains the subnet and gatway. +// NetAddress contains the subnet and gateway. type NetAddress struct { // Subnet of this NetAddress. Note that the subnet contains the // actual ip of the net interface and not the network address. diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index 46cda89a9..ffd53ec2b 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -484,10 +484,14 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin } cfgR := bytes.NewReader(cfgJSON) var stdout bytes.Buffer - cmd := exec.Command(fmt.Sprintf("/proc/%d/exe", os.Getpid())) - cmd.Args = []string{rootlessport.ReexecKey} - // Leak one end of the pipe in rootlessport process, the other will be sent to conmon + path, err := r.config.FindHelperBinary(rootlessport.BinaryName, false) + if err != nil { + return err + } + cmd := exec.Command(path) + cmd.Args = []string{rootlessport.BinaryName} + // Leak one end of the pipe in rootlessport process, the other will be sent to conmon if ctr.rootlessPortSyncR != nil { defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR) } diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index d4d4a1076..1f2a28ead 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -93,7 +93,7 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <- if attachRdy != nil { attachRdy <- true } - return readStdio(streams, receiveStdoutError, stdinDone) + return readStdio(conn, streams, receiveStdoutError, stdinDone) } // Attach to the given container's exec session @@ -174,7 +174,7 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se return err } - return readStdio(streams, receiveStdoutError, stdinDone) + return readStdio(conn, streams, receiveStdoutError, stdinDone) } func processDetachKeys(keys string) ([]byte, error) { @@ -217,11 +217,6 @@ func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detac var err error if streams.AttachInput { _, err = utils.CopyDetachable(conn, streams.InputStream, detachKeys) - if err == nil { - if connErr := conn.CloseWrite(); connErr != nil { - logrus.Errorf("Unable to close conn: %q", connErr) - } - } } stdinDone <- err }() @@ -274,7 +269,7 @@ func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeO return err } -func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { +func readStdio(conn *net.UnixConn, streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { var err error select { case err = <-receiveStdoutError: @@ -283,6 +278,12 @@ func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan if err == define.ErrDetach { return err } + if err == nil { + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } + } if streams.AttachOutput || streams.AttachError { return <-receiveStdoutError } diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index 822377bfe..654306f92 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -609,9 +609,6 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp _, err := utils.CopyDetachable(conn, httpBuf, detachKeys) logrus.Debugf("STDIN copy completed") stdinChan <- err - if connErr := conn.CloseWrite(); connErr != nil { - logrus.Errorf("Unable to close conn: %v", connErr) - } }() } @@ -654,6 +651,10 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp if err != nil { return err } + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } case <-cancel: return nil } diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 71a7b29fa..1719b2dfa 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -351,6 +351,12 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { return ctr.handleExitFile(exitFile, fi) } + // Handle ContainerStateStopping - keep it unless the container + // transitioned to no longer running. + if oldState == define.ContainerStateStopping && (ctr.state.State == define.ContainerStatePaused || ctr.state.State == define.ContainerStateRunning) { + ctr.state.State = define.ContainerStateStopping + } + return nil } @@ -435,7 +441,8 @@ func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool) } if err := waitContainerStop(ctr, time.Duration(timeout)*time.Second); err != nil { - logrus.Infof("Timed out stopping container %s, resorting to SIGKILL: %v", ctr.ID(), err) + logrus.Debugf("Timed out stopping container %s with %s, resorting to SIGKILL: %v", ctr.ID(), unix.SignalName(syscall.Signal(stopSignal)), err) + logrus.Warnf("StopSignal %s failed to stop container %s in %d seconds, resorting to SIGKILL", unix.SignalName(syscall.Signal(stopSignal)), ctr.Name(), timeout) } else { // No error, the container is dead return nil @@ -701,6 +708,10 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. if err != nil { return err } + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } case <-cancel: return nil } diff --git a/libpod/pod.go b/libpod/pod.go index 068a835f6..0e5ac4906 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -390,7 +390,7 @@ func (p *Pod) InfraContainerID() (string, error) { return p.infraContainerID() } -// infraContainer is the unlocked versio of InfraContainer which returns the infra container +// infraContainer is the unlocked version of InfraContainer which returns the infra container func (p *Pod) infraContainer() (*Container, error) { id, err := p.infraContainerID() if err != nil { diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 4ae02fb40..feb8ff250 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -37,7 +37,8 @@ func (p *Pod) startInitContainers(ctx context.Context) error { if initCon.config.InitContainerType == define.OneShotInitContainer { icLock := initCon.lock icLock.Lock() - if err := p.runtime.removeContainer(ctx, initCon, false, false, true); err != nil { + var time *uint + if err := p.runtime.removeContainer(ctx, initCon, false, false, true, time); err != nil { icLock.Unlock() return errors.Wrapf(err, "failed to remove once init container %s", initCon.ID()) } diff --git a/libpod/reset.go b/libpod/reset.go index 7b25ed680..5d9bb0e90 100644 --- a/libpod/reset.go +++ b/libpod/reset.go @@ -18,12 +18,13 @@ import ( // Reset removes all storage func (r *Runtime) Reset(ctx context.Context) error { + var timeout *uint pods, err := r.GetAllPods() if err != nil { return err } for _, p := range pods { - if err := r.RemovePod(ctx, p, true, true); err != nil { + if err := r.RemovePod(ctx, p, true, true, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchPod { continue } @@ -37,7 +38,7 @@ func (r *Runtime) Reset(ctx context.Context) error { } for _, c := range ctrs { - if err := r.RemoveContainer(ctx, c, true, true); err != nil { + if err := r.RemoveContainer(ctx, c, true, true, timeout); err != nil { if err := r.RemoveStorageContainer(c.ID(), true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { continue @@ -61,7 +62,7 @@ func (r *Runtime) Reset(ctx context.Context) error { return err } for _, v := range volumes { - if err := r.RemoveVolume(ctx, v, true); err != nil { + if err := r.RemoveVolume(ctx, v, true, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchVolume { continue } diff --git a/libpod/runtime.go b/libpod/runtime.go index 27885bf5c..855f3a9f9 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -489,8 +489,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { DefaultNetwork: runtime.config.Network.DefaultNetwork, DefaultSubnet: runtime.config.Network.DefaultSubnet, IsMachine: runtime.config.Engine.MachineEnabled, - // TODO use cni.lock - LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni1.lock"), + LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni.lock"), }) if err != nil { return errors.Wrapf(err, "could not create network interface") diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 00979a500..2256ba57c 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -535,10 +535,10 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai // If removeVolume is specified, named volumes used by the container will // be removed also if and only if the container is the sole user // Otherwise, RemoveContainer will return an error if the container is running -func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool) error { +func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool, timeout *uint) error { r.lock.Lock() defer r.lock.Unlock() - return r.removeContainer(ctx, c, force, removeVolume, false) + return r.removeContainer(ctx, c, force, removeVolume, false, timeout) } // Internal function to remove a container. @@ -546,7 +546,7 @@ func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, // removePod is used only when removing pods. It instructs Podman to ignore // infra container protections, and *not* remove from the database (as pod // remove will handle that). -func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool) error { +func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool, timeout *uint) error { if !c.valid { if ok, _ := r.state.HasContainer(c.ID()); !ok { // Container probably already removed @@ -642,9 +642,13 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo // Check that the container's in a good state to be removed. if c.state.State == define.ContainerStateRunning { + time := c.StopTimeout() + if timeout != nil { + time = *timeout + } // 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 { + if err := c.stop(time); err != nil && errors.Cause(err) != define.ErrConmonDead { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } @@ -751,7 +755,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo if !volume.Anonymous() { continue } - if err := runtime.removeVolume(ctx, volume, false); err != nil && errors.Cause(err) != define.ErrNoSuchVolume { + if err := runtime.removeVolume(ctx, volume, false, timeout); err != nil && errors.Cause(err) != define.ErrNoSuchVolume { logrus.Errorf("Cleanup volume (%s): %v", v, err) } } @@ -782,6 +786,7 @@ func (r *Runtime) EvictContainer(ctx context.Context, idOrName string, removeVol // remove will handle that). func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVolume bool) (string, error) { var err error + var timeout *uint if !r.valid { return "", define.ErrRuntimeStopped @@ -797,7 +802,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol if err == nil { logrus.Infof("Container %s successfully retrieved from state, attempting normal removal", id) // Assume force = true for the evict case - err = r.removeContainer(ctx, tmpCtr, true, removeVolume, false) + err = r.removeContainer(ctx, tmpCtr, true, removeVolume, false, timeout) if !tmpCtr.valid { // If the container is marked invalid, remove succeeded // in kicking it out of the state - no need to continue. @@ -892,7 +897,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol if !volume.Anonymous() { continue } - if err := r.removeVolume(ctx, volume, false); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed { + if err := r.removeVolume(ctx, volume, false, timeout); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed { logrus.Errorf("Cleanup volume (%s): %v", v, err) } } @@ -1089,7 +1094,8 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) ([]*reports.Pru preports = append(preports, report) continue } - err = r.RemoveContainer(context.Background(), c, false, false) + var time *uint + err = r.RemoveContainer(context.Background(), c, false, false, time) if err != nil { report.Err = err } else { diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 1915a5c4d..52ac0d4d7 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -37,7 +37,8 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage } for _, ctr := range ctrs { if ctr.config.RootfsImageID == imageID { - if err := r.removeContainer(ctx, ctr, true, false, false); err != nil { + var timeout *uint + if err := r.removeContainer(ctx, ctr, true, false, false, timeout); err != nil { return errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", imageID, ctr.ID()) } } diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go index b142472e8..2389ee6d9 100644 --- a/libpod/runtime_pod.go +++ b/libpod/runtime_pod.go @@ -26,7 +26,7 @@ type PodFilter func(*Pod) bool // If force is specified with removeCtrs, all containers will be stopped before // being removed // Otherwise, the pod will not be removed if any containers are running -func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { +func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) error { r.lock.Lock() defer r.lock.Unlock() @@ -45,7 +45,7 @@ func (r *Runtime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) p.lock.Lock() defer p.lock.Unlock() - return r.removePod(ctx, p, removeCtrs, force) + return r.removePod(ctx, p, removeCtrs, force, timeout) } // GetPod retrieves a pod by its ID @@ -196,7 +196,8 @@ func (r *Runtime) PrunePods(ctx context.Context) (map[string]error, error) { return response, nil } for _, pod := range pods { - err := r.removePod(context.TODO(), pod, true, false) + var timeout *uint + err := r.removePod(context.TODO(), pod, true, false, timeout) response[pod.ID()] = err } return response, nil diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index b0279c453..9bd6d5e78 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -168,7 +168,7 @@ func (r *Runtime) SavePod(pod *Pod) error { return nil } -func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { +func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) error { if err := p.updatePod(); err != nil { return err } @@ -254,7 +254,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) ctrNamedVolumes[vol.Name] = vol } - if err := r.removeContainer(ctx, ctr, force, false, true); err != nil { + if err := r.removeContainer(ctx, ctr, force, false, true, timeout); err != nil { if removalErr == nil { removalErr = err } else { @@ -280,7 +280,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) if !volume.Anonymous() { continue } - if err := r.removeVolume(ctx, volume, false); err != nil { + if err := r.removeVolume(ctx, volume, false, timeout); err != nil { if errors.Cause(err) == define.ErrNoSuchVolume || errors.Cause(err) == define.ErrVolumeRemoved { continue } diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go index 5f8f9ca1e..2b3ad10b4 100644 --- a/libpod/runtime_volume.go +++ b/libpod/runtime_volume.go @@ -21,7 +21,7 @@ type VolumeCreateOption func(*Volume) error type VolumeFilter func(*Volume) bool // RemoveVolume removes a volumes -func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error { +func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool, timeout *uint) error { r.lock.Lock() defer r.lock.Unlock() @@ -36,7 +36,7 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error return nil } } - return r.removeVolume(ctx, v, force) + return r.removeVolume(ctx, v, force, timeout) } // GetVolume retrieves a volume given its full name. @@ -149,7 +149,8 @@ func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) } report.Size = volSize report.Id = vol.Name() - if err := r.RemoveVolume(ctx, vol, false); err != nil { + var timeout *uint + if err := r.RemoveVolume(ctx, vol, false, timeout); err != nil { if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved { report.Err = err } else { diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index def6ca411..ed3cc971c 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -189,7 +189,7 @@ func makeVolumeInPluginIfNotExist(name string, options map[string]string, plugin } // removeVolume removes the specified volume from state as well tears down its mountpoint and storage -func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error { +func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeout *uint) error { if !v.valid { if ok, _ := r.state.HasVolume(v.Name()); !ok { return nil @@ -230,11 +230,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error logrus.Debugf("Removing container %s (depends on volume %q)", ctr.ID(), v.Name()) - // TODO: do we want to set force here when removing - // containers? - // I'm inclined to say no, in case someone accidentally - // wipes a container they're using... - if err := r.removeContainer(ctx, ctr, false, false, false); err != nil { + if err := r.removeContainer(ctx, ctr, force, false, false, timeout); err != nil { return errors.Wrapf(err, "error removing container %s that depends on volume %s", ctr.ID(), v.Name()) } } diff --git a/libpod/shutdown/handler.go b/libpod/shutdown/handler.go index cca74c3c4..9add05c9c 100644 --- a/libpod/shutdown/handler.go +++ b/libpod/shutdown/handler.go @@ -5,9 +5,10 @@ import ( "os/signal" "sync" "syscall" + "time" "github.com/pkg/errors" - "github.com/sirupsen/logrus" + logrusImport "github.com/sirupsen/logrus" ) var ( @@ -25,6 +26,7 @@ var ( // Ordering that on-shutdown handlers will be invoked. handlerOrder []string shutdownInhibit sync.RWMutex + logrus = logrusImport.WithField("PID", os.Getpid()) ) // Start begins handling SIGTERM and SIGINT and will run the given on-signal @@ -44,25 +46,31 @@ func Start() error { go func() { select { case <-cancelChan: + logrus.Infof("Received shutdown.Stop(), terminating!") signal.Stop(sigChan) close(sigChan) close(cancelChan) stopped = true return case sig := <-sigChan: - logrus.Infof("Received shutdown signal %v, terminating!", sig) + logrus.Infof("Received shutdown signal %q, terminating!", sig.String()) shutdownInhibit.Lock() handlerLock.Lock() + for _, name := range handlerOrder { handler, ok := handlers[name] if !ok { - logrus.Errorf("Shutdown handler %s definition not found!", name) + logrus.Errorf("Shutdown handler %q definition not found!", name) continue } - logrus.Infof("Invoking shutdown handler %s", name) + + logrus.Infof("Invoking shutdown handler %q", name) + start := time.Now() if err := handler(sig); err != nil { - logrus.Errorf("Running shutdown handler %s: %v", name, err) + logrus.Errorf("Running shutdown handler %q: %v", name, err) } + logrus.Debugf("Completed shutdown handler %q, duration %v", name, + time.Since(start).Round(time.Second)) } handlerLock.Unlock() shutdownInhibit.Unlock() @@ -87,12 +95,12 @@ func Stop() error { return nil } -// Temporarily inhibit signals from shutting down Libpod. +// Inhibit temporarily inhibit signals from shutting down Libpod. func Inhibit() { shutdownInhibit.RLock() } -// Stop inhibiting signals from shutting down Libpod. +// Uninhibit stop inhibiting signals from shutting down Libpod. func Uninhibit() { shutdownInhibit.RUnlock() } |