diff options
32 files changed, 379 insertions, 143 deletions
@@ -45,7 +45,7 @@ endif ifeq (,$(findstring systemd,$(BUILDTAGS))) $(warning \ Podman is being compiled without the systemd build tag.\ - Install libsystemd for journald support) + Install libsystemd on Ubuntu or systemd-devel on rpm based distro for journald support) endif BUILDTAGS_CROSS ?= containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay @@ -73,11 +73,12 @@ endif LIBPOD := ${PROJECT}/libpod GCFLAGS ?= all=-trimpath=${PWD} ASMFLAGS ?= all=-trimpath=${PWD} -LDFLAGS_PODMAN ?= $(LDFLAGS) \ +LDFLAGS_PODMAN ?= \ -X $(LIBPOD)/define.gitCommit=$(GIT_COMMIT) \ -X $(LIBPOD)/define.buildInfo=$(BUILD_INFO) \ -X $(LIBPOD)/config._installPrefix=$(PREFIX) \ - -X $(LIBPOD)/config._etcDir=$(ETCDIR) + -X $(LIBPOD)/config._etcDir=$(ETCDIR) \ + -extldflags "$(LDFLAGS)" #Update to LIBSECCOMP_COMMIT should reflect in Dockerfile too. LIBSECCOMP_COMMIT := release-2.3 # Rarely if ever should integration tests take more than 50min, @@ -158,10 +159,10 @@ gofmt: ## Verify the source code gofmt git diff --exit-code test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go) - $(GO_BUILD) -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/checkseccomp + $(GO_BUILD) -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/checkseccomp test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go) - $(GO_BUILD) -ldflags '$(LDFLAGS)' -o $@ $(PROJECT)/test/goecho + $(GO_BUILD) -ldflags '$(LDFLAGS_PODMAN)' -o $@ $(PROJECT)/test/goecho podman: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman $(GO_BUILD) $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman @@ -240,6 +241,7 @@ testunit: libpodimage ## Run unittest on the built image localunit: test/goecho/goecho varlink_generate ginkgo \ -r \ + $(TESTFLAGS) \ --skipPackage test/e2e,pkg/apparmor,test/endpoint \ --cover \ --covermode atomic \ @@ -247,13 +249,13 @@ localunit: test/goecho/goecho varlink_generate --succinct ginkgo: - ginkgo -v -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -nodes 3 -debug test/e2e/. + ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -nodes 3 -debug test/e2e/. ginkgo-remote: - ginkgo -v -tags "$(BUILDTAGS) remoteclient" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/. + ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS) remoteclient" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/. endpoint: - ginkgo -v -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -debug test/endpoint/. + ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -debug test/endpoint/. localintegration: varlink_generate test-binaries ginkgo diff --git a/contrib/build_rpm.sh b/contrib/build_rpm.sh index e41763fa7..088d8b7a5 100755 --- a/contrib/build_rpm.sh +++ b/contrib/build_rpm.sh @@ -26,6 +26,7 @@ declare -a PKGS=(device-mapper-devel \ make \ rpm-build \ go-compilers-golang-compiler \ + systemd-devel \ ) if [[ $pkg_manager == *dnf ]]; then diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 2b9621dbc..9676a3fb4 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -22,6 +22,9 @@ %define gobuild(o:) go build -tags="$BUILDTAGS" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n')" -a -v -x %{?**}; #% endif +# libpod hack directory +%define hackdir %{_builddir}/%{repo}-%{shortcommit0} + %global provider github %global provider_tld com %global project containers @@ -384,7 +387,7 @@ ln -s ../../../../ src/%{import_path} popd ln -s vendor src export GOPATH=$(pwd)/_build:$(pwd):$(pwd):%{gopath} -export BUILDTAGS="varlink selinux seccomp $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh) $(hack/libdm_tag.sh) exclude_graphdriver_devicemapper" +export BUILDTAGS="varlink selinux seccomp $(%{hackdir}/hack/btrfs_installed_tag.sh) $(%{hackdir}/hack/btrfs_tag.sh) $(%{hackdir}/hack/libdm_tag.sh) exclude_graphdriver_devicemapper" GOPATH=$GOPATH go generate ./cmd/podman/varlink/... @@ -402,7 +405,7 @@ mkdir -p src/%{provider}.%{provider_tld}/{containers,opencontainers} ln -s $(dirs +1 -l) src/%{import_path_conmon} popd -export BUILDTAGS="selinux seccomp $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh)" +export BUILDTAGS="selinux seccomp $(%{hackdir}/hack/btrfs_installed_tag.sh) $(%{hackdir}/hack/btrfs_tag.sh)" BUILDTAGS=$BUILDTAGS make popd diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 6c8f239a6..fac8296ad 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -176,6 +176,10 @@ value can be entered. The password is entered without echo. Add a host device to the container. The format is `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +Note: if the user only has access rights via a group then accessing the device +from inside a rootless container will fail. The `crun` runtime offers a +workaround for this by adding the option `--annotation io.crun.keep_original_groups=1`. + **--disable-compression, -D** Don't compress filesystem layers when building the image unless it is required diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 82d2e8f6a..85aa81553 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -204,6 +204,10 @@ Specify the key sequence for detaching a container. Format is a single character Add a host device to the container. The format is `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +Note: if the user only has access rights via a group then accessing the device +from inside a rootless container will fail. The `crun` runtime offers a +workaround for this by adding the option `--annotation io.crun.keep_original_groups=1`. + **--device-read-bps**=*path* Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb) diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md index 9721755ef..b539f1d3c 100644 --- a/docs/source/markdown/podman-info.1.md +++ b/docs/source/markdown/podman-info.1.md @@ -53,13 +53,26 @@ host: os: linux uptime: 218h 49m 33.66s (Approximately 9.08 days) registries: - blocked: null - insecure: null - search: - - quay.io - - registry.fedoraproject.org - - docker.io - - registry.redhat.io + docker.io: + Blocked: true + Insecure: true + Location: docker.io + MirrorByDigestOnly: false + Mirrors: + - Insecure: true + Location: example2.io/example/ubi8-minimal + Prefix: docker.io + redhat.com: + Blocked: false + Insecure: false + Location: registry.access.redhat.com/ubi8 + MirrorByDigestOnly: true + Mirrors: + - Insecure: false + Location: example.io/example/ubi8-minimal + - Insecure: true + Location: example3.io/example/ubi8-minimal + Prefix: redhat.com store: ConfigFile: /etc/containers/storage.conf ContainerStore: diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index e1177cb34..e8744de35 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -210,6 +210,10 @@ Specify the key sequence for detaching a container. Format is a single character Add a host device to the container. The format is `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +Note: if the user only has access rights via a group then accessing the device +from inside a rootless container will fail. The `crun` runtime offers a +workaround for this by adding the option `--annotation io.crun.keep_original_groups=1`. + **--device-read-bps**=*path* Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb) @@ -59,7 +59,7 @@ require ( github.com/stretchr/testify v1.4.0 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/uber-go/atomic v1.4.0 // indirect - github.com/uber/jaeger-client-go v2.20.0+incompatible + github.com/uber/jaeger-client-go v2.20.1+incompatible github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5 // indirect github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b github.com/vishvananda/netlink v1.0.0 @@ -466,6 +466,8 @@ github.com/uber/jaeger-client-go v2.19.0+incompatible h1:pbwbYfHUoaase0oPQOdZ1Gc github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.20.0+incompatible h1:ttG9wKdl2ikV/BGOtu+eb+VPp+R7jMeuM177Ihs5Fdc= github.com/uber/jaeger-client-go v2.20.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-client-go v2.20.1+incompatible h1:HgqpYBng0n7tLJIlyT4kPCIv5XgCsF+kai1NnnrJzEU= +github.com/uber/jaeger-client-go v2.20.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5 h1:CwmGyzHTzCqCdZJkWR0A7ucZXgrCY7spRcpvm7ci//s= github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= diff --git a/hack/systemd_tag.sh b/hack/systemd_tag.sh index c59cad559..19a7bf6a6 100755 --- a/hack/systemd_tag.sh +++ b/hack/systemd_tag.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash -if pkg-config --exists libsystemd; then - echo systemd +cc -E - > /dev/null 2> /dev/null << EOF +#include <systemd/sd-daemon.h> +EOF +if test $? -eq 0 ; then + echo systemd fi diff --git a/install.md b/install.md index 218994587..5771fff45 100644 --- a/install.md +++ b/install.md @@ -149,6 +149,12 @@ sudo apt-get install \ uidmap ``` +On openSUSE Leap 15.x and Tumbleweed: + +```bash +sudo zypper -n in libseccomp-devel libgpgme-devel +``` + On Manjaro (and maybe other Linux distributions): Make sure that the Linux kernel supports user namespaces: diff --git a/libpod/config/config.go b/libpod/config/config.go index 0e867a50e..6240bccb0 100644 --- a/libpod/config/config.go +++ b/libpod/config/config.go @@ -448,20 +448,27 @@ func NewConfig(userConfigPath string) (*Config, error) { if err != nil { return nil, errors.Wrapf(err, "error reading user config %q", userConfigPath) } - if err := cgroupV2Check(userConfigPath, config); err != nil { - return nil, errors.Wrapf(err, "error rewriting configuration file %s", userConfigPath) - } } // Now, check if the user can access system configs and merge them if needed. if configs, err := systemConfigs(); err != nil { return nil, errors.Wrapf(err, "error finding config on system") } else { + migrated := false for _, path := range configs { systemConfig, err := readConfigFromFile(path) if err != nil { return nil, errors.Wrapf(err, "error reading system config %q", path) } + // Handle CGroups v2 configuration migration. + // Migrate only the first config, and do it before + // merging. + if !migrated { + if err := cgroupV2Check(path, systemConfig); err != nil { + return nil, errors.Wrapf(err, "error rewriting configuration file %s", userConfigPath) + } + migrated = true + } // Merge the it into the config. Any unset field in config will be // over-written by the systemConfig. if err := config.mergeConfig(systemConfig); err != nil { @@ -564,6 +571,7 @@ func (c *Config) checkCgroupsAndLogger() { // TODO Once runc has support for cgroups, this function should be removed. func cgroupV2Check(configPath string, tmpConfig *Config) error { if !tmpConfig.CgroupCheck && rootless.IsRootless() { + logrus.Debugf("Rewriting %s for CGroup v2 upgrade", configPath) cgroupsV2, err := cgroups.IsCgroup2UnifiedMode() if err != nil { return err diff --git a/libpod/container_api.go b/libpod/container_api.go index 153a1d628..5168dbc68 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -594,7 +594,12 @@ func (c *Container) Cleanup(ctx context.Context) error { // If we didn't restart, we perform a normal cleanup - // Check if we have active exec sessions + // Reap exec sessions first. + if err := c.reapExecSessions(); err != nil { + return err + } + + // Check if we have active exec sessions after reaping. if len(c.state.ExecSessions) != 0 { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has active exec sessions, refusing to clean up", c.ID()) } diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1e8a8a580..37801162a 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1749,6 +1749,11 @@ func (c *Container) checkReadyForRemoval() error { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed without force", c.ID(), c.state.State.String()) } + // Reap exec sessions + if err := c.reapExecSessions(); err != nil { + return err + } + if len(c.state.ExecSessions) != 0 { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it has active exec sessions", c.ID()) } @@ -1855,3 +1860,38 @@ func (c *Container) checkExitFile() error { // Read the exit file to get our stopped time and exit code. return c.handleExitFile(exitFile, info) } + +// Reap dead exec sessions +func (c *Container) reapExecSessions() error { + // Instead of saving once per iteration, use a defer to do it once at + // the end. + var lastErr error + needSave := false + for id := range c.state.ExecSessions { + alive, err := c.ociRuntime.ExecUpdateStatus(c, id) + if err != nil { + if lastErr != nil { + logrus.Errorf("Error reaping exec sessions for container %s: %v", c.ID(), lastErr) + } + lastErr = err + continue + } + if !alive { + // Clean up lingering files and remove the exec session + if err := c.ociRuntime.ExecContainerCleanup(c, id); err != nil { + return errors.Wrapf(err, "error cleaning up container %s exec session %s files", c.ID(), id) + } + delete(c.state.ExecSessions, id) + needSave = true + } + } + if needSave { + if err := c.save(); err != nil { + if lastErr != nil { + logrus.Errorf("Error reaping exec sessions for container %s: %v", c.ID(), lastErr) + } + lastErr = err + } + } + return lastErr +} diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 1b0570998..6ec06943f 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -593,22 +593,68 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error) // Get root file-system changes included in the checkpoint archive rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar") + deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files") if !ignoreRootfs { - rootfsDiffFile, err := os.Create(rootfsDiffPath) - if err != nil { - return errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath) - } - tarStream, err := c.runtime.GetDiffTarStream("", c.ID()) + // To correctly track deleted files, let's go through the output of 'podman diff' + tarFiles, err := c.runtime.GetDiff("", c.ID()) if err != nil { return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath) } - _, err = io.Copy(rootfsDiffFile, tarStream) - if err != nil { - return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath) + var rootfsIncludeFiles []string + var deletedFiles []string + + for _, file := range tarFiles { + if file.Kind == archive.ChangeAdd { + rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path) + continue + } + if file.Kind == archive.ChangeDelete { + deletedFiles = append(deletedFiles, file.Path) + continue + } + fileName, err := os.Stat(file.Path) + if err != nil { + continue + } + if !fileName.IsDir() && file.Kind == archive.ChangeModify { + rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path) + continue + } + } + + if len(rootfsIncludeFiles) > 0 { + rootfsTar, err := archive.TarWithOptions(c.state.Mountpoint, &archive.TarOptions{ + Compression: archive.Uncompressed, + IncludeSourceDir: true, + IncludeFiles: rootfsIncludeFiles, + }) + if err != nil { + return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath) + } + rootfsDiffFile, err := os.Create(rootfsDiffPath) + if err != nil { + return errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath) + } + defer rootfsDiffFile.Close() + _, err = io.Copy(rootfsDiffFile, rootfsTar) + if err != nil { + return err + } + + includeFiles = append(includeFiles, "rootfs-diff.tar") + } + + if len(deletedFiles) > 0 { + formatJSON, err := json.MarshalIndent(deletedFiles, "", " ") + if err != nil { + return errors.Wrapf(err, "error creating delete files list file %q", deleteFilesList) + } + if err := ioutil.WriteFile(deleteFilesList, formatJSON, 0600); err != nil { + return errors.Wrapf(err, "error creating delete files list file %q", deleteFilesList) + } + + includeFiles = append(includeFiles, "deleted.files") } - tarStream.Close() - rootfsDiffFile.Close() - includeFiles = append(includeFiles, "rootfs-diff.tar") } input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{ @@ -637,6 +683,7 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error) } os.Remove(rootfsDiffPath) + os.Remove(deleteFilesList) return nil } @@ -941,10 +988,35 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti if err != nil { return errors.Wrapf(err, "Failed to open root file-system diff file %s", rootfsDiffPath) } + defer rootfsDiffFile.Close() if err := c.runtime.ApplyDiffTarStream(c.ID(), rootfsDiffFile); err != nil { return errors.Wrapf(err, "Failed to apply root file-system diff file %s", rootfsDiffPath) } - rootfsDiffFile.Close() + } + deletedFilesPath := filepath.Join(c.bundlePath(), "deleted.files") + if _, err := os.Stat(deletedFilesPath); err == nil { + deletedFilesFile, err := os.Open(deletedFilesPath) + if err != nil { + return errors.Wrapf(err, "Failed to open deleted files file %s", deletedFilesPath) + } + defer deletedFilesFile.Close() + + var deletedFiles []string + deletedFilesJSON, err := ioutil.ReadAll(deletedFilesFile) + if err != nil { + return errors.Wrapf(err, "Failed to read deleted files file %s", deletedFilesPath) + } + if err := json.Unmarshal(deletedFilesJSON, &deletedFiles); err != nil { + return errors.Wrapf(err, "Failed to read deleted files file %s", deletedFilesPath) + } + for _, deleteFile := range deletedFiles { + // Using RemoveAll as deletedFiles, which is generated from 'podman diff' + // lists completely deleted directories as a single entry: 'D /root'. + err = os.RemoveAll(filepath.Join(c.state.Mountpoint, deleteFile)) + if err != nil { + return errors.Wrapf(err, "Failed to delete file %s from container %s during restore", deletedFilesPath, c.ID()) + } + } } } @@ -965,7 +1037,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti if err != nil { logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err) } - cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar"} + cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar", "deleted.files"} for _, del := range cleanup { file := filepath.Join(c.bundlePath(), del) err = os.Remove(file) diff --git a/libpod/diff.go b/libpod/diff.go index 925bda927..baa4d6ad7 100644 --- a/libpod/diff.go +++ b/libpod/diff.go @@ -1,7 +1,6 @@ package libpod import ( - "archive/tar" "io" "github.com/containers/libpod/libpod/layers" @@ -47,49 +46,6 @@ func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) { return rchanges, err } -// skipFileInTarAchive is an archive.TarModifierFunc function -// which tells archive.ReplaceFileTarWrapper to skip files -// from the tarstream -func skipFileInTarAchive(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) { - return nil, nil, nil -} - -// GetDiffTarStream returns the differences between the two images, layers, or containers. -// It is the same functionality as GetDiff() except that it returns a tarstream -func (r *Runtime) GetDiffTarStream(from, to string) (io.ReadCloser, error) { - toLayer, err := r.getLayerID(to) - if err != nil { - return nil, err - } - fromLayer := "" - if from != "" { - fromLayer, err = r.getLayerID(from) - if err != nil { - return nil, err - } - } - rc, err := r.store.Diff(fromLayer, toLayer, nil) - if err != nil { - return nil, err - } - - // Skip files in the tar archive which are listed - // in containerMounts map. Just as in the GetDiff() - // function from above - filterMap := make(map[string]archive.TarModifierFunc) - for key := range containerMounts { - filterMap[key[1:]] = skipFileInTarAchive - // In the tarstream directories always include a trailing '/'. - // For simplicity this duplicates every entry from - // containerMounts with a trailing '/', as containerMounts - // does not use trailing '/' for directories. - filterMap[key[1:]+"/"] = skipFileInTarAchive - } - - filteredTarStream := archive.ReplaceFileTarWrapper(rc, filterMap) - return filteredTarStream, nil -} - // ApplyDiffTarStream applies the changes stored in 'diff' to the layer 'to' func (r *Runtime) ApplyDiffTarStream(to string, diff io.Reader) error { toLayer, err := r.getLayerID(to) diff --git a/libpod/oci.go b/libpod/oci.go index 9e761788e..05a2f37db 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -23,9 +23,6 @@ type OCIRuntime interface { // CreateContainer creates the container in the OCI runtime. CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error // UpdateContainerStatus updates the status of the given container. - // It includes a switch for whether to perform a hard query of the - // runtime. If unset, the exit file (if supported by the implementation) - // will be used. UpdateContainerStatus(ctr *Container) error // StartContainer starts the given container. StartContainer(ctr *Container) error @@ -59,6 +56,9 @@ type OCIRuntime interface { // If timeout is 0, SIGKILL will be sent immediately, and SIGTERM will // be omitted. ExecStopContainer(ctr *Container, sessionID string, timeout uint) error + // ExecUpdateStatus checks the status of a given exec session. + // Returns true if the session is still running, or false if it exited. + ExecUpdateStatus(ctr *Container, sessionID string) (bool, error) // ExecContainerCleanup cleans up after an exec session exits. // It removes any files left by the exec session that are no longer // needed, including the attach socket. diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 026b13129..37aa71cbb 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -687,6 +687,28 @@ func (r *ConmonOCIRuntime) ExecStopContainer(ctr *Container, sessionID string, t return nil } +// ExecUpdateStatus checks if the given exec session is still running. +func (r *ConmonOCIRuntime) ExecUpdateStatus(ctr *Container, sessionID string) (bool, error) { + session, ok := ctr.state.ExecSessions[sessionID] + if !ok { + // TODO This should probably be a separate error + return false, errors.Wrapf(define.ErrInvalidArg, "no exec session with ID %s found in container %s", sessionID, ctr.ID()) + } + + logrus.Debugf("Checking status of container %s exec session %s", ctr.ID(), sessionID) + + // Is the session dead? + // Ping the PID with signal 0 to see if it still exists. + if err := unix.Kill(session.PID, 0); err != nil { + if err == unix.ESRCH { + return false, nil + } + return false, errors.Wrapf(err, "error pinging container %s exec session %s PID %d with signal 0", ctr.ID(), sessionID, session.PID) + } + + return true, nil +} + // ExecCleanupContainer cleans up files created when a command is run via // ExecContainer. This includes the attach socket for the exec session. func (r *ConmonOCIRuntime) ExecContainerCleanup(ctr *Container, sessionID string) error { diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index d4524cd34..0faa1805b 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -120,6 +120,11 @@ func (r *MissingRuntime) ExecStopContainer(ctr *Container, sessionID string, tim return r.printError() } +// ExecUpdateStatus is not available as the runtime is missing. +func (r *MissingRuntime) ExecUpdateStatus(ctr *Container, sessionID string) (bool, error) { + return false, r.printError() +} + // ExecContainerCleanup is not available as the runtime is missing func (r *MissingRuntime) ExecContainerCleanup(ctr *Container, sessionID string) error { return r.printError() diff --git a/libpod/runtime.go b/libpod/runtime.go index 3873079ce..001d850b0 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -691,24 +691,22 @@ func (r *Runtime) Info() ([]define.InfoData, error) { } info = append(info, define.InfoData{Type: "store", Data: storeInfo}) - reg, err := sysreg.GetRegistries() - if err != nil { - return nil, errors.Wrapf(err, "error getting registries") - } registries := make(map[string]interface{}) - registries["search"] = reg - - ireg, err := sysreg.GetInsecureRegistries() + data, err := sysreg.GetRegistriesData() if err != nil { return nil, errors.Wrapf(err, "error getting registries") } - registries["insecure"] = ireg - - breg, err := sysreg.GetBlockedRegistries() + for _, reg := range data { + registries[reg.Prefix] = reg + } + regs, err := sysreg.GetRegistries() if err != nil { return nil, errors.Wrapf(err, "error getting registries") } - registries["blocked"] = breg + if len(regs) > 0 { + registries["search"] = regs + } + info = append(info, define.InfoData{Type: "registries", Data: registries}) return info, nil } diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/adapter/checkpoint_restore.go index 15f9e8105..7f80b782a 100644 --- a/pkg/adapter/checkpoint_restore.go +++ b/pkg/adapter/checkpoint_restore.go @@ -60,6 +60,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri "ctr.log", "rootfs-diff.tar", "network.status", + "deleted.files", }, } dir, err := ioutil.TempDir("", "checkpoint") diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go index 9643c947f..ba7de7cf9 100644 --- a/pkg/registries/registries.go +++ b/pkg/registries/registries.go @@ -34,7 +34,8 @@ func SystemRegistriesConfPath() string { return "" } -func getRegistries() ([]sysregistriesv2.Registry, error) { +// GetRegistriesData obtains the list of registries +func GetRegistriesData() ([]sysregistriesv2.Registry, error) { registries, err := sysregistriesv2.GetRegistries(&types.SystemContext{SystemRegistriesConfPath: SystemRegistriesConfPath()}) if err != nil { return nil, errors.Wrapf(err, "unable to parse the registries.conf file") @@ -50,7 +51,7 @@ func GetRegistries() ([]string, error) { // GetBlockedRegistries obtains the list of blocked registries defined in the global registries file. func GetBlockedRegistries() ([]string, error) { var blockedRegistries []string - registries, err := getRegistries() + registries, err := GetRegistriesData() if err != nil { return nil, err } @@ -65,7 +66,7 @@ func GetBlockedRegistries() ([]string, error) { // GetInsecureRegistries obtains the list of insecure registries from the global registration file. func GetInsecureRegistries() ([]string, error) { var insecureRegistries []string - registries, err := getRegistries() + registries, err := GetRegistriesData() if err != nil { return nil, err } diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index f208a4cf0..237223283 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -439,6 +439,18 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) + result = podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "rm /etc/motd"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + result = podmanTest.Podman([]string{"diff", "-l"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("C /etc")) + Expect(result.OutputToString()).To(ContainSubstring("A /test.output")) + Expect(result.OutputToString()).To(ContainSubstring("D /etc/motd")) + Expect(len(result.OutputToStringArray())).To(Equal(3)) + // Checkpoint the container result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) result.WaitWithDefaultTimeout() @@ -462,6 +474,14 @@ var _ = Describe("Podman checkpoint", func() { Expect(result.ExitCode()).To(Equal(0)) Expect(result.OutputToString()).To(ContainSubstring("test" + cid + "test")) + result = podmanTest.Podman([]string{"diff", "-l"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("C /etc")) + Expect(result.OutputToString()).To(ContainSubstring("A /test.output")) + Expect(result.OutputToString()).To(ContainSubstring("D /etc/motd")) + Expect(len(result.OutputToStringArray())).To(Equal(3)) + // Remove exported checkpoint os.Remove(fileName) }) diff --git a/test/e2e/e2e.coverprofile b/test/e2e/e2e.coverprofile deleted file mode 100644 index d413679ea..000000000 --- a/test/e2e/e2e.coverprofile +++ /dev/null @@ -1,11 +0,0 @@ -mode: atomic -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:14.46,21.20 2 3 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:32.2,32.19 1 3 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:39.2,39.53 1 3 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:66.2,66.52 1 3 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:21.20,23.17 2 6 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:26.3,29.36 4 6 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:23.17,25.4 1 0 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:32.19,37.3 3 6 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:39.53,64.3 20 3 -github.com/containers/libpod/test/e2e/pod_pod_namespaces.go:66.52,91.3 20 3
\ No newline at end of file diff --git a/troubleshooting.md b/troubleshooting.md index 9def0e08b..d122983d7 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -442,3 +442,46 @@ Attempts to run podman result in #### Solution One workaround is to disable Secure Boot in your BIOS. + +### 19) error creating libpod runtime: there might not be enough IDs available in the namespace + +Unable to pull images + +#### Symptom + +```console +$ podman unshare cat /proc/self/uid_map + 0 1000 1 +``` + +#### Solution + +```console +$ podman system migrate +``` + +Original command now returns + +``` +$ podman unshare cat /proc/self/uid_map + 0 1000 1 + 1 100000 65536 +``` + +Reference [subuid](http://man7.org/linux/man-pages/man5/subuid.5.html) and [subgid](http://man7.org/linux/man-pages/man5/subgid.5.html) man pages for more detail. + +### 20) Passed-in device can't be accessed in rootless container + +As a non-root user you have group access rights to a device that you want to +pass into a rootless container with `--device=...`. + +#### Symptom + +Any access inside the container is rejected with "Permission denied". + +#### Solution + +The runtime uses `setgroups(2)` hence the process looses all additional groups +the non-root user has. If you use the `crun` runtime, 0.10.4 or newer, +then you can enable a workaround by adding `--annotation io.crun.keep_original_groups=1` +to the `podman` command line. diff --git a/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md b/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md index c4590bf93..5e7e9d5e5 100644 --- a/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md +++ b/vendor/github.com/uber/jaeger-client-go/CHANGELOG.md @@ -1,6 +1,16 @@ Changes by Version ================== +2.20.1 (2019-11-08) +------------------- + +Minor patch via https://github.com/jaegertracing/jaeger-client-go/pull/468 + +- Make `AdaptiveSamplerUpdater` usable with default values; Resolves #467 +- Create `OperationNameLateBinding` sampler option and config option +- Make `SamplerOptions` var of public type, so that its functions are discoverable via godoc + + 2.20.0 (2019-11-06) ------------------- diff --git a/vendor/github.com/uber/jaeger-client-go/config/config.go b/vendor/github.com/uber/jaeger-client-go/config/config.go index 965f7c3ee..a0c32d804 100644 --- a/vendor/github.com/uber/jaeger-client-go/config/config.go +++ b/vendor/github.com/uber/jaeger-client-go/config/config.go @@ -76,17 +76,26 @@ type SamplerConfig struct { // Can be set by exporting an environment variable named JAEGER_SAMPLER_MANAGER_HOST_PORT SamplingServerURL string `yaml:"samplingServerURL"` - // MaxOperations is the maximum number of operations that the sampler - // will keep track of. If an operation is not tracked, a default probabilistic - // sampler will be used rather than the per operation specific sampler. - // Can be set by exporting an environment variable named JAEGER_SAMPLER_MAX_OPERATIONS - MaxOperations int `yaml:"maxOperations"` - // SamplingRefreshInterval controls how often the remotely controlled sampler will poll // jaeger-agent for the appropriate sampling strategy. // Can be set by exporting an environment variable named JAEGER_SAMPLER_REFRESH_INTERVAL SamplingRefreshInterval time.Duration `yaml:"samplingRefreshInterval"` + // MaxOperations is the maximum number of operations that the PerOperationSampler + // will keep track of. If an operation is not tracked, a default probabilistic + // sampler will be used rather than the per operation specific sampler. + // Can be set by exporting an environment variable named JAEGER_SAMPLER_MAX_OPERATIONS. + MaxOperations int `yaml:"maxOperations"` + + // Opt-in feature for applications that require late binding of span name via explicit + // call to SetOperationName when using PerOperationSampler. When this feature is enabled, + // the sampler will return retryable=true from OnCreateSpan(), thus leaving the sampling + // decision as non-final (and the span as writeable). This may lead to degraded performance + // in applications that always provide the correct span name on trace creation. + // + // For backwards compatibility this option is off by default. + OperationNameLateBinding bool `yaml:"operationNameLateBinding"` + // Options can be used to programmatically pass additional options to the Remote sampler. Options []jaeger.SamplerOption } @@ -335,7 +344,7 @@ func (sc *SamplerConfig) NewSampler( return jaeger.NewProbabilisticSampler(sc.Param) } return nil, fmt.Errorf( - "Invalid Param for probabilistic sampler: %v. Expecting value between 0 and 1", + "invalid Param for probabilistic sampler; expecting value between 0 and 1, received %v", sc.Param, ) } @@ -353,17 +362,14 @@ func (sc *SamplerConfig) NewSampler( jaeger.SamplerOptions.Metrics(metrics), jaeger.SamplerOptions.InitialSampler(initSampler), jaeger.SamplerOptions.SamplingServerURL(sc.SamplingServerURL), - } - if sc.MaxOperations != 0 { - options = append(options, jaeger.SamplerOptions.MaxOperations(sc.MaxOperations)) - } - if sc.SamplingRefreshInterval != 0 { - options = append(options, jaeger.SamplerOptions.SamplingRefreshInterval(sc.SamplingRefreshInterval)) + jaeger.SamplerOptions.MaxOperations(sc.MaxOperations), + jaeger.SamplerOptions.OperationNameLateBinding(sc.OperationNameLateBinding), + jaeger.SamplerOptions.SamplingRefreshInterval(sc.SamplingRefreshInterval), } options = append(options, sc.Options...) return jaeger.NewRemotelyControlledSampler(serviceName, options...), nil } - return nil, fmt.Errorf("Unknown sampler type %v", sc.Type) + return nil, fmt.Errorf("unknown sampler type (%s)", sc.Type) } // NewReporter instantiates a new reporter that submits spans to the collector diff --git a/vendor/github.com/uber/jaeger-client-go/constants.go b/vendor/github.com/uber/jaeger-client-go/constants.go index 0da47b02f..5d27b628d 100644 --- a/vendor/github.com/uber/jaeger-client-go/constants.go +++ b/vendor/github.com/uber/jaeger-client-go/constants.go @@ -22,7 +22,7 @@ import ( const ( // JaegerClientVersion is the version of the client library reported as Span tag. - JaegerClientVersion = "Go-2.20.0" + JaegerClientVersion = "Go-2.20.1" // JaegerClientVersionTagKey is the name of the tag used to report client version. JaegerClientVersionTagKey = "jaeger.version" diff --git a/vendor/github.com/uber/jaeger-client-go/sampler.go b/vendor/github.com/uber/jaeger-client-go/sampler.go index 6195d59c5..f47004b1f 100644 --- a/vendor/github.com/uber/jaeger-client-go/sampler.go +++ b/vendor/github.com/uber/jaeger-client-go/sampler.go @@ -363,6 +363,9 @@ type PerOperationSamplerParams struct { // NewPerOperationSampler returns a new PerOperationSampler. func NewPerOperationSampler(params PerOperationSamplerParams) *PerOperationSampler { + if params.MaxOperations <= 0 { + params.MaxOperations = defaultMaxOperations + } samplers := make(map[string]*GuaranteedThroughputProbabilisticSampler) for _, strategy := range params.Strategies.PerOperationStrategies { sampler := newGuaranteedThroughputProbabilisticSampler( diff --git a/vendor/github.com/uber/jaeger-client-go/sampler_remote.go b/vendor/github.com/uber/jaeger-client-go/sampler_remote.go index 9bd0c9822..4448b8f64 100644 --- a/vendor/github.com/uber/jaeger-client-go/sampler_remote.go +++ b/vendor/github.com/uber/jaeger-client-go/sampler_remote.go @@ -258,8 +258,9 @@ func (u *RateLimitingSamplerUpdater) Update(sampler SamplerV2, strategy interfac // ----------------------- // AdaptiveSamplerUpdater is used by RemotelyControlledSampler to parse sampling configuration. +// Fields have the same meaning as in PerOperationSamplerParams. type AdaptiveSamplerUpdater struct { - MaxOperations int // required + MaxOperations int OperationNameLateBinding bool } diff --git a/vendor/github.com/uber/jaeger-client-go/sampler_remote_options.go b/vendor/github.com/uber/jaeger-client-go/sampler_remote_options.go index 7a292effc..3b5c6aa9c 100644 --- a/vendor/github.com/uber/jaeger-client-go/sampler_remote_options.go +++ b/vendor/github.com/uber/jaeger-client-go/sampler_remote_options.go @@ -23,12 +23,17 @@ import ( // SamplerOption is a function that sets some option on the sampler type SamplerOption func(options *samplerOptions) -// SamplerOptions is a factory for all available SamplerOption's -var SamplerOptions samplerOptions +// SamplerOptions is a factory for all available SamplerOption's. +var SamplerOptions SamplerOptionsFactory + +// SamplerOptionsFactory is a factory for all available SamplerOption's. +// The type acts as a namespace for factory functions. It is public to +// make the functions discoverable via godoc. Recommended to be used +// via global SamplerOptions variable. +type SamplerOptionsFactory struct{} type samplerOptions struct { metrics *Metrics - maxOperations int sampler SamplerV2 logger Logger samplingServerURL string @@ -36,11 +41,12 @@ type samplerOptions struct { samplingFetcher SamplingStrategyFetcher samplingParser SamplingStrategyParser updaters []SamplerUpdater + posParams PerOperationSamplerParams } // Metrics creates a SamplerOption that initializes Metrics on the sampler, // which is used to emit statistics. -func (samplerOptions) Metrics(m *Metrics) SamplerOption { +func (SamplerOptionsFactory) Metrics(m *Metrics) SamplerOption { return func(o *samplerOptions) { o.metrics = m } @@ -48,22 +54,30 @@ func (samplerOptions) Metrics(m *Metrics) SamplerOption { // MaxOperations creates a SamplerOption that sets the maximum number of // operations the sampler will keep track of. -func (samplerOptions) MaxOperations(maxOperations int) SamplerOption { +func (SamplerOptionsFactory) MaxOperations(maxOperations int) SamplerOption { + return func(o *samplerOptions) { + o.posParams.MaxOperations = maxOperations + } +} + +// OperationNameLateBinding creates a SamplerOption that sets the respective +// field in the PerOperationSamplerParams. +func (SamplerOptionsFactory) OperationNameLateBinding(enable bool) SamplerOption { return func(o *samplerOptions) { - o.maxOperations = maxOperations + o.posParams.OperationNameLateBinding = enable } } // InitialSampler creates a SamplerOption that sets the initial sampler // to use before a remote sampler is created and used. -func (samplerOptions) InitialSampler(sampler Sampler) SamplerOption { +func (SamplerOptionsFactory) InitialSampler(sampler Sampler) SamplerOption { return func(o *samplerOptions) { o.sampler = samplerV1toV2(sampler) } } // Logger creates a SamplerOption that sets the logger used by the sampler. -func (samplerOptions) Logger(logger Logger) SamplerOption { +func (SamplerOptionsFactory) Logger(logger Logger) SamplerOption { return func(o *samplerOptions) { o.logger = logger } @@ -71,7 +85,7 @@ func (samplerOptions) Logger(logger Logger) SamplerOption { // SamplingServerURL creates a SamplerOption that sets the sampling server url // of the local agent that contains the sampling strategies. -func (samplerOptions) SamplingServerURL(samplingServerURL string) SamplerOption { +func (SamplerOptionsFactory) SamplingServerURL(samplingServerURL string) SamplerOption { return func(o *samplerOptions) { o.samplingServerURL = samplingServerURL } @@ -79,28 +93,28 @@ func (samplerOptions) SamplingServerURL(samplingServerURL string) SamplerOption // SamplingRefreshInterval creates a SamplerOption that sets how often the // sampler will poll local agent for the appropriate sampling strategy. -func (samplerOptions) SamplingRefreshInterval(samplingRefreshInterval time.Duration) SamplerOption { +func (SamplerOptionsFactory) SamplingRefreshInterval(samplingRefreshInterval time.Duration) SamplerOption { return func(o *samplerOptions) { o.samplingRefreshInterval = samplingRefreshInterval } } // SamplingStrategyFetcher creates a SamplerOption that initializes sampling strategy fetcher. -func (samplerOptions) SamplingStrategyFetcher(fetcher SamplingStrategyFetcher) SamplerOption { +func (SamplerOptionsFactory) SamplingStrategyFetcher(fetcher SamplingStrategyFetcher) SamplerOption { return func(o *samplerOptions) { o.samplingFetcher = fetcher } } // SamplingStrategyParser creates a SamplerOption that initializes sampling strategy parser. -func (samplerOptions) SamplingStrategyParser(parser SamplingStrategyParser) SamplerOption { +func (SamplerOptionsFactory) SamplingStrategyParser(parser SamplingStrategyParser) SamplerOption { return func(o *samplerOptions) { o.samplingParser = parser } } // Updaters creates a SamplerOption that initializes sampler updaters. -func (samplerOptions) Updaters(updaters ...SamplerUpdater) SamplerOption { +func (SamplerOptionsFactory) Updaters(updaters ...SamplerUpdater) SamplerOption { return func(o *samplerOptions) { o.updaters = updaters } @@ -116,9 +130,6 @@ func (o *samplerOptions) applyOptionsAndDefaults(opts ...SamplerOption) *sampler if o.logger == nil { o.logger = log.NullLogger } - if o.maxOperations <= 0 { - o.maxOperations = defaultMaxOperations - } if o.samplingServerURL == "" { o.samplingServerURL = DefaultSamplingServerURL } @@ -139,7 +150,10 @@ func (o *samplerOptions) applyOptionsAndDefaults(opts ...SamplerOption) *sampler } if o.updaters == nil { o.updaters = []SamplerUpdater{ - &AdaptiveSamplerUpdater{MaxOperations: o.maxOperations}, + &AdaptiveSamplerUpdater{ + MaxOperations: o.posParams.MaxOperations, + OperationNameLateBinding: o.posParams.OperationNameLateBinding, + }, new(ProbabilisticSamplerUpdater), new(RateLimitingSamplerUpdater), } diff --git a/vendor/modules.txt b/vendor/modules.txt index 580b4e8cd..ba692e089 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -451,7 +451,7 @@ github.com/stretchr/testify/require github.com/syndtr/gocapability/capability # github.com/tchap/go-patricia v2.3.0+incompatible github.com/tchap/go-patricia/patricia -# github.com/uber/jaeger-client-go v2.20.0+incompatible +# github.com/uber/jaeger-client-go v2.20.1+incompatible github.com/uber/jaeger-client-go github.com/uber/jaeger-client-go/config github.com/uber/jaeger-client-go/internal/baggage |