diff options
59 files changed, 1947 insertions, 289 deletions
@@ -185,6 +185,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func UnpausePod(name: string) string](#UnpausePod) +[func UntagImage(name: string, tag: string) string](#UntagImage) + [func VolumeCreate(options: VolumeCreateOpts) string](#VolumeCreate) [func VolumeRemove(options: VolumeRemoveOpts) []string, map[string]](#VolumeRemove) @@ -235,6 +237,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type InfoPodmanBinary](#InfoPodmanBinary) +[type InfoRegistry](#InfoRegistry) + [type InfoStore](#InfoStore) [type KubePodService](#KubePodService) @@ -1234,6 +1238,14 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.UnpausePod '{"name": "foo "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" } ~~~ +### <a name="UntagImage"></a>func UntagImage +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method UntagImage(name: [string](https://godoc.org/builtin#string), tag: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> +UntagImage takes the name or ID of an image in local storage as well as the +tag name to be removed. If the image cannot be found, an +[ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of +the image is returned on success. ### <a name="VolumeCreate"></a>func VolumeCreate <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -1840,6 +1852,15 @@ go_version [string](https://godoc.org/builtin#string) podman_version [string](https://godoc.org/builtin#string) git_commit [string](https://godoc.org/builtin#string) +### <a name="InfoRegistry"></a>type InfoRegistry + +InfoRegistry describes the host's registry information + +search [[]string](#[]string) + +insecure [[]string](#[]string) + +blocked [[]string](#[]string) ### <a name="InfoStore"></a>type InfoStore InfoStore describes the host's storage informatoin @@ -1952,9 +1973,7 @@ PodmanInfo describes the Podman host and build host [InfoHost](#InfoHost) -registries [[]string](#[]string) - -insecure_registries [[]string](#[]string) +registries [InfoRegistry](#InfoRegistry) store [InfoStore](#InfoStore) @@ -3,7 +3,7 @@ export GOPROXY=https://proxy.golang.org GO ?= go DESTDIR ?= -EPOCH_TEST_COMMIT ?= 2d8f1c8fda4d0a8b7c41addbc89f0b2f83908ec2 +EPOCH_TEST_COMMIT ?= $(shell git merge-base HEAD $${DEST_BRANCH:-master}) HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD @@ -471,7 +471,7 @@ uninstall: .PHONY: .gitvalidation .gitvalidation: .gopathok - GIT_CHECK_EXCLUDE="./vendor:docs/make.bat" $(GOBIN)/git-validation -v -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) + GIT_CHECK_EXCLUDE="./vendor:docs/make.bat" $(GOBIN)/git-validation -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) .PHONY: install.tools install.tools: .install.gitvalidation .install.gometalinter .install.md2man .install.ginkgo .install.golangci-lint ## Install needed tools @@ -572,10 +572,13 @@ vendor-in-container: package: ## Build rpm packages ## TODO(ssbarnea): make version number predictable, it should not change ## on each execution, producing duplicates. - rm -f ~/rpmbuild/RPMS/x86_64/* ~/rpmbuild/RPMS/noarch/* + rm -rf build/* *.src.rpm ~/rpmbuild/RPMS/* ./contrib/build_rpm.sh +# Remember that rpms install exec to /usr/bin/podman while a `make install` +# installs them to /usr/local/bin/podman which is likely before. Always use +# a full path to test installed podman or you risk to call another executable. package-install: package ## Install rpm packages - sudo ${PKG_MANAGER} -y remove podman podman-remote - sudo ${PKG_MANAGER} -y clean all sudo ${PKG_MANAGER} -y install ${HOME}/rpmbuild/RPMS/*/*.rpm + /usr/bin/podman version + /usr/bin/podman info # will catch a broken conmon diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index e81756808..0e4315411 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -308,12 +308,13 @@ type HealthCheckValues struct { type KubePlayValues struct { PodmanCommand - Authfile string - CertDir string - Creds string - Quiet bool - SignaturePolicy string - TlsVerify bool + Authfile string + CertDir string + Creds string + Quiet bool + SignaturePolicy string + TlsVerify bool + SeccompProfileRoot string } type PodCreateValues struct { @@ -680,3 +681,7 @@ type SystemDfValues struct { Verbose bool Format string } + +type UntagValues struct { + PodmanCommand +} diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 69365201e..dc7590590 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -308,7 +308,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "image-volume", cliconfig.DefaultImageVolume, - "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore'", + `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, ) createFlags.Bool( "init", false, @@ -431,7 +431,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "pull", "missing", - `Pull image before creating ("always"|"missing"|"never") (default "missing")`, + `Pull image before creating ("always"|"missing"|"never")`, ) createFlags.BoolP( "quiet", "q", false, @@ -447,7 +447,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "restart", "", - "Restart policy to apply when a container exits", + `Restart policy to apply when a container exits ("always"|"no"|"on-failure")`, ) createFlags.Bool( "rm", false, @@ -492,7 +492,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "systemd", "true", - `Run container in systemd mode ("true"|"false"|"always" (default "true")`, + `Run container in systemd mode ("true"|"false"|"always")`, ) createFlags.StringArray( "tmpfs", []string{}, diff --git a/cmd/podman/image.go b/cmd/podman/image.go index 66c141686..ce576ff4b 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -74,6 +74,7 @@ var imageSubCommands = []*cobra.Command{ _saveCommand, _tagCommand, _treeCommand, + _untagCommand, } func init() { diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 344170ddd..c727eea85 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -68,6 +68,7 @@ var mainCommands = []*cobra.Command{ imageCommand.Command, _startCommand, systemCommand.Command, + _untagCommand, } var rootCmd = &cobra.Command{ diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index bc46e4652..e5b87754b 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -33,7 +33,7 @@ const remote = false func init() { cgroupManager := define.SystemdCgroupsManager - cgroupHelp := "Cgroup manager to use (cgroupfs or systemd)" + cgroupHelp := `Cgroup manager to use ("cgroupfs"|"systemd")` cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() if rootless.IsRootless() && !cgroupv2 { cgroupManager = "" @@ -50,12 +50,12 @@ func init() { if err := rootCmd.PersistentFlags().MarkHidden("default-mounts-file"); err != nil { logrus.Error("unable to mark default-mounts-file flag as hidden") } - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.EventsBackend, "events-backend", "", "Events backend to use") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.EventsBackend, "events-backend", "", `Events backend to use ("file"|"journald"|"none")`) // Override default --help information of `--help` global flag var dummyHelp bool rootCmd.PersistentFlags().BoolVar(&dummyHelp, "help", false, "Help for podman") rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", `Log messages above specified level ("debug"|"info"|"warn"|"error"|"fatal"|"panic")`) rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") if err := rootCmd.PersistentFlags().MarkHidden("max-workers"); err != nil { logrus.Error("unable to mark max-workers flag as hidden") diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index fc9f2d5b6..2028d2ef4 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -28,6 +28,8 @@ var ( }, Example: `podman play kube demo.yml`, } + // https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/ + defaultSeccompRoot = "/var/lib/kubelet/seccomp" ) func init() { @@ -46,6 +48,7 @@ func init() { flags.StringVar(&playKubeCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + flags.StringVar(&playKubeCommand.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles") markFlagHidden(flags, "signature-policy") } } diff --git a/cmd/podman/untag.go b/cmd/podman/untag.go new file mode 100644 index 000000000..9ff62b808 --- /dev/null +++ b/cmd/podman/untag.go @@ -0,0 +1,67 @@ +package main + +import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + untagCommand cliconfig.UntagValues + + _untagCommand = &cobra.Command{ + Use: "untag [flags] IMAGE [NAME...]", + Short: "Remove a name from a local image", + Long: "Removes one or more names from a locally-stored image.", + RunE: func(cmd *cobra.Command, args []string) error { + untagCommand.InputArgs = args + untagCommand.GlobalFlags = MainGlobalOpts + untagCommand.Remote = remoteclient + return untag(&untagCommand) + }, + Example: `podman untag 0e3bbc2 + podman untag imageID:latest otherImageName:latest + podman untag httpd myregistryhost:5000/fedora/httpd:v2`, + } +) + +func init() { + untagCommand.Command = _untagCommand + untagCommand.SetHelpTemplate(HelpTemplate()) + untagCommand.SetUsageTemplate(UsageTemplate()) +} + +func untag(c *cliconfig.UntagValues) error { + args := c.InputArgs + + if len(args) == 0 { + return errors.Errorf("at least one image name needs to be specified") + } + + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not create runtime") + } + defer runtime.DeferredShutdown(false) + + newImage, err := runtime.NewImageFromLocal(args[0]) + if err != nil { + return err + } + + tags := args[1:] + if len(args) == 1 { + // Remove all tags if not explicitly specified + tags = newImage.Names() + } + logrus.Debugf("Tags to be removed: %v", tags) + + for _, tag := range tags { + if err := newImage.UntagImage(tag); err != nil { + return errors.Wrapf(err, "removing %q from %q", tag, newImage.InputName) + } + } + return nil +} diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 1bacd2de6..ac400a467 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -245,6 +245,13 @@ type InfoGraphStatus ( supports_d_type: string ) +# InfoRegistry describes the host's registry information +type InfoRegistry ( + search: []string, + insecure: []string, + blocked: []string +) + # InfoStore describes the host's storage informatoin type InfoStore ( containers: int, @@ -267,8 +274,7 @@ type InfoPodmanBinary ( # PodmanInfo describes the Podman host and build type PodmanInfo ( host: InfoHost, - registries: []string, - insecure_registries: []string, + registries: InfoRegistry, store: InfoStore, podman: InfoPodmanBinary ) @@ -860,6 +866,12 @@ method PushImage(name: string, tag: string, compress: bool, format: string, remo # be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success. method TagImage(name: string, tagged: string) -> (image: string) +# UntagImage takes the name or ID of an image in local storage as well as the +# tag name to be removed. If the image cannot be found, an +# [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of +# the image is returned on success. +method UntagImage(name: string, tag: string) -> (image: string) + # RemoveImage takes the name or ID of an image as well as a boolean that determines if containers using that image # should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The # ID of the removed image is returned when complete. See also [DeleteUnusedImages](DeleteUnusedImages). diff --git a/commands.md b/commands.md index de9169a4b..b744b702e 100644 --- a/commands.md +++ b/commands.md @@ -37,7 +37,7 @@ | [podman-import(1)](/docs/source/markdown/podman-import.1.md) | Import a tarball and save it as a filesystem image | | [podman-info(1)](/docs/source/markdown/podman-info.1.md) | Display system information | | [podman-init(1)](/docs/source/markdown/podman-init.1.md) | Initialize a container | -| [podman-inspect(1)](/docs/source/markdown/podman-inspect.1.md) | Display the configuration of a container or image | [![...](/docs/source/markdown/play.png)](https://asciinema.org/a/133418) | +| [podman-inspect(1)](/docs/source/markdown/podman-inspect.1.md) | Display the configuration of a container or image | [![...](/docs/source/markdown/play.png)](https://podman.io/asciinema/podman/inspect/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_inspect.sh) | | [podman-kill(1)](/docs/source/markdown/podman-kill.1.md) | Kill the main process in one or more running containers | | [podman-load(1)](/docs/source/markdown/podman-load.1.md) | Load an image from a container image archive | | [podman-login(1)](/docs/source/markdown/podman-login.1.md) | Login to a container registry | diff --git a/completions/bash/podman b/completions/bash/podman index 40be0018b..c23d156bc 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2672,6 +2672,7 @@ _podman_play_kube() { --quiet -q --tls-verify + --seccomp-profile-root " case "$cur" in diff --git a/completions/zsh/_podman b/completions/zsh/_podman index 06aa92748..067eebbbb 100644 --- a/completions/zsh/_podman +++ b/completions/zsh/_podman @@ -111,20 +111,13 @@ _podman_find_helper() { elif expr "$desc" : ".*[Pp]ath" >/dev/null; then optval="path" helper=_files - elif [ "$flags" = "--cgroup-manager" ]; then - optval="cgroup manager" - helper="(cgroupfs systemd)" - elif [ "$flags" = "--log-level" ]; then - optval="log level" - # 'Log messages above specified level: debug, ... (default "...")' - # Strip off the description and all 'default' strings - desc=${desc/Log*:/} # debug, info, ... (default "...") - desc=${(S)desc//\(*\)/} # debug, info, ... or panic - desc=${desc//,/} # debug info ... or panic - desc=${desc// or / } # debug info ... panic - desc=${desc// / } # collapse multiple spaces + # For messages like 'restart policy ("always"|"no"|"on-failure") + elif optlist=$(expr "$desc" : '.*(\(\"[^\\)]\+|[^\\)]\+\"\))' 2>/dev/null); then + optval=${${flags##--}//-/ } # "--log-level" => "log level" + optlist=${optlist//\"/} # "a"|"b"|"c" => a|b|c + optlist=${optlist//\|/ } # a|b|c => a b c # FIXME: how to present values _in order_, not sorted alphabetically? - helper="($desc)" + helper="($optlist)" fi echo "$optval:$helper" } diff --git a/contrib/build_rpm.sh b/contrib/build_rpm.sh index b64973f0d..b162a9c88 100755 --- a/contrib/build_rpm.sh +++ b/contrib/build_rpm.sh @@ -2,7 +2,7 @@ set -euxo pipefail # returned path can vary: /usr/bin/dnf /bin/dnf ... -pkg_manager=`command -v dnf yum | head -n1` +pkg_manager=$(command -v dnf yum | head -n1) echo "Package manager binary: $pkg_manager" @@ -14,25 +14,32 @@ enabled=1 gpgcheck=0" > /etc/yum.repos.d/container_virt.repo fi -declare -a PKGS=(device-mapper-devel \ +declare -a PKGS=(\ + createrepo \ + device-mapper-devel \ git \ glib2-devel \ glibc-static \ + go-compilers-golang-compiler \ golang \ gpgme-devel \ libassuan-devel \ libseccomp-devel \ libselinux-devel \ make \ + redhat-rpm-config \ rpm-build \ - go-compilers-golang-compiler \ + rpmdevtools \ systemd-devel \ ) if [[ $pkg_manager == *dnf ]]; then # We need to enable PowerTools if we want to get # install all the pkgs we define in PKGS - sudo dnf config-manager --set-enabled PowerTools + # PowerTools exists on centos-8 but not on fedora-30 and rhel-8 + if (dnf -v -C repolist all|grep "Repo-id : PowerTools" >/dev/null); then + sudo dnf config-manager --set-enabled PowerTools + fi PKGS+=(python3-devel \ python3-varlink \ @@ -53,6 +60,9 @@ export extra_arg="--without doc --without debug" echo ${PKGS[*]} sudo $pkg_manager install -y ${PKGS[*]} +# clean up src.rpm as it's been built +sudo rm -f podman-*.src.rpm + make -f .copr/Makefile # workaround for https://github.com/containers/libpod/issues/4627 if [ -d ~/rpmbuild/BUILD ]; then @@ -60,6 +70,3 @@ if [ -d ~/rpmbuild/BUILD ]; then fi rpmbuild --rebuild ${extra_arg:-} podman-*.src.rpm - -# clean up src.rpm as it's been built -sudo rm -f podman-*.src.rpm diff --git a/docs/source/markdown/links/podman-image-untag.1 b/docs/source/markdown/links/podman-image-untag.1 new file mode 100644 index 000000000..3ec6a8a68 --- /dev/null +++ b/docs/source/markdown/links/podman-image-untag.1 @@ -0,0 +1 @@ +.so man1/podman-untag.1 diff --git a/docs/source/markdown/podman-image.1.md b/docs/source/markdown/podman-image.1.md index 339a531dd..1552098ac 100644 --- a/docs/source/markdown/podman-image.1.md +++ b/docs/source/markdown/podman-image.1.md @@ -27,6 +27,7 @@ The image command allows you to manage images | save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | | sign | [podman-image-sign(1)](podman-image-sign.1.md) | Create a signature for an image. | | tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | +| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | | tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. | | trust | [podman-image-trust(1)](podman-image-trust.1.md)| Manage container registry image trust policy. | diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index 2ac860a32..2367ff7fe 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -40,6 +40,10 @@ value can be entered. The password is entered without echo. Suppress output information when pulling images +**--seccomp-profile-root**=*path* + +Directory path for seccomp profiles (default: "/var/lib/kubelet/seccomp"). (Not available for remote commands) + **--tls-verify**=*true|false* Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, diff --git a/docs/source/markdown/podman-untag.1.md b/docs/source/markdown/podman-untag.1.md new file mode 100644 index 000000000..3a54283d6 --- /dev/null +++ b/docs/source/markdown/podman-untag.1.md @@ -0,0 +1,37 @@ +% podman-untag(1) + +## NAME +podman\-untag - Removes one or more names from a locally-stored image + +## SYNOPSIS +**podman untag** *image*[:*tag*] [*target-names*[:*tag*]] [*options*] + +**podman image untag** *image*[:*tag*] [target-names[:*tag*]] [*options*] + +## DESCRIPTION +Removes one or all names of an image. A name refers to the entire image name, +including the optional *tag* after the `:`. If no target image names are +specified, `untag` will remove all tags for the image at once. + +## OPTIONS + +**--help**, **-h** + +Print usage statement + +## EXAMPLES + +``` +$ podman untag 0e3bbc2 + +$ podman untag imageName:latest otherImageName:latest + +$ podman untag httpd myregistryhost:5000/fedora/httpd:v2 +``` + + +## SEE ALSO +podman(1) + +## HISTORY +December 2019, Originally compiled by Sascha Grunert <sgrunert@suse.com> diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 01c750144..0c9ec3d1c 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -201,6 +201,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. | | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. | | [podman-unshare(1)](podman-unshare.1.md) | Run a command inside of a modified user namespace. | +| [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | | [podman-varlink(1)](podman-varlink.1.md) | Runs the varlink backend interface. | | [podman-version(1)](podman-version.1.md) | Display the Podman version information. | | [podman-volume(1)](podman-volume.1.md) | Simple management tool for volumes. | @@ -11,7 +11,6 @@ require ( github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 github.com/containernetworking/plugins v0.8.2 github.com/containers/buildah v1.12.0 - github.com/containers/conmon v2.0.2+incompatible // indirect github.com/containers/image/v5 v5.1.0 github.com/containers/psgo v1.4.0 github.com/containers/storage v1.15.4 @@ -37,7 +36,6 @@ require ( github.com/hashicorp/go-multierror v1.0.0 github.com/hpcloud/tail v1.0.0 github.com/json-iterator/go v1.1.8 - github.com/mattn/go-isatty v0.0.8 // indirect github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 github.com/onsi/ginkgo v1.10.3 github.com/onsi/gomega v1.7.1 @@ -51,13 +49,13 @@ require ( github.com/pkg/errors v0.8.1 github.com/pkg/profile v1.4.0 github.com/pmezard/go-difflib v1.0.0 + github.com/rootless-containers/rootlesskit v0.7.1 github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 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.1+incompatible github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5 // indirect github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b @@ -13,8 +13,6 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= @@ -58,8 +56,6 @@ github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go. github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= @@ -73,20 +69,10 @@ github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVL github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.2 h1:5lnwfsAYO+V7yXhysJKy3E1A2Gy9oVut031zfdOzI9w= github.com/containernetworking/plugins v0.8.2/go.mod h1:TxALKWZpWL79BC3GOYKJzzXr7U8R23PdhwaLp6F3adc= -github.com/containers/buildah v1.11.4-0.20191028173731-21b4778b359e h1:iDavHEx5Yr7o+0l6495Ya6N0YEPplIUZuWC2e14baDM= -github.com/containers/buildah v1.11.4-0.20191028173731-21b4778b359e/go.mod h1:Igrk75FAxLnzDaHUbtpWB8pwL+Bv+cnakWMvqAXW2v8= -github.com/containers/buildah v1.11.5-0.20191031204705-20e92ffe0982 h1:5WUe09k2sJSbmxwLHZLHc41TrIPrP0GlbhX+WDJBqvs= -github.com/containers/buildah v1.11.5-0.20191031204705-20e92ffe0982/go.mod h1:eGWB4tLoo0hIBuytQpvgUC0hk2mvl2ofaYBeDsU/qoc= -github.com/containers/buildah v1.11.5 h1:bVpkaVlvA7G+1mBDAcX6yf7jNZJ/ZrrAHDt4WCx2i8E= -github.com/containers/buildah v1.11.5/go.mod h1:bfNPqLO8GnI0qMPmI6MHSpQNK+a3TH9syYsRg+iqhRw= -github.com/containers/buildah v1.11.6 h1:PhlF++LAezRtOKHfKhBlo8DLvpMQIvU/K2VfAhknadE= -github.com/containers/buildah v1.11.6/go.mod h1:02+o3ZTICaPyP0QcQFoQd07obLMdAecSnFN2kDhcqNo= github.com/containers/buildah v1.12.0 h1:bi/8ACl8qobazwfYgNze5y+aRuBIG+R7lMStFbnDOxE= github.com/containers/buildah v1.12.0/go.mod h1:yzPuQ/mJTPsfSLCyBPbeaoXgBLanjnf36M2cDzyckMg= github.com/containers/common v0.0.3 h1:C2Zshb0w720FqPa42MCRuiGfbW0kwbURRwvK1EWIC5I= github.com/containers/common v0.0.3/go.mod h1:CaOgMRiwi2JJHISMZ6VPPZhQYFUDRv3YYVss2RqUCMg= -github.com/containers/conmon v2.0.2+incompatible h1:h2HCdd/EBpwFn7RT82Y2GyXnVUHWxk1Jm4cESSZG4P8= -github.com/containers/conmon v2.0.2+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.0.0 h1:arnXgbt1ucsC/ndtSpiQY87rA0UjhF+/xQnPzqdBDn4= github.com/containers/image/v5 v5.0.0/go.mod h1:MgiLzCfIeo8lrHi+4Lb8HP+rh513sm0Mlk6RrhjFOLY= github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo= @@ -95,21 +81,11 @@ github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDpl github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM= github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/psgo v1.3.2 h1:jYfppPih3S/j2Yi5O14AXjd8GfCx1ph9L3YsoK3adko= -github.com/containers/psgo v1.3.2/go.mod h1:ENXXLQ5E1At4K0EUsGogXBJi/C28gwqkONWeLPI9fJ8= github.com/containers/psgo v1.4.0 h1:D8B4fZCCZhYgc8hDyMPCiShOinmOB1TP1qe46sSC19k= github.com/containers/psgo v1.4.0/go.mod h1:ENXXLQ5E1At4K0EUsGogXBJi/C28gwqkONWeLPI9fJ8= github.com/containers/storage v1.13.2/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= github.com/containers/storage v1.13.4 h1:j0bBaJDKbUHtAW1MXPFnwXJtqcH+foWeuXK1YaBV5GA= github.com/containers/storage v1.13.4/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= -github.com/containers/storage v1.13.5 h1:/SUzGeOP2HDijpF7Yur21Ch6WTZC1BNeZF917CWcp5c= -github.com/containers/storage v1.13.5/go.mod h1:HELz8Sn+UVbPaUZMI8RvIG9doD4y4z6Gtg4k7xdd2ZY= -github.com/containers/storage v1.14.0 h1:LbX6WZaDmkXt4DT4xWIg3YXAWd6oA4K9Fi6/KG1xt84= -github.com/containers/storage v1.14.0/go.mod h1:qGPsti/qC1xxX+xcpHfiTMT+8ThVE2Jf83wFHHqkDAY= -github.com/containers/storage v1.15.0 h1:QNW7jJ94ccGcAbFIOSMHUAsUxvHceb71ecLye9EDrkk= -github.com/containers/storage v1.15.0/go.mod h1:qGPsti/qC1xxX+xcpHfiTMT+8ThVE2Jf83wFHHqkDAY= -github.com/containers/storage v1.15.2 h1:hLgafU4tuyQk/smMkXZfHTS8FtAQsqQvfWCp4bsgjuw= -github.com/containers/storage v1.15.2/go.mod h1:v0lq/3f+cXH3Y/HiDaFYRR0zilwDve7I4W7U5xQxvF8= github.com/containers/storage v1.15.3 h1:+lFSQZnnKUFyUEtguIgdoQLJfWSuYz+j/wg5GxLtsN4= github.com/containers/storage v1.15.3/go.mod h1:v0lq/3f+cXH3Y/HiDaFYRR0zilwDve7I4W7U5xQxvF8= github.com/containers/storage v1.15.4 h1:eiUtV9MOTnPHibO18nDRI+aDhKudY7WmAiJdyVMsqSM= @@ -128,8 +104,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbp github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca h1:CJstDqYy9ClWuPcDHMTCAiUS+ckekluYetGR2iYYWuo= -github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca/go.mod h1:BO0al9TKber3XUTucLzKgoG5sq8qiOB41H7zSdfw6r8= github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b h1:SgS+WV10y2Bubuy2HquSBori6DXj9sqRN77Hgs5H7Qc= github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b/go.mod h1:ZOuIEOp/3MB1eCBWANnNxM3zUA3NWh76wSRCsnKAg2c= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= @@ -149,8 +123,6 @@ github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BU github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.0.0-20171019062838-86f080cff091/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v0.0.0-20180522102801-da99009bbb11/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v1.4.2-0.20190927142053-ada3c14355ce h1:H3csZuxZESJeeEiOxq4YXPNmLFbjl7u2qVBrAAGX/sA= -github.com/docker/docker v1.4.2-0.20190927142053-ada3c14355ce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23 h1:oqgGT9O61YAYvI41EBsLePOr+LE6roB0xY4gpkZuFSE= github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f h1:Sm8iD2lifO31DwXfkGzq8VgA7rwxPjRsYmeo0K/dF9Y= @@ -188,8 +160,6 @@ github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsouza/go-dockerclient v1.5.0 h1:7OtayOe5HnoG+KWMHgyyPymwaodnB2IDYuVfseKyxbA= -github.com/fsouza/go-dockerclient v1.5.0/go.mod h1:AqZZK/zFO3phxYxlTsAaeAMSdQ9mgHuhy+bjN034Qds= github.com/fsouza/go-dockerclient v1.6.0 h1:f7j+AX94143JL1H3TiqSMkM4EcLDI0De1qD4GGn3Hig= github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= @@ -207,10 +177,9 @@ github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nA github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f h1:zlOR3rOlPAVvtfuxGKoghCmop5B0TRyu/ZieziZuGiM= -github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v0.0.0-20170815085658-fcdc5011193f/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -225,7 +194,6 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= @@ -270,15 +238,15 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/insomniacslk/dhcp v0.0.0-20190712084813-dc1a53400564/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw= github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 h1:NAAiV9ass6VReWFjuxqrMIq12WKlSULI6Gs3PxQghLA= github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jamescun/tuntap v0.0.0-20190712092105-cb1fb277045c/go.mod h1:zzwpsgcYhzzIP5WyF8g9ivCv38cY9uAV9Gu0m3lThhE= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= @@ -298,8 +266,6 @@ github.com/klauspost/compress v1.7.2 h1:liMOoeIvFpr9kEvalrZ7VVBA4wGf7zfOgwBjzz/5 github.com/klauspost/compress v1.7.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.8.1 h1:oygt2ychZFHOB6M9gUgajzgKrwRgHbGC77NwA4COVgI= github.com/klauspost/compress v1.8.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY= -github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.4 h1:xhvAeUPQ2drNUhKtrGdTGNvV9nNafHMUkRyLkzxJoB4= github.com/klauspost/compress v1.9.4/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= @@ -319,8 +285,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -332,6 +296,7 @@ github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJd github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/vpnkit v0.3.1-0.20190720080441-7dd3dcce7d3d/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -354,8 +319,6 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -364,7 +327,6 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -380,7 +342,6 @@ github.com/opencontainers/runc v0.0.0-20190425234816-dae70e8efea4/go.mod h1:qT5X github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8 h1:dDCFes8Hj1r/i5qnypONo5jdOme/8HWZC/aNDyhECt0= github.com/opencontainers/runc v1.0.0-rc8/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190827142921-dd075602f158 h1:/A6bAdnSZoTQmKml3MdHAnSEPnBAQeigNBl4sxnfaaQ= github.com/opencontainers/runc v1.0.0-rc8.0.20190827142921-dd075602f158/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -408,8 +369,6 @@ github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.3.0 h1:OQIvuDgm00gWVWGTf4m4mCt6W1/0YqU7Ntg0mySWgaI= -github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI= github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -438,6 +397,8 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rootless-containers/rootlesskit v0.7.1 h1:enhwHIAXDjfpV83bL4xF60WZ+1ATjMB7spDDvpWAfPk= +github.com/rootless-containers/rootlesskit v0.7.1/go.mod h1:r9YL5mKRIdnwcYk4G8E5CSc9MDeFtgYmhfE4CSvDGYA= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= @@ -481,12 +442,8 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= -github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/uber/jaeger-client-go v2.19.0+incompatible h1:pbwbYfHUoaase0oPQOdZ1GcaUjImYGimUXSQ/+8+Z8Q= -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/theckman/go-flock v0.7.1/go.mod h1:kjuth3y9VJ2aNlkNEO99G/8lp9fMIKaGyBmh84IBheM= +github.com/u-root/u-root v5.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY= 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= @@ -495,6 +452,7 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b h1:hdDRrn9OP/roL8a/e/5Zu85ldrcdndu9IeBj2OEvQm0= github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s= @@ -577,19 +535,18 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190425145619-16072639606e h1:4ktJgTV34+N3qOZUc5fAaG3Pb11qzMm3PkAoTAgUZ2I= golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190902133755-9109b7679e13 h1:tdsQdquKbTNMsSZLqnLELJGzCANp9oXhu6zFBW6ODx4= golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -623,7 +580,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 h1:9VBRTdmgQxbs6HE0sUnMrSWNePppAJU07NYvX5dIB04= google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= @@ -655,8 +611,6 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v0.0.0-20190624233834-05ebafbffc79/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= diff --git a/libpod/config/config.go b/libpod/config/config.go index 6240bccb0..13c128688 100644 --- a/libpod/config/config.go +++ b/libpod/config/config.go @@ -72,7 +72,7 @@ const ( // SetOptions contains a subset of options in a Config. It's used to indicate if // a given option has either been set by the user or by a parsed libpod // configuration file. If not, the corresponding option might be overwritten by -// values from the database. This behavior guarantess backwards compat with +// values from the database. This behavior guarantees backwards compat with // older version of libpod and Podman. type SetOptions struct { // StorageConfigRunRootSet indicates if the RunRoot has been explicitly set @@ -119,7 +119,7 @@ type Config struct { // SetOptions contains a subset of config options. It's used to indicate if // a given option has either been set by the user or by a parsed libpod // configuration file. If not, the corresponding option might be - // overwritten by values from the database. This behavior guarantess + // overwritten by values from the database. This behavior guarantees // backwards compat with older version of libpod and Podman. SetOptions @@ -451,45 +451,47 @@ func NewConfig(userConfigPath string) (*Config, error) { } // Now, check if the user can access system configs and merge them if needed. - if configs, err := systemConfigs(); err != nil { + configs, err := systemConfigs() + if 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 { - return nil, errors.Wrapf(err, "error merging system config") + } + + 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) } - logrus.Debugf("Merged system config %q: %v", path, config) + 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 { + return nil, errors.Wrapf(err, "error merging system config") } + logrus.Debugf("Merged system config %q: %v", path, config) } // Finally, create a default config from memory and forcefully merge it into // the config. This way we try to make sure that all fields are properly set // and that user AND system config can partially set. - if defaultConfig, err := defaultConfigFromMemory(); err != nil { + defaultConfig, err := defaultConfigFromMemory() + if err != nil { return nil, errors.Wrapf(err, "error generating default config from memory") - } else { - // Check if we need to switch to cgroupfs and logger=file on rootless. - defaultConfig.checkCgroupsAndLogger() + } - if err := config.mergeConfig(defaultConfig); err != nil { - return nil, errors.Wrapf(err, "error merging default config from memory") - } + // Check if we need to switch to cgroupfs and logger=file on rootless. + defaultConfig.checkCgroupsAndLogger() + + if err := config.mergeConfig(defaultConfig); err != nil { + return nil, errors.Wrapf(err, "error merging default config from memory") } // Relative paths can cause nasty bugs, because core paths we use could diff --git a/libpod/config/default.go b/libpod/config/default.go index 5decaeab7..c4a4efdaf 100644 --- a/libpod/config/default.go +++ b/libpod/config/default.go @@ -26,11 +26,12 @@ const ( // config is different for root and rootless. It also parses the storage.conf. func defaultConfigFromMemory() (*Config, error) { c := new(Config) - if tmp, err := defaultTmpDir(); err != nil { + tmp, err := defaultTmpDir() + if err != nil { return nil, err - } else { - c.TmpDir = tmp } + c.TmpDir = tmp + c.EventsLogFilePath = filepath.Join(c.TmpDir, "events", "events.log") storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID()) diff --git a/libpod/container.go b/libpod/container.go index 2693190b5..edf72f4ee 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -135,6 +135,9 @@ type Container struct { rootlessSlirpSyncR *os.File rootlessSlirpSyncW *os.File + rootlessPortSyncR *os.File + rootlessPortSyncW *os.File + // A restored container should have the same IP address as before // being checkpointed. If requestedIP is set it will be used instead // of config.StaticIP. diff --git a/libpod/image/config.go b/libpod/image/config.go index 40e7fd496..bb84175a3 100644 --- a/libpod/image/config.go +++ b/libpod/image/config.go @@ -2,7 +2,7 @@ package image // ImageDeleteResponse is the response for removing an image from storage and containers // what was untagged vs actually removed -type ImageDeleteResponse struct { +type ImageDeleteResponse struct { //nolint Untagged []string `json:"untagged"` Deleted string `json:"deleted"` } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index a68338dbb..89dac2b5d 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -3,8 +3,10 @@ package libpod import ( + "bytes" "crypto/rand" "fmt" + "io" "io/ioutil" "net" "os" @@ -20,6 +22,7 @@ import ( "github.com/containers/libpod/pkg/errorhandling" "github.com/containers/libpod/pkg/netns" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/rootlessport" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -151,19 +154,6 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, return ctrNS, networkStatus, err } -type slirp4netnsCmdArg struct { - Proto string `json:"proto,omitempty"` - HostAddr string `json:"host_addr"` - HostPort int32 `json:"host_port"` - GuestAddr string `json:"guest_addr"` - GuestPort int32 `json:"guest_port"` -} - -type slirp4netnsCmd struct { - Execute string `json:"execute"` - Args slirp4netnsCmdArg `json:"arguments"` -} - func checkSlirpFlags(path string) (bool, bool, bool, error) { cmd := exec.Command(path, "--help") out, err := cmd.CombinedOutput() @@ -194,13 +184,9 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { defer errorhandling.CloseQuiet(syncW) havePortMapping := len(ctr.Config().PortMappings) > 0 - apiSocket := filepath.Join(ctr.runtime.config.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID)) logPath := filepath.Join(ctr.runtime.config.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID)) cmdArgs := []string{} - if havePortMapping { - cmdArgs = append(cmdArgs, "--api-socket", apiSocket) - } dhp, mtu, sandbox, err := checkSlirpFlags(path) if err != nil { return errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err) @@ -221,15 +207,19 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { // -e, --exit-fd=FD specify the FD for terminating slirp4netns // -r, --ready-fd=FD specify the FD to write to when the initialization steps are finished cmdArgs = append(cmdArgs, "-c", "-e", "3", "-r", "4") + netnsPath := "" if !ctr.config.PostConfigureNetNS { ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe() if err != nil { return errors.Wrapf(err, "failed to create rootless network sync pipe") } - cmdArgs = append(cmdArgs, "--netns-type=path", ctr.state.NetNS.Path(), "tap0") + netnsPath = ctr.state.NetNS.Path() + cmdArgs = append(cmdArgs, "--netns-type=path", netnsPath, "tap0") } else { defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR) defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW) + netnsPath = fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID) + // we don't use --netns-path here (unavailable for slirp4netns < v0.4) cmdArgs = append(cmdArgs, fmt.Sprintf("%d", ctr.state.PID), "tap0") } @@ -269,11 +259,27 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { } }() + if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil { + return err + } + + if havePortMapping { + return r.setupRootlessPortMapping(ctr, netnsPath) + } + return nil +} + +func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout time.Duration) error { + prog := filepath.Base(cmd.Path) + if len(cmd.Args) > 0 { + prog = cmd.Args[0] + } b := make([]byte, 16) for { - if err := syncR.SetDeadline(time.Now().Add(1 * time.Second)); err != nil { - return errors.Wrapf(err, "error setting slirp4netns pipe timeout") + if err := syncR.SetDeadline(time.Now().Add(timeout)); err != nil { + return errors.Wrapf(err, "error setting %s pipe timeout", prog) } + // FIXME: return err as soon as proc exits, without waiting for timeout if _, err := syncR.Read(b); err == nil { break } else { @@ -282,7 +288,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { var status syscall.WaitStatus pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil) if err != nil { - return errors.Wrapf(err, "failed to read slirp4netns process status") + return errors.Wrapf(err, "failed to read %s process status", prog) } if pid != cmd.Process.Pid { continue @@ -294,100 +300,87 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { } logContent, err := ioutil.ReadAll(logFile) if err != nil { - return errors.Wrapf(err, "slirp4netns failed") + return errors.Wrapf(err, "%s failed", prog) } - return errors.Errorf("slirp4netns failed: %q", logContent) + return errors.Errorf("%s failed: %q", prog, logContent) } if status.Signaled() { - return errors.New("slirp4netns killed by signal") + return errors.Errorf("%s killed by signal", prog) } continue } - return errors.Wrapf(err, "failed to read from slirp4netns sync pipe") + return errors.Wrapf(err, "failed to read from %s sync pipe", prog) } } + return nil +} - if havePortMapping { - const pidWaitTimeout = 60 * time.Second - chWait := make(chan error) - go func() { - interval := 25 * time.Millisecond - for i := time.Duration(0); i < pidWaitTimeout; i += interval { - // Check if the process is still running. - var status syscall.WaitStatus - pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil) - if err != nil { - break - } - if pid != cmd.Process.Pid { - continue - } - if status.Exited() || status.Signaled() { - chWait <- fmt.Errorf("slirp4netns exited with status %d", status.ExitStatus()) - } - time.Sleep(interval) - } - }() - defer close(chWait) +func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) (err error) { + syncR, syncW, err := os.Pipe() + if err != nil { + return errors.Wrapf(err, "failed to open pipe") + } + defer errorhandling.CloseQuiet(syncR) + defer errorhandling.CloseQuiet(syncW) - // wait that API socket file appears before trying to use it. - if _, err := WaitForFile(apiSocket, chWait, pidWaitTimeout); err != nil { - return errors.Wrapf(err, "waiting for slirp4nets to create the api socket file %s", apiSocket) - } + logPath := filepath.Join(ctr.runtime.config.TmpDir, fmt.Sprintf("rootlessport-%s.log", ctr.config.ID)) + logFile, err := os.Create(logPath) + if err != nil { + return errors.Wrapf(err, "failed to open rootlessport log file %s", logPath) + } + defer logFile.Close() + // Unlink immediately the file so we won't need to worry about cleaning it up later. + // It is still accessible through the open fd logFile. + if err := os.Remove(logPath); err != nil { + return errors.Wrapf(err, "delete file %s", logPath) + } - // for each port we want to add we need to open a connection to the slirp4netns control socket - // and send the add_hostfwd command. - for _, i := range ctr.config.PortMappings { - conn, err := net.Dial("unix", apiSocket) - if err != nil { - return errors.Wrapf(err, "cannot open connection to %s", apiSocket) - } - defer func() { - if err := conn.Close(); err != nil { - logrus.Errorf("unable to close connection: %q", err) - } - }() - hostIP := i.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - cmd := slirp4netnsCmd{ - Execute: "add_hostfwd", - Args: slirp4netnsCmdArg{ - Proto: i.Protocol, - HostAddr: hostIP, - HostPort: i.HostPort, - GuestPort: i.ContainerPort, - }, - } - // create the JSON payload and send it. Mark the end of request shutting down writes - // to the socket, as requested by slirp4netns. - data, err := json.Marshal(&cmd) - if err != nil { - return errors.Wrapf(err, "cannot marshal JSON for slirp4netns") - } - if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil { - return errors.Wrapf(err, "cannot write to control socket %s", apiSocket) - } - if err := conn.(*net.UnixConn).CloseWrite(); err != nil { - return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket) - } - buf := make([]byte, 2048) - readLength, err := conn.Read(buf) - if err != nil { - return errors.Wrapf(err, "cannot read from control socket %s", apiSocket) - } - // if there is no 'error' key in the received JSON data, then the operation was - // successful. - var y map[string]interface{} - if err := json.Unmarshal(buf[0:readLength], &y); err != nil { - return errors.Wrapf(err, "error parsing error status from slirp4netns") - } - if e, found := y["error"]; found { - return errors.Errorf("error from slirp4netns while setting up port redirection: %v", e) - } + ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe() + if err != nil { + return errors.Wrapf(err, "failed to create rootless port sync pipe") + } + cfg := rootlessport.Config{ + Mappings: ctr.config.PortMappings, + NetNSPath: netnsPath, + ExitFD: 3, + ReadyFD: 4, + TmpDir: ctr.runtime.config.TmpDir, + } + cfgJSON, err := json.Marshal(cfg) + if err != nil { + return err + } + 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 + cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessPortSyncR, syncW) + cmd.Stdin = cfgR + // stdout is for human-readable error, stderr is for debug log + cmd.Stdout = &stdout + cmd.Stderr = io.MultiWriter(logFile, &logrusDebugWriter{"rootlessport: "}) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + if err := cmd.Start(); err != nil { + return errors.Wrapf(err, "failed to start rootlessport process") + } + defer func() { + if err := cmd.Process.Release(); err != nil { + logrus.Errorf("unable to release rootlessport process: %q", err) } + }() + if err := waitForSync(syncR, cmd, logFile, 3*time.Second); err != nil { + stdoutStr := stdout.String() + if stdoutStr != "" { + // err contains full debug log and too verbose, so return stdoutStr + logrus.Debug(err) + return errors.Errorf("failed to expose ports via rootlessport: %q", stdoutStr) + } + return err } + logrus.Debug("rootlessport is ready") return nil } @@ -587,3 +580,12 @@ func (c *Container) getContainerNetworkInfo(data *InspectContainerData) *Inspect } return data } + +type logrusDebugWriter struct { + prefix string +} + +func (w *logrusDebugWriter) Write(p []byte) (int, error) { + logrus.Debugf("%s%s", w.prefix, string(p)) + return len(p), nil +} diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 78c8f4126..b09dc3a4f 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -712,7 +712,7 @@ func (r *ConmonOCIRuntime) ExecUpdateStatus(ctr *Container, sessionID string) (b return true, nil } -// ExecCleanupContainer cleans up files created when a command is run via +// ExecContainerCleanup 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 { // Clean up the sockets dir. Issue #3962 @@ -1003,6 +1003,15 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co } // Leak one end in conmon, the other one will be leaked into slirp4netns cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncW) + + if ctr.rootlessPortSyncR != nil { + defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR) + } + if ctr.rootlessPortSyncW != nil { + defer errorhandling.CloseQuiet(ctr.rootlessPortSyncW) + // Leak one end in conmon, the other one will be leaked into rootlessport + cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessPortSyncW) + } } err = startCommandGivenSelinux(cmd) diff --git a/libpod/options.go b/libpod/options.go index ebde4eecc..031f4f705 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -20,7 +20,9 @@ import ( ) var ( - NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$") + // NameRegex is a regular expression to validate container/pod names. + NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$") + // RegexError is thrown in presence of an invalid container/pod name. RegexError = errors.Wrapf(define.ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*") ) diff --git a/libpod/runtime.go b/libpod/runtime.go index 001d850b0..b4cbde28e 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -213,11 +213,11 @@ func getLockManager(runtime *Runtime) (lock.Manager, error) { // Sets up containers/storage, state store, OCI runtime func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { // Find a working conmon binary - if cPath, err := runtime.config.FindConmon(); err != nil { + cPath, err := runtime.config.FindConmon() + if err != nil { return err - } else { - runtime.conmonPath = cPath } + runtime.conmonPath = cPath // Make the static files directory if it does not exist if err := os.MkdirAll(runtime.config.StaticDir, 0700); err != nil { diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go index 3170e5b3d..c55d1f6ef 100644 --- a/pkg/adapter/info_remote.go +++ b/pkg/adapter/info_remote.go @@ -14,12 +14,11 @@ func (r RemoteRuntime) Info() ([]define.InfoData, error) { // TODO the varlink implementation for info should be updated to match the output for regular info var ( reply []define.InfoData + regInfo map[string]interface{} hostInfo map[string]interface{} store map[string]interface{} ) - registries := make(map[string]interface{}) - insecureRegistries := make(map[string]interface{}) info, err := iopodman.GetInfo().Call(r.Conn) if err != nil { return nil, err @@ -39,13 +38,16 @@ func (r RemoteRuntime) Info() ([]define.InfoData, error) { } json.Unmarshal(s, &store) - registries["registries"] = info.Registries - insecureRegistries["registries"] = info.Insecure_registries + // info.Registries -> map[string]interface{} + reg, err := json.Marshal(info.Registries) + if err != nil { + return nil, err + } + json.Unmarshal(reg, ®Info) // Add everything to the reply reply = append(reply, define.InfoData{Type: "host", Data: hostInfo}) - reply = append(reply, define.InfoData{Type: "registries", Data: registries}) - reply = append(reply, define.InfoData{Type: "insecure registries", Data: insecureRegistries}) + reply = append(reply, define.InfoData{Type: "registries", Data: regInfo}) reply = append(reply, define.InfoData{Type: "store", Data: store}) return reply, nil } diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index a726153c0..5891c361f 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -8,6 +8,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" "strings" "github.com/containers/buildah/pkg/parse" @@ -597,7 +598,7 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa volumes[volume.Name] = hostPath.Path } - seccompPaths, err := initializeSeccompPaths(podYAML.ObjectMeta.Annotations) + seccompPaths, err := initializeSeccompPaths(podYAML.ObjectMeta.Annotations, c.SeccompProfileRoot) if err != nil { return nil, err } @@ -847,7 +848,8 @@ func (k *kubeSeccompPaths) findForContainer(ctrName string) string { // initializeSeccompPaths takes annotations from the pod object metadata and finds annotations pertaining to seccomp // it parses both pod and container level -func initializeSeccompPaths(annotations map[string]string) (*kubeSeccompPaths, error) { +// if the annotation is of the form "localhost/%s", the seccomp profile will be set to profileRoot/%s +func initializeSeccompPaths(annotations map[string]string, profileRoot string) (*kubeSeccompPaths, error) { seccompPaths := &kubeSeccompPaths{containerPaths: make(map[string]string)} var err error if annotations != nil { @@ -863,7 +865,7 @@ func initializeSeccompPaths(annotations map[string]string) (*kubeSeccompPaths, e return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0]) } - path, err := verifySeccompPath(seccomp) + path, err := verifySeccompPath(seccomp, profileRoot) if err != nil { return nil, err } @@ -872,7 +874,7 @@ func initializeSeccompPaths(annotations map[string]string) (*kubeSeccompPaths, e podSeccomp, ok := annotations[v1.SeccompPodAnnotationKey] if ok { - seccompPaths.podPath, err = verifySeccompPath(podSeccomp) + seccompPaths.podPath, err = verifySeccompPath(podSeccomp, profileRoot) } else { seccompPaths.podPath, err = libpod.DefaultSeccompPath() } @@ -885,7 +887,7 @@ func initializeSeccompPaths(annotations map[string]string) (*kubeSeccompPaths, e // verifySeccompPath takes a path and checks whether it is a default, unconfined, or a path // the available options are parsed as defined in https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp -func verifySeccompPath(path string) (string, error) { +func verifySeccompPath(path string, profileRoot string) (string, error) { switch path { case v1.DeprecatedSeccompProfileDockerDefault: fallthrough @@ -894,13 +896,9 @@ func verifySeccompPath(path string) (string, error) { case "unconfined": return path, nil default: - // TODO we have an inconsistency here - // k8s parses `localhost/<path>` which is found at `<seccomp_root>` - // we currently parse `localhost:<seccomp_root>/<path> - // to fully conform, we need to find a good location for the seccomp root - parts := strings.Split(path, ":") + parts := strings.Split(path, "/") if parts[0] == "localhost" { - return parts[1], nil + return filepath.Join(profileRoot, parts[1]), nil } return "", errors.Errorf("invalid seccomp path: %s", path) } diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index dd4f0f35f..8933e826f 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -84,7 +84,7 @@ func getRuntime(runtime *libpod.Runtime) (*LocalRuntime, error) { }, nil } -// GetFilterImages returns a slice of images in containerimages that are "filtered" +// GetFilteredImages returns a slice of images in containerimages that are "filtered" func (r *LocalRuntime) GetFilteredImages(filters []string, rwOnly bool) ([]*ContainerImage, error) { images, err := r.ImageRuntime().GetImagesWithFilters(filters) if err != nil { @@ -111,6 +111,8 @@ func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) { return r.ImagestoContainerImages(images, rwOnly) } +// ImagestoContainerImages converts the slice of *image.Image to a slice of +// *ContainerImage. ReadOnly images are skipped when rwOnly is set. func (r *LocalRuntime) ImagestoContainerImages(images []*image.Image, rwOnly bool) ([]*ContainerImage, error) { var containerImages []*ContainerImage for _, i := range images { diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index fe5cc4fef..9c10b31c0 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -413,6 +413,12 @@ func (ci *ContainerImage) TagImage(tag string) error { return err } +// UntagImage removes a single tag from an image +func (ci *ContainerImage) UntagImage(tag string) error { + _, err := iopodman.UntagImage().Call(ci.Runtime.Conn, ci.ID(), tag) + return err +} + // RemoveImage calls varlink to remove an image func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) { ir := image.ImageDeleteResponse{} diff --git a/pkg/network/config.go b/pkg/network/config.go index e47b16143..a41455f68 100644 --- a/pkg/network/config.go +++ b/pkg/network/config.go @@ -90,6 +90,7 @@ func (p PortMapConfig) Bytes() ([]byte, error) { return json.MarshalIndent(p, "", "\t") } +// IPAMDHCP describes the ipamdhcp config type IPAMDHCP struct { DHCP string `json:"type"` } diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go index 7e9fe9db6..d02721ea9 100644 --- a/pkg/rootless/rootless.go +++ b/pkg/rootless/rootless.go @@ -7,6 +7,9 @@ import ( "github.com/pkg/errors" ) +// TryJoinPauseProcess attempts to join the namespaces of the pause PID via +// TryJoinFromFilePaths. If joining fails, it attempts to delete the specified +// file. func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { if _, err := os.Stat(pausePidPath); err != nil { return false, -1, nil diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 94c42f7d0..182a39f6b 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -514,6 +514,8 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st return joinUserAndMountNS(uint(pausePid), pausePidPath) } + +// ReadMappingsProc parses and returns the ID mappings at the specified path. func ReadMappingsProc(path string) ([]idtools.IDMap, error) { file, err := os.Open(path) if err != nil { diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go new file mode 100644 index 000000000..3e678d33a --- /dev/null +++ b/pkg/rootlessport/rootlessport_linux.go @@ -0,0 +1,264 @@ +// +build linux + +// Package rootlessport provides reexec for RootlessKit-based port forwarder. +// +// init() contains reexec.Register() for ReexecKey . +// +// The reexec requires Config to be provided via stdin. +// +// The reexec writes human-readable error message on stdout on error. +// +// Debug log is printed on stderr. +package rootlessport + +import ( + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "syscall" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containers/storage/pkg/reexec" + "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/pkg/errors" + rkport "github.com/rootless-containers/rootlesskit/pkg/port" + rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin" + rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil" + "github.com/sirupsen/logrus" +) + +const ( + // ReexecKey is the reexec key for the parent process. + ReexecKey = "containers-rootlessport" + // reexecChildKey is used internally for the second reexec + reexecChildKey = "containers-rootlessport-child" + reexecChildEnvOpaque = "_CONTAINERS_ROOTLESSPORT_CHILD_OPAQUE" +) + +// Config needs to be provided to the process via stdin as a JSON string. +// stdin needs to be closed after the message has been written. +type Config struct { + Mappings []ocicni.PortMapping + NetNSPath string + ExitFD int + ReadyFD int + TmpDir string +} + +func init() { + reexec.Register(ReexecKey, func() { + if err := parent(); err != nil { + fmt.Println(err) + os.Exit(1) + } + }) + reexec.Register(reexecChildKey, func() { + if err := child(); err != nil { + fmt.Println(err) + os.Exit(1) + } + }) + +} + +func loadConfig(r io.Reader) (*Config, io.ReadCloser, io.WriteCloser, error) { + stdin, err := ioutil.ReadAll(r) + if err != nil { + return nil, nil, nil, err + } + var cfg Config + if err := json.Unmarshal(stdin, &cfg); err != nil { + return nil, nil, nil, err + } + if cfg.NetNSPath == "" { + return nil, nil, nil, errors.New("missing NetNSPath") + } + if cfg.ExitFD <= 0 { + return nil, nil, nil, errors.New("missing ExitFD") + } + exitFile := os.NewFile(uintptr(cfg.ExitFD), "exitfile") + if exitFile == nil { + return nil, nil, nil, errors.New("invalid ExitFD") + } + if cfg.ReadyFD <= 0 { + return nil, nil, nil, errors.New("missing ReadyFD") + } + readyFile := os.NewFile(uintptr(cfg.ReadyFD), "readyfile") + if readyFile == nil { + return nil, nil, nil, errors.New("invalid ReadyFD") + } + return &cfg, exitFile, readyFile, nil +} + +func parent() error { + // load config from stdin + cfg, exitR, readyW, err := loadConfig(os.Stdin) + if err != nil { + return err + } + + // create the parent driver + stateDir, err := ioutil.TempDir(cfg.TmpDir, "rootlessport") + if err != nil { + return err + } + defer os.RemoveAll(stateDir) + driver, err := rkbuiltin.NewParentDriver(&logrusWriter{prefix: "parent: "}, stateDir) + if err != nil { + return err + } + initComplete := make(chan struct{}) + quit := make(chan struct{}) + errCh := make(chan error) + // start the parent driver. initComplete will be closed when the child connected to the parent. + logrus.Infof("starting parent driver") + go func() { + driverErr := driver.RunParentDriver(initComplete, quit, nil) + if driverErr != nil { + logrus.WithError(driverErr).Warn("parent driver exited") + } + errCh <- driverErr + }() + opaque := driver.OpaqueForChild() + logrus.Infof("opaque=%+v", opaque) + opaqueJSON, err := json.Marshal(opaque) + if err != nil { + return err + } + childQuitR, childQuitW, err := os.Pipe() + if err != nil { + return err + } + defer func() { + // stop the child + logrus.Info("stopping child driver") + if err := childQuitW.Close(); err != nil { + logrus.WithError(err).Warn("unable to close childQuitW") + } + }() + + // reexec the child process in the child netns + cmd := exec.Command(fmt.Sprintf("/proc/%d/exe", os.Getpid())) + cmd.Args = []string{reexecChildKey} + cmd.Stdin = childQuitR + cmd.Stdout = &logrusWriter{prefix: "child"} + cmd.Stderr = cmd.Stdout + cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON)) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGTERM, + } + childNS, err := ns.GetNS(cfg.NetNSPath) + if err != nil { + return err + } + if err := childNS.Do(func(_ ns.NetNS) error { + logrus.Infof("starting child driver in child netns (%q %v)", cmd.Path, cmd.Args) + return cmd.Start() + }); err != nil { + return err + } + + logrus.Info("waiting for initComplete") + // wait for the child to connect to the parent + select { + case <-initComplete: + logrus.Infof("initComplete is closed; parent and child established the communication channel") + case err := <-errCh: + return err + } + defer func() { + logrus.Info("stopping parent driver") + quit <- struct{}{} + if err := <-errCh; err != nil { + logrus.WithError(err).Warn("parent driver returned error on exit") + } + }() + + // let parent expose ports + logrus.Infof("exposing ports %v", cfg.Mappings) + if err := exposePorts(driver, cfg.Mappings); err != nil { + return err + } + + // write and close ReadyFD (convention is same as slirp4netns --ready-fd) + logrus.Info("ready") + if _, err := readyW.Write([]byte("1")); err != nil { + return err + } + if err := readyW.Close(); err != nil { + return err + } + + // wait for ExitFD to be closed + logrus.Info("waiting for exitfd to be closed") + if _, err := ioutil.ReadAll(exitR); err != nil { + return err + } + return nil +} + +func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping) error { + ctx := context.TODO() + for _, i := range portMappings { + hostIP := i.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + spec := rkport.Spec{ + Proto: i.Protocol, + ParentIP: hostIP, + ParentPort: int(i.HostPort), + ChildPort: int(i.ContainerPort), + } + if err := rkportutil.ValidatePortSpec(spec, nil); err != nil { + return err + } + if _, err := pm.AddPort(ctx, spec); err != nil { + return err + } + } + return nil +} + +func child() error { + // load the config from the parent + var opaque map[string]string + if err := json.Unmarshal([]byte(os.Getenv(reexecChildEnvOpaque)), &opaque); err != nil { + return err + } + + // start the child driver + quit := make(chan struct{}) + errCh := make(chan error) + go func() { + d := rkbuiltin.NewChildDriver(os.Stderr) + dErr := d.RunChildDriver(opaque, quit) + errCh <- dErr + }() + defer func() { + logrus.Info("stopping child driver") + quit <- struct{}{} + if err := <-errCh; err != nil { + logrus.WithError(err).Warn("child driver returned error on exit") + } + }() + + // wait for stdin to be closed + if _, err := ioutil.ReadAll(os.Stdin); err != nil { + return err + } + return nil +} + +type logrusWriter struct { + prefix string +} + +func (w *logrusWriter) Write(p []byte) (int, error) { + logrus.Infof("%s%s", w.prefix, string(p)) + return len(p), nil +} diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go index a45137416..8e95a3ca0 100644 --- a/pkg/spec/namespaces.go +++ b/pkg/spec/namespaces.go @@ -17,6 +17,7 @@ import ( "github.com/sirupsen/logrus" ) +// ToCreateOptions converts the input to a slice of container create options. func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) { var portBindings []ocicni.PortMapping var err error @@ -97,6 +98,8 @@ func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserCon return options, nil } +// ConfigureGenerator configures the generator based according to the current +// state of the NetworkConfig. func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error { netMode := c.NetMode if netMode.IsHost() { @@ -183,6 +186,7 @@ func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) { return portBindings, nil } +// ToCreateOptions converts the input to container create options. func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) if c.CgroupMode.IsNS() { @@ -213,6 +217,7 @@ func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCre return options, nil } +// ToCreateOptions converts the input to container create options. func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) if c.UsernsMode.IsNS() { @@ -241,6 +246,8 @@ func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreat return options, nil } +// ConfigureGenerator configures the generator according to the current state +// of the UserConfig. func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error { if IsNS(string(c.UsernsMode)) { if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil { @@ -271,11 +278,14 @@ func (c *UserConfig) getPostConfigureNetNS() bool { return postConfigureNetNS } +// InNS returns true if the UserConfig indicates to be in a dedicated user +// namespace. func (c *UserConfig) InNS(isRootless bool) bool { hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0 return isRootless || (hasUserns && !c.UsernsMode.IsHost()) } +// ToCreateOptions converts the input to container create options. func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) if c.IpcMode.IsHost() { @@ -293,6 +303,8 @@ func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreate return options, nil } +// ConfigureGenerator configures the generator according to the current state +// of the IpcConfig. func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error { ipcMode := c.IpcMode if IsNS(string(ipcMode)) { @@ -308,6 +320,8 @@ func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error { return nil } +// ConfigureGenerator configures the generator according to the current state +// of the CgroupConfig. func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error { cgroupMode := c.CgroupMode if cgroupMode.IsDefaultValue() { @@ -337,6 +351,7 @@ func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error { return nil } +// ToCreateOptions converts the input to container create options. func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) if c.PidMode.IsContainer() { @@ -351,6 +366,8 @@ func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreate return options, nil } +// ConfigureGenerator configures the generator according to the current state +// of the PidConfig. func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error { pidMode := c.PidMode if IsNS(string(pidMode)) { @@ -368,6 +385,7 @@ func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error { return nil } +// ToCreateOptions converts the input to container create options. func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) if IsPod(string(c.UtsMode)) { @@ -391,6 +409,8 @@ func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([ return options, nil } +// ConfigureGenerator configures the generator according to the current state +// of the UtsConfig. func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error { hostname := c.Hostname var err error diff --git a/pkg/spec/security.go b/pkg/spec/security.go index 05ed94e66..372fe87c6 100644 --- a/pkg/spec/security.go +++ b/pkg/spec/security.go @@ -11,6 +11,8 @@ import ( "github.com/pkg/errors" ) +// ToCreateOptions convert the SecurityConfig to a slice of container create +// options. func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) options = append(options, libpod.WithSecLabels(c.LabelOpts)) @@ -18,6 +20,8 @@ func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) { return options, nil } +// SetLabelOpts sets the label options of the SecurityConfig according to the +// input. func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error { if c.Privileged { c.LabelOpts = label.DisableSecOpt() @@ -57,6 +61,7 @@ func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidCon return nil } +// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.). func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error { for _, opt := range securityOpts { if opt == "no-new-privileges" { @@ -91,6 +96,7 @@ func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts [ return nil } +// ConfigureGenerator configures the generator according to the input. func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error { // HANDLE CAPABILITIES // NOTE: Must happen before SECCOMP diff --git a/pkg/util/utils.go b/pkg/util/utils.go index f7d04c73b..c9d09b8b5 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -304,7 +304,7 @@ func GetImageConfig(changes []string) (ImageConfig, error) { return config, nil } -// Parse and validate a signal name or number +// ParseSignal parses and validates a signal name or number. func ParseSignal(rawSignal string) (syscall.Signal, error) { // Strip off leading dash, to allow -1 or -HUP basename := strings.TrimPrefix(rawSignal, "-") diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index ac92343d0..bc644f87c 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -450,6 +450,18 @@ func (i *LibpodAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error return call.ReplyTagImage(newImage.ID()) } +// UntagImage accepts an image name and tag as strings and removes the tag from the local store. +func (i *LibpodAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) error { + newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) + if err != nil { + return call.ReplyImageNotFound(name, err.Error()) + } + if err := newImage.UntagImage(tag); err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + return call.ReplyUntagImage(newImage.ID()) +} + // RemoveImage accepts a image name or ID as a string and force bool to determine if it should // remove the image even if being used by stopped containers func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bool) error { diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index b81ff11ba..50aaaaa44 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -9,6 +9,7 @@ import ( goruntime "runtime" "time" + "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod/define" "github.com/sirupsen/logrus" @@ -37,9 +38,6 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { if err != nil { return err } - var ( - registries, insecureRegistries []string - ) podmanInfo := iopodman.PodmanInfo{} info, err := i.Runtime.Info() if err != nil { @@ -90,22 +88,25 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { Graph_status: graphStatus, } + // Registry information if any is stored as the second list item if len(info) > 2 { - registriesInterface := info[2].Data["registries"] - if registriesInterface != nil { - registries = registriesInterface.([]string) - } - } - if len(info) > 3 { - insecureRegistriesInterface := info[3].Data["registries"] - if insecureRegistriesInterface != nil { - insecureRegistries = insecureRegistriesInterface.([]string) + for key, val := range info[2].Data { + if key == "search" { + podmanInfo.Registries.Search = val.([]string) + continue + } + regData := val.(sysregistriesv2.Registry) + if regData.Insecure { + podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key) + } + if regData.Blocked { + podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key) + } } + } podmanInfo.Store = infoStore podmanInfo.Podman = pmaninfo - podmanInfo.Registries = registries - podmanInfo.Insecure_registries = insecureRegistries return call.ReplyGetInfo(podmanInfo) } diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go index dd171943f..d96e82a3f 100644 --- a/pkg/varlinkapi/virtwriter/virtwriter.go +++ b/pkg/varlinkapi/virtwriter/virtwriter.go @@ -27,13 +27,13 @@ const ( TerminalResize SocketDest = iota // Quit and detach Quit SocketDest = iota - // Quit from the client + // HangUpFromClient hangs up from the client HangUpFromClient SocketDest = iota ) -// ClientHangup signifies that the client wants to drop its -// connection from the server -var ClientHangup = errors.New("client hangup") +// ErrClientHangup signifies that the client wants to drop its connection from +// the server. +var ErrClientHangup = errors.New("client hangup") // IntToSocketDest returns a socketdest based on integer input func IntToSocketDest(i int) SocketDest { @@ -177,7 +177,7 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote // // reproducer: echo hello | (podman-remote run -i alpine cat) time.Sleep(1 * time.Second) - return ClientHangup + return ErrClientHangup default: // Something really went wrong return errors.New("unknown multiplex destination") diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go index d3b1b974e..d16661d5b 100644 --- a/test/e2e/info_test.go +++ b/test/e2e/info_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 29c60d7ac..89a5eddf4 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -358,10 +358,11 @@ var _ = Describe("Podman generate kube", func() { ctrAnnotation := "container.seccomp.security.alpha.kubernetes.io/" + defaultCtrName ctr := getCtr(withCmd([]string{"pwd"})) - err = generateKubeYaml(getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost:"+jsonFile)), kubeYaml) + err = generateKubeYaml(getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost/"+filepath.Base(jsonFile))), kubeYaml) Expect(err).To(BeNil()) - kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look + kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) @@ -378,13 +379,15 @@ var _ = Describe("Podman generate kube", func() { fmt.Println(err) Skip("Failed to prepare seccomp.json for test.") } + defer os.Remove(jsonFile) ctr := getCtr(withCmd([]string{"pwd"})) - err = generateKubeYaml(getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost:"+jsonFile)), kubeYaml) + err = generateKubeYaml(getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost/"+filepath.Base(jsonFile))), kubeYaml) Expect(err).To(BeNil()) - kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look + kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) diff --git a/test/e2e/untag_test.go b/test/e2e/untag_test.go new file mode 100644 index 000000000..17171cd41 --- /dev/null +++ b/test/e2e/untag_test.go @@ -0,0 +1,73 @@ +package integration + +import ( + "os" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman untag", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.RestoreAllArtifacts() + + for _, tag := range []string{"test", "foo", "bar"} { + session := podmanTest.PodmanNoCache([]string{"tag", ALPINE, tag}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + } + + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman untag all", func() { + session := podmanTest.PodmanNoCache([]string{"untag", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + results := podmanTest.PodmanNoCache([]string{"images", ALPINE}) + results.WaitWithDefaultTimeout() + Expect(results.ExitCode()).To(Equal(0)) + Expect(results.OutputToStringArray()).To(HaveLen(1)) + }) + + It("podman untag single", func() { + session := podmanTest.PodmanNoCache([]string{"untag", ALPINE, "localhost/test:latest"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + results := podmanTest.PodmanNoCache([]string{"images"}) + results.WaitWithDefaultTimeout() + Expect(results.ExitCode()).To(Equal(0)) + Expect(results.OutputToStringArray()).To(HaveLen(5)) + Expect(results.LineInOuputStartsWith("docker.io/library/alpine")).To(BeTrue()) + Expect(results.LineInOuputStartsWith("localhost/foo")).To(BeTrue()) + Expect(results.LineInOuputStartsWith("localhost/bar")).To(BeTrue()) + Expect(results.LineInOuputStartsWith("localhost/test")).To(BeFalse()) + }) + + It("podman untag not enough arguments", func() { + session := podmanTest.PodmanNoCache([]string{"untag"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).NotTo(Equal(0)) + }) +}) diff --git a/vendor/github.com/rootless-containers/rootlesskit/LICENSE b/vendor/github.com/rootless-containers/rootlesskit/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/msgutil/msgutil.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/msgutil/msgutil.go new file mode 100644 index 000000000..a0a0c94c6 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/msgutil/msgutil.go @@ -0,0 +1,66 @@ +// Package msgutil provides utility for JSON message with uint32le header +package msgutil + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "io" + + "github.com/pkg/errors" +) + +const ( + maxLength = 1 << 16 +) + +func MarshalToWriter(w io.Writer, x interface{}) (int, error) { + b, err := json.Marshal(x) + if err != nil { + return 0, err + } + if len(b) > maxLength { + return 0, errors.Errorf("bad message length: %d (max: %d)", len(b), maxLength) + } + h := make([]byte, 4) + binary.LittleEndian.PutUint32(h, uint32(len(b))) + return w.Write(append(h, b...)) +} + +func UnmarshalFromReader(r io.Reader, x interface{}) (int, error) { + hdr := make([]byte, 4) + n, err := r.Read(hdr) + if err != nil { + return n, err + } + if n != 4 { + return n, errors.Errorf("read %d bytes, expected 4 bytes", n) + } + bLen := binary.LittleEndian.Uint32(hdr) + if bLen > maxLength || bLen < 1 { + return n, errors.Errorf("bad message length: %d (max: %d)", bLen, maxLength) + } + b := make([]byte, bLen) + n, err = r.Read(b) + if err != nil { + return 4 + n, err + } + if n != int(bLen) { + return 4 + n, errors.Errorf("read %d bytes, expected %d bytes", n, bLen) + } + return 4 + n, json.Unmarshal(b, x) +} + +func Marshal(x interface{}) ([]byte, error) { + var b bytes.Buffer + _, err := MarshalToWriter(&b, x) + return b.Bytes(), err +} + +func Unmarshal(b []byte, x interface{}) error { + n, err := UnmarshalFromReader(bytes.NewReader(b), x) + if n != len(b) { + return errors.Errorf("read %d bytes, expected %d bytes", n, len(b)) + } + return err +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/builtin.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/builtin.go new file mode 100644 index 000000000..ca3f10b26 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/builtin.go @@ -0,0 +1,14 @@ +package builtin + +import ( + "io" + + "github.com/rootless-containers/rootlesskit/pkg/port" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/child" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent" +) + +var ( + NewParentDriver func(logWriter io.Writer, stateDir string) (port.ParentDriver, error) = parent.NewDriver + NewChildDriver func(logWriter io.Writer) port.ChildDriver = child.NewDriver +) diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/child/child.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/child/child.go new file mode 100644 index 000000000..5477dda51 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/child/child.go @@ -0,0 +1,134 @@ +package child + +import ( + "fmt" + "io" + "net" + "os" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" + + "github.com/rootless-containers/rootlesskit/pkg/msgutil" + "github.com/rootless-containers/rootlesskit/pkg/port" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg" + opaquepkg "github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque" +) + +func NewDriver(logWriter io.Writer) port.ChildDriver { + return &childDriver{ + logWriter: logWriter, + } +} + +type childDriver struct { + logWriter io.Writer +} + +func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan struct{}) error { + socketPath := opaque[opaquepkg.SocketPath] + if socketPath == "" { + return errors.New("socket path not set") + } + childReadyPipePath := opaque[opaquepkg.ChildReadyPipePath] + if childReadyPipePath == "" { + return errors.New("child ready pipe path not set") + } + childReadyPipeW, err := os.OpenFile(childReadyPipePath, os.O_WRONLY, os.ModeNamedPipe) + if err != nil { + return err + } + ln, err := net.ListenUnix("unix", &net.UnixAddr{ + Name: socketPath, + Net: "unix", + }) + if err != nil { + return err + } + // write nothing, just close + if err = childReadyPipeW.Close(); err != nil { + return err + } + stopAccept := make(chan struct{}, 1) + go func() { + <-quit + stopAccept <- struct{}{} + ln.Close() + }() + for { + c, err := ln.AcceptUnix() + if err != nil { + select { + case <-stopAccept: + return nil + default: + } + return err + } + go func() { + if rerr := d.routine(c); rerr != nil { + rep := msg.Reply{ + Error: rerr.Error(), + } + msgutil.MarshalToWriter(c, &rep) + } + c.Close() + }() + } + return nil +} + +func (d *childDriver) routine(c *net.UnixConn) error { + var req msg.Request + if _, err := msgutil.UnmarshalFromReader(c, &req); err != nil { + return err + } + switch req.Type { + case msg.RequestTypeInit: + return d.handleConnectInit(c, &req) + case msg.RequestTypeConnect: + return d.handleConnectRequest(c, &req) + default: + return errors.Errorf("unknown request type %q", req.Type) + } +} + +func (d *childDriver) handleConnectInit(c *net.UnixConn, req *msg.Request) error { + _, err := msgutil.MarshalToWriter(c, nil) + return err +} + +func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) error { + switch req.Proto { + case "tcp": + case "udp": + default: + return errors.Errorf("unknown proto: %q", req.Proto) + } + var dialer net.Dialer + targetConn, err := dialer.Dial(req.Proto, fmt.Sprintf("127.0.0.1:%d", req.Port)) + if err != nil { + return err + } + defer targetConn.Close() // no effect on duplicated FD + targetConnFiler, ok := targetConn.(filer) + if !ok { + return errors.Errorf("unknown target connection: %+v", targetConn) + } + targetConnFile, err := targetConnFiler.File() + if err != nil { + return err + } + oob := unix.UnixRights(int(targetConnFile.Fd())) + f, err := c.File() + if err != nil { + return err + } + err = unix.Sendmsg(int(f.Fd()), []byte("dummy"), oob, nil, 0) + return err +} + +// filer is implemented by *net.TCPConn and *net.UDPConn +type filer interface { + File() (f *os.File, err error) +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg/msg.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg/msg.go new file mode 100644 index 000000000..c603f473a --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg/msg.go @@ -0,0 +1,129 @@ +package msg + +import ( + "net" + "time" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" + + "github.com/rootless-containers/rootlesskit/pkg/msgutil" + "github.com/rootless-containers/rootlesskit/pkg/port" +) + +const ( + RequestTypeInit = "init" + RequestTypeConnect = "connect" +) + +// Request and Response are encoded as JSON with uint32le length header. +type Request struct { + Type string // "init" or "connect" + Proto string // "tcp" or "udp" + Port int +} + +// Reply may contain FD as OOB +type Reply struct { + Error string +} + +// Initiate sends "init" request to the child UNIX socket. +func Initiate(c *net.UnixConn) error { + req := Request{ + Type: RequestTypeInit, + } + if _, err := msgutil.MarshalToWriter(c, &req); err != nil { + return err + } + if err := c.CloseWrite(); err != nil { + return err + } + var rep Reply + if _, err := msgutil.UnmarshalFromReader(c, &rep); err != nil { + return err + } + return c.CloseRead() +} + +// ConnectToChild connects to the child UNIX socket, and obtains TCP or UDP socket FD +// that corresponds to the port spec. +func ConnectToChild(c *net.UnixConn, spec port.Spec) (int, error) { + req := Request{ + Type: RequestTypeConnect, + Proto: spec.Proto, + Port: spec.ChildPort, + } + if _, err := msgutil.MarshalToWriter(c, &req); err != nil { + return 0, err + } + if err := c.CloseWrite(); err != nil { + return 0, err + } + oobSpace := unix.CmsgSpace(4) + oob := make([]byte, oobSpace) + _, oobN, _, _, err := c.ReadMsgUnix(nil, oob) + if err != nil { + return 0, err + } + if oobN != oobSpace { + return 0, errors.Errorf("expected OOB space %d, got %d", oobSpace, oobN) + } + oob = oob[:oobN] + fd, err := parseFDFromOOB(oob) + if err != nil { + return 0, err + } + if err := c.CloseRead(); err != nil { + return 0, err + } + return fd, nil +} + +// ConnectToChildWithSocketPath wraps ConnectToChild +func ConnectToChildWithSocketPath(socketPath string, spec port.Spec) (int, error) { + var dialer net.Dialer + conn, err := dialer.Dial("unix", socketPath) + if err != nil { + return 0, err + } + defer conn.Close() + c := conn.(*net.UnixConn) + return ConnectToChild(c, spec) +} + +// ConnectToChildWithRetry retries ConnectToChild every (i*5) milliseconds. +func ConnectToChildWithRetry(socketPath string, spec port.Spec, retries int) (int, error) { + for i := 0; i < retries; i++ { + fd, err := ConnectToChildWithSocketPath(socketPath, spec) + if i == retries-1 && err != nil { + return 0, err + } + if err == nil { + return fd, err + } + // TODO: backoff + time.Sleep(time.Duration(i*5) * time.Millisecond) + } + // NOT REACHED + return 0, errors.New("reached max retry") +} + +func parseFDFromOOB(oob []byte) (int, error) { + scms, err := unix.ParseSocketControlMessage(oob) + if err != nil { + return 0, err + } + if len(scms) != 1 { + return 0, errors.Errorf("unexpected scms: %v", scms) + } + scm := scms[0] + fds, err := unix.ParseUnixRights(&scm) + if err != nil { + return 0, err + } + if len(fds) != 1 { + return 0, errors.Errorf("unexpected fds: %v", fds) + } + return fds[0], nil +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque/opaque.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque/opaque.go new file mode 100644 index 000000000..391b3d340 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque/opaque.go @@ -0,0 +1,6 @@ +package opaque + +const ( + SocketPath = "builtin.socketpath" + ChildReadyPipePath = "builtin.readypipepath" +) diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go new file mode 100644 index 000000000..893bf1da9 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go @@ -0,0 +1,145 @@ +package parent + +import ( + "context" + "io" + "io/ioutil" + "net" + "os" + "path/filepath" + "sync" + "syscall" + + "github.com/pkg/errors" + + "github.com/rootless-containers/rootlesskit/pkg/port" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp" + "github.com/rootless-containers/rootlesskit/pkg/port/portutil" +) + +// NewDriver for builtin driver. +func NewDriver(logWriter io.Writer, stateDir string) (port.ParentDriver, error) { + // TODO: consider using socketpair FD instead of socket file + socketPath := filepath.Join(stateDir, ".bp.sock") + childReadyPipePath := filepath.Join(stateDir, ".bp-ready.pipe") + // remove the path just in case the previous rootlesskit instance crashed + if err := os.RemoveAll(childReadyPipePath); err != nil { + return nil, errors.Wrapf(err, "cannot remove %s", childReadyPipePath) + } + if err := syscall.Mkfifo(childReadyPipePath, 0600); err != nil { + return nil, errors.Wrapf(err, "cannot mkfifo %s", childReadyPipePath) + } + d := driver{ + logWriter: logWriter, + socketPath: socketPath, + childReadyPipePath: childReadyPipePath, + ports: make(map[int]*port.Status, 0), + stoppers: make(map[int]func() error, 0), + nextID: 1, + } + return &d, nil +} + +type driver struct { + logWriter io.Writer + socketPath string + childReadyPipePath string + mu sync.Mutex + ports map[int]*port.Status + stoppers map[int]func() error + nextID int +} + +func (d *driver) OpaqueForChild() map[string]string { + return map[string]string{ + opaque.SocketPath: d.socketPath, + opaque.ChildReadyPipePath: d.childReadyPipePath, + } +} + +func (d *driver) RunParentDriver(initComplete chan struct{}, quit <-chan struct{}, _ *port.ChildContext) error { + childReadyPipeR, err := os.OpenFile(d.childReadyPipePath, os.O_RDONLY, os.ModeNamedPipe) + if err != nil { + return err + } + if _, err = ioutil.ReadAll(childReadyPipeR); err != nil { + return err + } + childReadyPipeR.Close() + var dialer net.Dialer + conn, err := dialer.Dial("unix", d.socketPath) + if err != nil { + return err + } + err = msg.Initiate(conn.(*net.UnixConn)) + conn.Close() + if err != nil { + return err + } + initComplete <- struct{}{} + <-quit + return nil +} + +func (d *driver) AddPort(ctx context.Context, spec port.Spec) (*port.Status, error) { + d.mu.Lock() + err := portutil.ValidatePortSpec(spec, d.ports) + d.mu.Unlock() + if err != nil { + return nil, err + } + routineStopCh := make(chan struct{}) + routineStop := func() error { + close(routineStopCh) + return nil // FIXME + } + switch spec.Proto { + case "tcp": + err = tcp.Run(d.socketPath, spec, routineStopCh, d.logWriter) + case "udp": + err = udp.Run(d.socketPath, spec, routineStopCh, d.logWriter) + default: + // NOTREACHED + return nil, errors.New("spec was not validated?") + } + if err != nil { + return nil, err + } + d.mu.Lock() + id := d.nextID + st := port.Status{ + ID: id, + Spec: spec, + } + d.ports[id] = &st + d.stoppers[id] = routineStop + d.nextID++ + d.mu.Unlock() + return &st, nil +} + +func (d *driver) ListPorts(ctx context.Context) ([]port.Status, error) { + var ports []port.Status + d.mu.Lock() + for _, p := range d.ports { + ports = append(ports, *p) + } + d.mu.Unlock() + return ports, nil +} + +func (d *driver) RemovePort(ctx context.Context, id int) error { + d.mu.Lock() + defer d.mu.Unlock() + stop, ok := d.stoppers[id] + if !ok { + return errors.Errorf("unknown id: %d", id) + } + err := stop() + delete(d.stoppers, id) + delete(d.ports, id) + return err +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go new file mode 100644 index 000000000..b9f2d1802 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp/tcp.go @@ -0,0 +1,104 @@ +package tcp + +import ( + "fmt" + "io" + "net" + "os" + "sync" + + "github.com/rootless-containers/rootlesskit/pkg/port" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg" +) + +func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error { + ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort)) + if err != nil { + fmt.Fprintf(logWriter, "listen: %v\n", err) + return err + } + newConns := make(chan net.Conn) + go func() { + for { + c, err := ln.Accept() + if err != nil { + fmt.Fprintf(logWriter, "accept: %v\n", err) + close(newConns) + return + } + newConns <- c + } + }() + go func() { + defer ln.Close() + for { + select { + case c, ok := <-newConns: + if !ok { + return + } + go func() { + if err := copyConnToChild(c, socketPath, spec, stopCh); err != nil { + fmt.Fprintf(logWriter, "copyConnToChild: %v\n", err) + return + } + }() + case <-stopCh: + return + } + } + }() + // no wait + return nil +} + +func copyConnToChild(c net.Conn, socketPath string, spec port.Spec, stopCh <-chan struct{}) error { + defer c.Close() + // get fd from the child as an SCM_RIGHTS cmsg + fd, err := msg.ConnectToChildWithRetry(socketPath, spec, 10) + if err != nil { + return err + } + f := os.NewFile(uintptr(fd), "") + defer f.Close() + fc, err := net.FileConn(f) + if err != nil { + return err + } + defer fc.Close() + bicopy(c, fc, stopCh) + return nil +} + +// bicopy is based on libnetwork/cmd/proxy/tcp_proxy.go . +// NOTE: sendfile(2) cannot be used for sockets +func bicopy(x, y net.Conn, quit <-chan struct{}) { + var wg sync.WaitGroup + var broker = func(to, from net.Conn) { + io.Copy(to, from) + if fromTCP, ok := from.(*net.TCPConn); ok { + fromTCP.CloseRead() + } + if toTCP, ok := to.(*net.TCPConn); ok { + toTCP.CloseWrite() + } + wg.Done() + } + + wg.Add(2) + go broker(x, y) + go broker(y, x) + finish := make(chan struct{}) + go func() { + wg.Wait() + close(finish) + }() + + select { + case <-quit: + case <-finish: + } + x.Close() + y.Close() + <-finish +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go new file mode 100644 index 000000000..d8f646b5d --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udp.go @@ -0,0 +1,60 @@ +package udp + +import ( + "fmt" + "io" + "net" + "os" + + "github.com/pkg/errors" + + "github.com/rootless-containers/rootlesskit/pkg/port" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg" + "github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy" +) + +func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, logWriter io.Writer) error { + addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", spec.ParentIP, spec.ParentPort)) + if err != nil { + return err + } + c, err := net.ListenUDP("udp", addr) + if err != nil { + return err + } + udpp := &udpproxy.UDPProxy{ + LogWriter: logWriter, + Listener: c, + BackendDial: func() (*net.UDPConn, error) { + // get fd from the child as an SCM_RIGHTS cmsg + fd, err := msg.ConnectToChildWithRetry(socketPath, spec, 10) + if err != nil { + return nil, err + } + f := os.NewFile(uintptr(fd), "") + defer f.Close() + fc, err := net.FileConn(f) + if err != nil { + return nil, err + } + uc, ok := fc.(*net.UDPConn) + if !ok { + return nil, errors.Errorf("file conn doesn't implement *net.UDPConn: %+v", fc) + } + return uc, nil + }, + } + go udpp.Run() + go func() { + for { + select { + case <-stopCh: + // udpp.Close closes ln as well + udpp.Close() + return + } + } + }() + // no wait + return nil +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy/udp_proxy.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy/udp_proxy.go new file mode 100644 index 000000000..af7b7d5d9 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy/udp_proxy.go @@ -0,0 +1,150 @@ +// Package udpproxy is from https://raw.githubusercontent.com/docker/libnetwork/fec6476dfa21380bf8ee4d74048515d968c1ee63/cmd/proxy/udp_proxy.go +package udpproxy + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "strings" + "sync" + "syscall" + "time" +) + +const ( + // UDPConnTrackTimeout is the timeout used for UDP connection tracking + UDPConnTrackTimeout = 90 * time.Second + // UDPBufSize is the buffer size for the UDP proxy + UDPBufSize = 65507 +) + +// A net.Addr where the IP is split into two fields so you can use it as a key +// in a map: +type connTrackKey struct { + IPHigh uint64 + IPLow uint64 + Port int +} + +func newConnTrackKey(addr *net.UDPAddr) *connTrackKey { + if len(addr.IP) == net.IPv4len { + return &connTrackKey{ + IPHigh: 0, + IPLow: uint64(binary.BigEndian.Uint32(addr.IP)), + Port: addr.Port, + } + } + return &connTrackKey{ + IPHigh: binary.BigEndian.Uint64(addr.IP[:8]), + IPLow: binary.BigEndian.Uint64(addr.IP[8:]), + Port: addr.Port, + } +} + +type connTrackMap map[connTrackKey]*net.UDPConn + +// UDPProxy is proxy for which handles UDP datagrams. +// From libnetwork udp_proxy.go . +type UDPProxy struct { + LogWriter io.Writer + Listener *net.UDPConn + BackendDial func() (*net.UDPConn, error) + connTrackTable connTrackMap + connTrackLock sync.Mutex +} + +func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) { + defer func() { + proxy.connTrackLock.Lock() + delete(proxy.connTrackTable, *clientKey) + proxy.connTrackLock.Unlock() + proxyConn.Close() + }() + + readBuf := make([]byte, UDPBufSize) + for { + proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout)) + again: + read, err := proxyConn.Read(readBuf) + if err != nil { + if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED { + // This will happen if the last write failed + // (e.g: nothing is actually listening on the + // proxied port on the container), ignore it + // and continue until UDPConnTrackTimeout + // expires: + goto again + } + return + } + for i := 0; i != read; { + written, err := proxy.Listener.WriteToUDP(readBuf[i:read], clientAddr) + if err != nil { + return + } + i += written + } + } +} + +// Run starts forwarding the traffic using UDP. +func (proxy *UDPProxy) Run() { + proxy.connTrackTable = make(connTrackMap) + readBuf := make([]byte, UDPBufSize) + for { + read, from, err := proxy.Listener.ReadFromUDP(readBuf) + if err != nil { + // NOTE: Apparently ReadFrom doesn't return + // ECONNREFUSED like Read do (see comment in + // UDPProxy.replyLoop) + if !isClosedError(err) { + fmt.Fprintf(proxy.LogWriter, "Stopping proxy on udp: %v\n", err) + } + break + } + + fromKey := newConnTrackKey(from) + proxy.connTrackLock.Lock() + proxyConn, hit := proxy.connTrackTable[*fromKey] + if !hit { + proxyConn, err = proxy.BackendDial() + if err != nil { + fmt.Fprintf(proxy.LogWriter, "Can't proxy a datagram to udp: %v\n", err) + proxy.connTrackLock.Unlock() + continue + } + proxy.connTrackTable[*fromKey] = proxyConn + go proxy.replyLoop(proxyConn, from, fromKey) + } + proxy.connTrackLock.Unlock() + for i := 0; i != read; { + written, err := proxyConn.Write(readBuf[i:read]) + if err != nil { + fmt.Fprintf(proxy.LogWriter, "Can't proxy a datagram to udp: %v\n", err) + break + } + i += written + } + } +} + +// Close stops forwarding the traffic. +func (proxy *UDPProxy) Close() { + proxy.Listener.Close() + proxy.connTrackLock.Lock() + defer proxy.connTrackLock.Unlock() + for _, conn := range proxy.connTrackTable { + conn.Close() + } +} + +func isClosedError(err error) bool { + /* This comparison is ugly, but unfortunately, net.go doesn't export errClosing. + * See: + * http://golang.org/src/pkg/net/net.go + * https://code.google.com/p/go/issues/detail?id=4337 + * https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ + */ + return strings.HasSuffix(err.Error(), "use of closed network connection") +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/port.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/port.go new file mode 100644 index 000000000..9ef46f549 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/port.go @@ -0,0 +1,51 @@ +package port + +import ( + "context" + "net" +) + +type Spec struct { + Proto string `json:"proto,omitempty"` // either "tcp" or "udp". in future "sctp" will be supported as well. + ParentIP string `json:"parentIP,omitempty"` // IPv4 address. can be empty (0.0.0.0). + ParentPort int `json:"parentPort,omitempty"` + ChildPort int `json:"childPort,omitempty"` +} + +type Status struct { + ID int `json:"id"` + Spec Spec `json:"spec"` +} + +// Manager MUST be thread-safe. +type Manager interface { + AddPort(ctx context.Context, spec Spec) (*Status, error) + ListPorts(ctx context.Context) ([]Status, error) + RemovePort(ctx context.Context, id int) error +} + +// ChildContext is used for RunParentDriver +type ChildContext struct { + // PID of the child, can be used for ns-entering to the child namespaces. + PID int + // IP of the tap device + IP net.IP +} + +// ParentDriver is a driver for the parent process. +type ParentDriver interface { + Manager + // OpaqueForChild typically consists of socket path + // for controlling child from parent + OpaqueForChild() map[string]string + // RunParentDriver signals initComplete when ParentDriver is ready to + // serve as Manager. + // RunParentDriver blocks until quit is signaled. + // + // ChildContext is optional. + RunParentDriver(initComplete chan struct{}, quit <-chan struct{}, cctx *ChildContext) error +} + +type ChildDriver interface { + RunChildDriver(opaque map[string]string, quit <-chan struct{}) error +} diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go new file mode 100644 index 000000000..f1aa5f859 --- /dev/null +++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/portutil/portutil.go @@ -0,0 +1,67 @@ +package portutil + +import ( + "net" + "regexp" + "strconv" + + "github.com/pkg/errors" + + "github.com/rootless-containers/rootlesskit/pkg/port" +) + +// ParsePortSpec parses a Docker-like representation of PortSpec. +// e.g. "127.0.0.1:8080:80/tcp" +func ParsePortSpec(s string) (*port.Spec, error) { + r := regexp.MustCompile("^([0-9a-f\\.]+):([0-9]+):([0-9]+)/([a-z]+)$") + g := r.FindStringSubmatch(s) + if len(g) != 5 { + return nil, errors.Errorf("unexpected PortSpec string: %q", s) + } + parentIP := g[1] + parentPort, err := strconv.Atoi(g[2]) + if err != nil { + return nil, errors.Wrapf(err, "unexpected ParentPort in PortSpec string: %q", s) + } + childPort, err := strconv.Atoi(g[3]) + if err != nil { + return nil, errors.Wrapf(err, "unexpected ChildPort in PortSpec string: %q", s) + } + proto := g[4] + // validation is up to the caller (as json.Unmarshal doesn't validate values) + return &port.Spec{ + Proto: proto, + ParentIP: parentIP, + ParentPort: parentPort, + ChildPort: childPort, + }, nil +} + +// ValidatePortSpec validates *port.Spec. +// existingPorts can be optionally passed for detecting conflicts. +func ValidatePortSpec(spec port.Spec, existingPorts map[int]*port.Status) error { + if spec.Proto != "tcp" && spec.Proto != "udp" { + return errors.Errorf("unknown proto: %q", spec.Proto) + } + if spec.ParentIP != "" { + if net.ParseIP(spec.ParentIP) == nil { + return errors.Errorf("invalid ParentIP: %q", spec.ParentIP) + } + } + if spec.ParentPort <= 0 || spec.ParentPort > 65535 { + return errors.Errorf("invalid ParentPort: %q", spec.ParentPort) + } + if spec.ChildPort <= 0 || spec.ChildPort > 65535 { + return errors.Errorf("invalid ChildPort: %q", spec.ChildPort) + } + for id, p := range existingPorts { + sp := p.Spec + sameProto := sp.Proto == spec.Proto + sameParent := sp.ParentIP == spec.ParentIP && sp.ParentPort == spec.ParentPort + sameChild := sp.ChildPort == spec.ChildPort + if sameProto && (sameParent || sameChild) { + return errors.Errorf("conflict with ID %d", id) + } + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e1154a7d7..5a2d4ab81 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -442,6 +442,18 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util +# github.com/rootless-containers/rootlesskit v0.7.1 +github.com/rootless-containers/rootlesskit/pkg/msgutil +github.com/rootless-containers/rootlesskit/pkg/port +github.com/rootless-containers/rootlesskit/pkg/port/builtin +github.com/rootless-containers/rootlesskit/pkg/port/builtin/child +github.com/rootless-containers/rootlesskit/pkg/port/builtin/msg +github.com/rootless-containers/rootlesskit/pkg/port/builtin/opaque +github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent +github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp +github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp +github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy +github.com/rootless-containers/rootlesskit/pkg/port/portutil # github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 github.com/safchain/ethtool # github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f |