summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml7
-rw-r--r--RELEASE_NOTES.md3
-rw-r--r--cmd/podman/containers/ps.go4
-rw-r--r--cmd/podman/images/build.go7
-rw-r--r--cmd/podman/inspect/inspect.go12
-rw-r--r--cmd/podman/machine/init.go4
-rw-r--r--cmd/podman/machine/list.go2
-rw-r--r--cmd/podman/machine/rm.go2
-rw-r--r--cmd/podman/machine/ssh.go6
-rw-r--r--cmd/podman/machine/start.go2
-rw-r--r--cmd/podman/machine/stop.go2
-rw-r--r--cmd/podman/root.go8
-rwxr-xr-xcontrib/cirrus/runner.sh25
-rw-r--r--contrib/rootless-cni-infra/Containerfile36
-rw-r--r--contrib/rootless-cni-infra/README.md25
-rwxr-xr-xcontrib/rootless-cni-infra/rootless-cni-infra181
-rw-r--r--docs/source/markdown/podman-create.1.md34
-rw-r--r--docs/source/markdown/podman-manifest-add.1.md16
-rw-r--r--docs/source/markdown/podman-manifest-push.1.md36
-rw-r--r--docs/source/markdown/podman-run.1.md18
-rw-r--r--docs/source/markdown/podman.1.md2
-rw-r--r--go.mod4
-rw-r--r--go.sum9
-rw-r--r--libpod/container_internal.go4
-rw-r--r--libpod/container_internal_linux.go16
-rw-r--r--libpod/network/create.go6
-rw-r--r--libpod/networking_linux.go934
-rw-r--r--libpod/networking_slirp4netns.go538
-rw-r--r--libpod/options.go3
-rw-r--r--libpod/rootless_cni_linux.go372
-rw-r--r--libpod/runtime.go10
-rw-r--r--libpod/runtime_pod_infra_linux.go2
-rw-r--r--pkg/api/handlers/compat/images_build.go202
-rw-r--r--pkg/bindings/images/build.go32
-rw-r--r--pkg/domain/infra/runtime_libpod.go6
-rw-r--r--pkg/netns/netns_linux.go26
-rw-r--r--pkg/rootless/rootless_linux.c58
-rw-r--r--pkg/specgen/generate/namespaces.go3
-rw-r--r--pkg/specgen/generate/pod_create.go16
-rw-r--r--test/apiv2/35-networks.at23
-rw-r--r--test/compose/mount_and_label/tests.sh3
-rwxr-xr-xtest/compose/test-compose52
-rw-r--r--test/compose/two_networks/Readme.md8
-rw-r--r--test/compose/two_networks/docker-compose.yml11
-rw-r--r--test/compose/two_networks/tests.sh7
-rw-r--r--test/e2e/network_connect_disconnect_test.go25
-rw-r--r--test/e2e/run_networking_test.go22
-rw-r--r--test/system/001-basic.bats7
-rw-r--r--test/system/005-info.bats9
-rw-r--r--test/system/070-build.bats45
-rw-r--r--test/system/270-socket-activation.bats103
-rw-r--r--test/system/410-selinux.bats28
-rw-r--r--test/system/500-networking.bats7
-rw-r--r--vendor/github.com/go-task/slim-sprig/.editorconfig14
-rw-r--r--vendor/github.com/go-task/slim-sprig/.gitattributes1
-rw-r--r--vendor/github.com/go-task/slim-sprig/.gitignore2
-rw-r--r--vendor/github.com/go-task/slim-sprig/CHANGELOG.md364
-rw-r--r--vendor/github.com/go-task/slim-sprig/LICENSE.txt19
-rw-r--r--vendor/github.com/go-task/slim-sprig/README.md73
-rw-r--r--vendor/github.com/go-task/slim-sprig/Taskfile.yml12
-rw-r--r--vendor/github.com/go-task/slim-sprig/crypto.go24
-rw-r--r--vendor/github.com/go-task/slim-sprig/date.go152
-rw-r--r--vendor/github.com/go-task/slim-sprig/defaults.go163
-rw-r--r--vendor/github.com/go-task/slim-sprig/dict.go118
-rw-r--r--vendor/github.com/go-task/slim-sprig/doc.go19
-rw-r--r--vendor/github.com/go-task/slim-sprig/functions.go317
-rw-r--r--vendor/github.com/go-task/slim-sprig/go.mod8
-rw-r--r--vendor/github.com/go-task/slim-sprig/go.sum22
-rw-r--r--vendor/github.com/go-task/slim-sprig/list.go464
-rw-r--r--vendor/github.com/go-task/slim-sprig/network.go12
-rw-r--r--vendor/github.com/go-task/slim-sprig/numeric.go228
-rw-r--r--vendor/github.com/go-task/slim-sprig/reflect.go28
-rw-r--r--vendor/github.com/go-task/slim-sprig/regex.go83
-rw-r--r--vendor/github.com/go-task/slim-sprig/strings.go189
-rw-r--r--vendor/github.com/go-task/slim-sprig/url.go66
-rw-r--r--vendor/github.com/onsi/ginkgo/CHANGELOG.md13
-rw-r--r--vendor/github.com/onsi/ginkgo/README.md12
-rw-r--r--vendor/github.com/onsi/ginkgo/config/config.go2
-rw-r--r--vendor/github.com/onsi/ginkgo/formatter/formatter.go190
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go3
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go6
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go4
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo/run_command.go21
-rw-r--r--vendor/github.com/onsi/ginkgo/ginkgo_dsl.go49
-rw-r--r--vendor/github.com/onsi/ginkgo/go.mod1
-rw-r--r--vendor/github.com/onsi/ginkgo/go.sum11
-rw-r--r--vendor/github.com/onsi/ginkgo/types/deprecation_support.go96
-rw-r--r--vendor/github.com/rootless-containers/rootlesskit/pkg/api/api.go13
-rw-r--r--vendor/github.com/rootless-containers/rootlesskit/pkg/api/openapi.yaml12
-rw-r--r--vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go5
-rw-r--r--vendor/modules.txt7
91 files changed, 4456 insertions, 1390 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index e09db2a81..074f2f4e0 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -440,7 +440,7 @@ apiv2_test_task:
time_script: '$SCRIPT_BASE/logcollector.sh time'
compose_test_task:
- name: "compose test on $DISTRO_NV"
+ name: "compose test on $DISTRO_NV ($PRIV_NAME)"
alias: compose_test
only_if: *not_docs
skip: *tags
@@ -450,6 +450,11 @@ compose_test_task:
env:
<<: *stdenvars
TEST_FLAVOR: compose
+ matrix:
+ - env:
+ PRIV_NAME: root
+ - env:
+ PRIV_NAME: rootless
clone_script: *noop # Comes from cache
gopath_cache: *ro_gopath_cache
setup_script: *setup
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 85daba722..5ba5e251b 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -17,6 +17,9 @@
- The `podman generate kube` command can now generate `PersistentVolumeClaim` YAML for Podman named volumes ([#5788](https://github.com/containers/podman/issues/5788)).
- The `podman generate kube` command can now generate YAML files containing multiple resources (pods or deployments) ([#9129](https://github.com/containers/podman/issues/9129)).
+### Security
+- This release resolves CVE-2021-20291, a deadlock vulnerability in the storage library caused by pulling a specially-crafted container image.
+
### Changes
- The Podman remote client's `podman build` command no longer allows the `-v` flag to be used. Volumes are not yet supported with remote Podman when the client and service are on different machines.
- The `podman kill` and `podman stop` commands now print the name given by the user for each container, instead of the full ID.
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index 6b125fede..70bfd3574 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -232,7 +232,9 @@ func ps(cmd *cobra.Command, _ []string) error {
ns := strings.NewReplacer(".Namespaces.", ".")
format = ns.Replace(format)
- tmpl, err := template.New("listContainers").Parse(format)
+ tmpl, err := template.New("listContainers").
+ Funcs(template.FuncMap(report.DefaultFuncs)).
+ Parse(format)
if err != nil {
return err
}
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index da7f5d862..04fdeab0a 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -11,6 +11,7 @@ import (
buildahDefine "github.com/containers/buildah/define"
buildahCLI "github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/parse"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
encconfig "github.com/containers/ocicrypt/config"
@@ -330,6 +331,12 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
pullPolicy = buildahDefine.PullNever
}
+ if c.Flag("authfile").Changed {
+ if err := auth.CheckAuthFile(flags.Authfile); err != nil {
+ return nil, err
+ }
+ }
+
args := make(map[string]string)
if c.Flag("build-arg").Changed {
for _, arg := range flags.BuildArg {
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index 09721a2a8..351684af1 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -1,7 +1,6 @@
package inspect
import (
- "bytes"
"context"
"encoding/json" // due to a bug in json-iterator it cannot be used here
"fmt"
@@ -246,15 +245,8 @@ func printJSON(data []interface{}) error {
}
func printTmpl(typ, row string, data []interface{}) error {
- t, err := template.New(typ + " inspect").Funcs(map[string]interface{}{
- "json": func(v interface{}) string {
- b := &bytes.Buffer{}
- e := registry.JSONLibrary().NewEncoder(b)
- e.SetEscapeHTML(false)
- _ = e.Encode(v)
- return strings.TrimSpace(b.String())
- },
- }).Parse(row)
+ // We cannot use c/common/reports here, too many levels of interface{}
+ t, err := template.New(typ + " inspect").Funcs(template.FuncMap(report.DefaultFuncs)).Parse(row)
if err != nil {
return err
}
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index 61261e008..02dfc80aa 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -15,8 +15,8 @@ import (
var (
initCmd = &cobra.Command{
Use: "init [options] [NAME]",
- Short: "initialize a vm",
- Long: "initialize a virtual machine for Podman to run on. Virtual machines are used to run Podman.",
+ Short: "Initialize a virtual machine",
+ Long: "initialize a virtual machine ",
RunE: initMachine,
Args: cobra.MaximumNArgs(1),
Example: `podman machine init myvm`,
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 3c8368c6b..ce4129e87 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -28,7 +28,7 @@ var (
Use: "list [options]",
Aliases: []string{"ls"},
Short: "List machines",
- Long: "List Podman managed virtual machines.",
+ Long: "List managed virtual machines.",
RunE: list,
Args: validate.NoArgs,
Example: `podman machine list,
diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go
index e05b5f7a8..0be2ba40c 100644
--- a/cmd/podman/machine/rm.go
+++ b/cmd/podman/machine/rm.go
@@ -19,7 +19,7 @@ var (
rmCmd = &cobra.Command{
Use: "rm [options] [MACHINE]",
Short: "Remove an existing machine",
- Long: "Remove an existing machine ",
+ Long: "Remove a managed virtual machine ",
RunE: rm,
Args: cobra.MaximumNArgs(1),
Example: `podman machine rm myvm`,
diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go
index fc7c71992..504fcbe46 100644
--- a/cmd/podman/machine/ssh.go
+++ b/cmd/podman/machine/ssh.go
@@ -14,11 +14,11 @@ import (
var (
sshCmd = &cobra.Command{
Use: "ssh [NAME] [COMMAND [ARG ...]]",
- Short: "SSH into a virtual machine",
- Long: "SSH into a virtual machine ",
+ Short: "SSH into an existing machine",
+ Long: "SSH into a managed virtual machine ",
RunE: ssh,
Example: `podman machine ssh myvm
- podman machine ssh -e myvm echo hello`,
+ podman machine ssh myvm echo hello`,
ValidArgsFunction: autocompleteMachineSSH,
}
)
diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go
index 959fa21d2..d06e04f41 100644
--- a/cmd/podman/machine/start.go
+++ b/cmd/podman/machine/start.go
@@ -14,7 +14,7 @@ var (
startCmd = &cobra.Command{
Use: "start [MACHINE]",
Short: "Start an existing machine",
- Long: "Start an existing machine ",
+ Long: "Start a managed virtual machine ",
RunE: start,
Args: cobra.MaximumNArgs(1),
Example: `podman machine start myvm`,
diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go
index 36b434d8e..4235b64f1 100644
--- a/cmd/podman/machine/stop.go
+++ b/cmd/podman/machine/stop.go
@@ -14,7 +14,7 @@ var (
stopCmd = &cobra.Command{
Use: "stop [MACHINE]",
Short: "Stop an existing machine",
- Long: "Stop an existing machine ",
+ Long: "Stop a managed virtual machine ",
RunE: stop,
Args: cobra.MaximumNArgs(1),
Example: `podman machine stop myvm`,
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 2b77afbeb..4527c2646 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -180,6 +180,10 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
os.Setenv("TMPDIR", "/var/tmp")
}
+ context := cmd.Root().LocalFlags().Lookup("context")
+ if context.Value.String() != "default" {
+ return errors.New("Podman does not support swarm, the only --context value allowed is \"default\"")
+ }
if !registry.IsRemote() {
if cmd.Flag("cpu-profile").Changed {
f, err := os.Create(cfg.CPUProfile)
@@ -269,6 +273,10 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
lFlags.StringVar(&opts.URI, urlFlagName, uri, "URL to access Podman service (CONTAINER_HOST)")
_ = cmd.RegisterFlagCompletionFunc(urlFlagName, completion.AutocompleteDefault)
+ // Context option added just for compatibility with DockerCLI.
+ lFlags.String("context", "default", "Name of the context to use to connect to the daemon (This flag is a NOOP and provided solely for scripting compatibility.)")
+ _ = lFlags.MarkHidden("context")
+
identityFlagName := "identity"
lFlags.StringVar(&opts.Identity, identityFlagName, ident, "path to SSH identity file, (CONTAINER_SSHKEY)")
_ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault)
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index 507d22e13..f52e107cc 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -288,18 +288,6 @@ dotest() {
exec_container # does not return
fi;
- # shellcheck disable=SC2154
- if [[ "$PRIV_NAME" == "rootless" ]] && [[ "$UID" -eq 0 ]]; then
- req_env_vars ROOTLESS_USER
- msg "Re-executing runner through ssh as user '$ROOTLESS_USER'"
- msg "************************************************************"
- set -x
- exec ssh $ROOTLESS_USER@localhost \
- -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
- -o CheckHostIP=no $GOSRC/$SCRIPT_BASE/runner.sh
- # does not return
- fi
-
# containers/automation sets this to 0 for its dbg() function
# but the e2e integration tests are also sensitive to it.
unset DEBUG
@@ -340,6 +328,19 @@ msg "************************************************************"
((${SETUP_ENVIRONMENT:-0})) || \
die "Expecting setup_environment.sh to have completed successfully"
+# shellcheck disable=SC2154
+if [[ "$PRIV_NAME" == "rootless" ]] && [[ "$UID" -eq 0 ]]; then
+ req_env_vars ROOTLESS_USER
+ msg "Re-executing runner through ssh as user '$ROOTLESS_USER'"
+ msg "************************************************************"
+ set -x
+ exec ssh $ROOTLESS_USER@localhost \
+ -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
+ -o CheckHostIP=no $GOSRC/$SCRIPT_BASE/runner.sh
+ # Does not return!
+fi
+# else: not running rootless, do nothing special
+
cd "${GOSRC}/"
handler="_run_${TEST_FLAVOR}"
diff --git a/contrib/rootless-cni-infra/Containerfile b/contrib/rootless-cni-infra/Containerfile
deleted file mode 100644
index 4324f39d2..000000000
--- a/contrib/rootless-cni-infra/Containerfile
+++ /dev/null
@@ -1,36 +0,0 @@
-ARG GOLANG_VERSION=1.15
-ARG ALPINE_VERSION=3.12
-ARG CNI_VERSION=v0.8.0
-ARG CNI_PLUGINS_VERSION=v0.8.7
-ARG DNSNAME_VERSION=v1.1.1
-
-FROM golang:${GOLANG_VERSION}-alpine${ALPINE_VERSION} AS golang-base
-RUN apk add --no-cache git
-
-FROM golang-base AS cnitool
-RUN git clone https://github.com/containernetworking/cni /go/src/github.com/containernetworking/cni
-WORKDIR /go/src/github.com/containernetworking/cni
-ARG CNI_VERSION
-RUN git checkout ${CNI_VERSION}
-RUN go build -o /cnitool ./cnitool
-
-FROM golang-base AS dnsname
-RUN git clone https://github.com/containers/dnsname /go/src/github.com/containers/dnsname
-WORKDIR /go/src/github.com/containers/dnsname
-ARG DNSNAME_VERSION
-RUN git checkout ${DNSNAME_VERSION}
-RUN go build -o /dnsname ./plugins/meta/dnsname
-
-FROM alpine:${ALPINE_VERSION}
-RUN apk add --no-cache curl dnsmasq iptables ip6tables iproute2
-ARG TARGETARCH
-ARG CNI_PLUGINS_VERSION
-RUN mkdir -p /opt/cni/bin && \
- curl -fsSL https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/cni-plugins-linux-${TARGETARCH}-${CNI_PLUGINS_VERSION}.tgz | tar xz -C /opt/cni/bin
-COPY --from=cnitool /cnitool /usr/local/bin
-COPY --from=dnsname /dnsname /opt/cni/bin
-COPY rootless-cni-infra /usr/local/bin
-ENV CNI_PATH=/opt/cni/bin
-CMD ["sleep", "infinity"]
-
-ENV ROOTLESS_CNI_INFRA_VERSION=5
diff --git a/contrib/rootless-cni-infra/README.md b/contrib/rootless-cni-infra/README.md
deleted file mode 100644
index c43b4cf49..000000000
--- a/contrib/rootless-cni-infra/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# rootless-cni-infra
-
-Infra container for CNI-in-slirp4netns.
-
-## How it works
-
-When a CNI network is specified for `podman run` in rootless mode, Podman launches the `rootless-cni-infra` container to execute CNI plugins inside slirp4netns.
-
-The infra container is created per user, by executing an equivalent of:
-`podman run -d --name rootless-cni-infra --pid=host --privileged -v $HOME/.config/cni/net.d:/etc/cni/net.d rootless-cni-infra`.
-The infra container is automatically deleted when no CNI network is in use.
-
-Podman then allocates a CNI netns in the infra container, by executing an equivalent of:
-`podman exec rootless-cni-infra rootless-cni-infra alloc $CONTAINER_ID $NETWORK_NAME $POD_NAME`.
-
-The allocated netns is deallocated when the container is being removed, by executing an equivalent of:
-`podman exec rootless-cni-infra rootless-cni-infra dealloc $CONTAINER_ID $NETWORK_NAME`.
-
-The container images live on `quay.io/libpod/rootless-cni-infra`. The tags have the format `$version-$architecture`. Please make sure to increase the version number in the Containerfile (i.e., `ROOTLESS_CNI_INFRA_VERSION`) when applying changes to this directory. After committing the changes, upload the image(s) with the corresponding tag.
-
-## Directory layout
-
-* `/run/rootless-cni-infra/${CONTAINER_ID}/pid`: PID of the `sleep infinity` process that corresponds to the allocated netns
-* `/run/rootless-cni-infra/${CONTAINER_ID}/attached/${NETWORK_NAME}`: CNI result
-* `/run/rootless-cni-infra/${CONTAINER_ID}/attached-args/${NETWORK_NAME}`: CNI args
diff --git a/contrib/rootless-cni-infra/rootless-cni-infra b/contrib/rootless-cni-infra/rootless-cni-infra
deleted file mode 100755
index cceb8d817..000000000
--- a/contrib/rootless-cni-infra/rootless-cni-infra
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/bin/sh
-set -eu
-
-ARG0="$0"
-BASE="/run/rootless-cni-infra"
-
-wait_unshare_net() {
- pid="$1"
- # NOTE: busybox shell doesn't support the `for ((i=0; i < $MAX; i++)); do foo; done` statement
- i=0
- while :; do
- if [ "$(readlink /proc/self/ns/net)" != "$(readlink /proc/${pid}/ns/net)" ]; then
- break
- fi
- sleep 0.1
- if [ $i -ge 10 ]; then
- echo >&2 "/proc/${pid}/ns/net cannot be unshared"
- exit 1
- fi
- i=$((i + 1))
- done
-}
-
-# CLI subcommand: "alloc $CONTAINER_ID $NETWORK_NAME $POD_NAME $IP $MAC $CAP_ARGS"
-cmd_entrypoint_alloc() {
- if [ "$#" -ne 6 ]; then
- echo >&2 "Usage: $ARG0 alloc CONTAINER_ID NETWORK_NAME POD_NAME IP MAC CAP_ARGS"
- exit 1
- fi
-
- ID="$1"
- NET="$2"
- K8S_POD_NAME="$3"
- IP="$4"
- MAC="$5"
- CAP_ARGS="$6"
-
- dir="${BASE}/${ID}"
- mkdir -p "${dir}/attached" "${dir}/attached-args"
-
- pid=""
- if [ -f "${dir}/pid" ]; then
- pid=$(cat "${dir}/pid")
- else
- unshare -n sleep infinity &
- pid="$!"
- wait_unshare_net "${pid}"
- echo "${pid}" >"${dir}/pid"
- nsenter -t "${pid}" -n ip link set lo up
- fi
- CNI_ARGS="IgnoreUnknown=1;K8S_POD_NAME=${K8S_POD_NAME}"
- if [ "$IP" ]; then
- CNI_ARGS="$CNI_ARGS;IP=${IP}"
- fi
- if [ "$MAC" ]; then
- CNI_ARGS="$CNI_ARGS;MAC=${MAC}"
- fi
- if [ "$CAP_ARGS" ]; then
- CAP_ARGS="$CAP_ARGS"
- fi
- nwcount=$(find "${dir}/attached" -type f | wc -l)
- CNI_IFNAME="eth${nwcount}"
- export CNI_ARGS CNI_IFNAME CAP_ARGS
- cnitool add "${NET}" "/proc/${pid}/ns/net" >"${dir}/attached/${NET}"
- echo "${CNI_ARGS}" >"${dir}/attached-args/${NET}"
-
- # return the result
- ns="/proc/${pid}/ns/net"
- echo "{\"ns\":\"${ns}\"}"
-}
-
-# CLI subcommand: "dealloc $CONTAINER_ID $NETWORK_NAME"
-cmd_entrypoint_dealloc() {
- if [ "$#" -ne 2 ]; then
- echo >&2 "Usage: $ARG0 dealloc CONTAINER_ID NETWORK_NAME"
- exit 1
- fi
-
- ID=$1
- NET=$2
-
- dir="${BASE}/${ID}"
- if [ ! -f "${dir}/pid" ]; then
- exit 0
- fi
- pid=$(cat "${dir}/pid")
- if [ -f "${dir}/attached-args/${NET}" ]; then
- CNI_ARGS=$(cat "${dir}/attached-args/${NET}")
- export CNI_ARGS
- fi
- cnitool del "${NET}" "/proc/${pid}/ns/net"
- rm -f "${dir}/attached/${NET}" "${dir}/attached-args/${NET}"
-
- nwcount=$(find "${dir}/attached" -type f | wc -l)
- if [ "${nwcount}" = 0 ]; then
- kill -9 "${pid}"
- rm -rf "${dir}"
- fi
-
- # return empty json
- echo "{}"
-}
-
-# CLI subcommand: "is-idle"
-cmd_entrypoint_is_idle() {
- if [ ! -d ${BASE} ]; then
- echo '{"idle": true}'
- elif [ -z "$(ls -1 ${BASE})" ]; then
- echo '{"idle": true}'
- else
- echo '{"idle": false}'
- fi
-}
-
-# CLI subcommand: "print-cni-result $CONTAINER_ID $NETWORK_NAME"
-cmd_entrypoint_print_cni_result() {
- if [ "$#" -ne 2 ]; then
- echo >&2 "Usage: $ARG0 print-cni-result CONTAINER_ID NETWORK_NAME"
- exit 1
- fi
-
- ID=$1
- NET=$2
-
- # the result shall be CNI JSON
- cat "${BASE}/${ID}/attached/${NET}"
-}
-
-# CLI subcommand: "print-netns-path $CONTAINER_ID"
-cmd_entrypoint_print_netns_path() {
- if [ "$#" -ne 1 ]; then
- echo >&2 "Usage: $ARG0 print-netns-path CONTAINER_ID"
- exit 1
- fi
-
- ID=$1
-
- pid=$(cat "${BASE}/${ID}/pid")
- path="/proc/${pid}/ns/net"
-
- # return the result
- echo "{\"path\":\"${path}\"}"
-}
-
-# CLI subcommand: "help"
-cmd_entrypoint_help() {
- echo "Usage: ${ARG0} COMMAND"
- echo
- echo "Rootless CNI Infra container"
- echo
- echo "Commands:"
- echo " alloc Allocate a netns"
- echo " dealloc Deallocate a netns"
- echo " is-idle Print whether the infra container is idle"
- echo " print-cni-result Print CNI result"
- echo " print-netns-path Print netns path"
- echo " help Print help"
- echo " version Print version"
-}
-
-# CLI subcommand: "version"
-cmd_entrypoint_version() {
- echo "{\"version\": \"${ROOTLESS_CNI_INFRA_VERSION}\"}"
-}
-
-# parse args
-command="${1:-}"
-if [ -z "$command" ]; then
- echo >&2 "No command was specified. Run \`${ARG0} help\` to see the usage."
- exit 1
-fi
-
-command_func=$(echo "cmd_entrypoint_${command}" | sed -e "s/-/_/g")
-if ! command -v "${command_func}" >/dev/null 2>&1; then
- echo >&2 "Unknown command: ${command}. Run \`${ARG0} help\` to see the usage."
- exit 1
-fi
-
-# start the command func
-shift
-"${command_func}" "$@"
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index f56319cf3..927b3df33 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -989,25 +989,21 @@ The following examples are all valid:
Without this argument the command will be run as root in the container.
-#### **\-\-userns**=*auto*[:OPTIONS]
-#### **\-\-userns**=*host*
-#### **\-\-userns**=*keep-id*
-#### **\-\-userns**=container:container
-#### **\-\-userns**=private
-#### **\-\-userns**=*ns:my_namespace*
-
-Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled.
-
-
-- `auto`: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are
- **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace.
- **uidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a UID mapping to be present in the user namespace.
- **gidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a GID mapping to be present in the user namespace.
-- `container`: join the user namespace of the specified container.
-- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
-- `keep-id`: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
-- `ns`: run the container in the given existing user namespace.
-- `private`: create a new namespace for the container (default)
+#### **\-\-userns**=*mode*
+
+Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **\-\-uidmap** and **\-\-gidmap** options.
+
+Valid _mode_ values are:
+
+- **auto[:**_OPTIONS,..._**]**: automatically create a namespace. It is possible to specify these options to `auto`:
+ - **gidmapping=**_HOST_GID:CONTAINER_GID:SIZE_: to force a GID mapping to be present in the user namespace.
+ - **size=**_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace.
+ - **uidmapping=**_HOST_UID:CONTAINER_UID:SIZE_: to force a UID mapping to be present in the user namespace.
+- **container:**_id_: join the user namespace of the specified container.
+- **host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default).
+- **keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
+- **ns:**_namespace_: run the container in the given existing user namespace.
+- **private**: create a new namespace for the container.
This option is incompatible with **\-\-gidmap**, **\-\-uidmap**, **\-\-subuidname** and **\-\-subgidname**.
diff --git a/docs/source/markdown/podman-manifest-add.1.md b/docs/source/markdown/podman-manifest-add.1.md
index c3c1ea1cf..2727843da 100644
--- a/docs/source/markdown/podman-manifest-add.1.md
+++ b/docs/source/markdown/podman-manifest-add.1.md
@@ -4,7 +4,7 @@
podman\-manifest\-add - Add an image to a manifest list or image index
## SYNOPSIS
-**podman manifest add** [*options*] *listnameorindexname* *imagename*
+**podman manifest add** [*options*] *listnameorindexname* [*transport*]:*imagename*
## DESCRIPTION
@@ -79,6 +79,20 @@ Specify the variant which the list or index records for the image. This option
is typically used to distinguish between multiple entries which share the same
architecture value, but which expect different versions of its instruction set.
+## Transport
+
+ Multiple transports are supported:
+
+ **docker://**_docker-reference_ _(default)_
+ An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
+
+ $ podman manifest add mylist:v1.11 docker://quay.io/username/myimage
+
+ **containers-storage:**_oci-reference_
+ An image in _oci-reference_ format stored in the local container storage. _oci-reference_ must contain a tag.
+
+ $ podman manifest add mylist:v1.11 containers-storage:quay.io/username/myimage
+
## EXAMPLE
```
diff --git a/docs/source/markdown/podman-manifest-push.1.md b/docs/source/markdown/podman-manifest-push.1.md
index 4e7118dad..5236dd7c3 100644
--- a/docs/source/markdown/podman-manifest-push.1.md
+++ b/docs/source/markdown/podman-manifest-push.1.md
@@ -4,7 +4,7 @@
podman\-manifest\-push - Push a manifest list or image index to a registry
## SYNOPSIS
-**podman manifest push** [*options*] *listnameorindexname* *transport:details*
+**podman manifest push** [*options*] *listnameorindexname* [*destination*]
## DESCRIPTION
Pushes a manifest list or image index to a registry.
@@ -66,6 +66,40 @@ Sign the pushed images using the GPG key that matches the specified fingerprint.
Require HTTPS and verify certificates when talking to container registries. (defaults to true)
+## DESTINATION
+
+ The DESTINATION is a location to store container images
+ The Image "DESTINATION" uses a "transport":"details" format.
+ If a transport is not given, podman push will attempt to push
+ to a registry.
+
+ Multiple transports are supported:
+
+ **dir:**_path_
+ An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
+
+ $ podman manfiest push mylist:v1.11 dir:/tmp/mylist
+
+ **docker://**_docker-reference_
+ An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
+
+ $ podman manfiest push mylist:v1.11 docker://registry.example.org/mylist:v1.11
+
+ **docker-archive:**_path_[**:**_docker-reference_]
+ An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
+
+ $ podman manfiest push mylist:v1.11 docker-archive:/tmp/mylist
+
+ **docker-daemon:**_docker-reference_
+ An image in _docker-reference_ format stored in the docker daemon internal storage. _docker-reference_ must contain a tag.
+
+ $ podman manfiest push mylist:v1.11 docker-daemon:registry.example.org/mylist:v1.11
+
+ **oci-archive:**_path_**:**_tag_
+ An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
+
+ $ podman manifest push mylist:v1.11 oci-archive:/tmp/mylist
+
## EXAMPLE
```
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index fcb5a13ec..4c096ecfe 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -1070,19 +1070,21 @@ Without this argument, the command will run as the user specified in the contain
When a user namespace is not in use, the UID and GID used within the container and on the host will match. When user namespaces are in use, however, the UID and GID in the container may correspond to another UID and GID on the host. In rootless containers, for example, a user namespace is always used, and root in the container will by default correspond to the UID and GID of the user invoking Podman.
-#### **\-\-userns**=**auto**|**host**|**keep-id**|**container:**_id_|**ns:**_namespace_
+#### **\-\-userns**=*mode*
-Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with they `--uidmapping` and `--gidmapping` options.
+Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with the **\-\-uidmap** and **\-\-gidmap** options.
-- **auto**: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are
- **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace.
- **uidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a UID mapping to be present in the user namespace.
- **gidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a GID mapping to be present in the user namespace.
+Valid _mode_ values are:
+
+- **auto[:**_OPTIONS,..._**]**: automatically create a namespace. It is possible to specify these options to `auto`:
+ - **gidmapping=**_HOST_GID:CONTAINER_GID:SIZE_: to force a GID mapping to be present in the user namespace.
+ - **size=**_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace.
+ - **uidmapping=**_HOST_UID:CONTAINER_UID:SIZE_: to force a UID mapping to be present in the user namespace.
+- **container:**_id_: join the user namespace of the specified container.
- **host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default).
- **keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
-- **ns**: run the container in the given existing user namespace.
+- **ns:**_namespace_: run the container in the given existing user namespace.
- **private**: create a new namespace for the container.
-- **container**: join the user namespace of the specified container.
This option is incompatible with **\-\-gidmap**, **\-\-uidmap**, **\-\-subuidname** and **\-\-subgidname**.
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 7a6a51c39..5755b45ac 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -148,7 +148,7 @@ specify additional options via the `--storage-opt` flag.
#### **\-\-storage-opt**=*value*
-Storage driver option, Default storage driver options are configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode). The `STORAGE_OPTS` environment variable overrides the default. The --storage-opt specified options overrides all.
+Storage driver option, Default storage driver options are configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode). The `STORAGE_OPTS` environment variable overrides the default. The --storage-opt specified options overrides all. If you specify --storage-opt="", no storage options will be used.
#### **\-\-syslog**=*true|false*
diff --git a/go.mod b/go.mod
index 376f4e9f8..52d632b46 100644
--- a/go.mod
+++ b/go.mod
@@ -43,7 +43,7 @@ require (
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635
github.com/mrunalp/fileutils v0.5.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
- github.com/onsi/ginkgo v1.15.2
+ github.com/onsi/ginkgo v1.16.0
github.com/onsi/gomega v1.11.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
@@ -53,7 +53,7 @@ require (
github.com/opencontainers/selinux v1.8.0
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
- github.com/rootless-containers/rootlesskit v0.14.0
+ github.com/rootless-containers/rootlesskit v0.14.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.3
github.com/spf13/pflag v1.0.5
diff --git a/go.sum b/go.sum
index f3cb4e739..1176a7f33 100644
--- a/go.sum
+++ b/go.sum
@@ -313,6 +313,8 @@ github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
@@ -583,8 +585,9 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
-github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org=
github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
+github.com/onsi/ginkgo v1.16.0 h1:NBrNLB37exjJLxXtFOktx6CISBdS1aF8+7MwKlTV8U4=
+github.com/onsi/ginkgo v1.16.0/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -685,8 +688,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rootless-containers/rootlesskit v0.14.0 h1:4zfZqDv7JzsVuMkj4ZMB9cs9mQQnyl1gWBsrpOYcmtk=
-github.com/rootless-containers/rootlesskit v0.14.0/go.mod h1:nV3TpRISvwhZQSwo0nmQQnxjCxXr3mvrMi0oASLvzcg=
+github.com/rootless-containers/rootlesskit v0.14.1 h1:lvyG8XLYOiHPoSjWekgqY4MkNZJ+218KBOLfw9kB3Hk=
+github.com/rootless-containers/rootlesskit v0.14.1/go.mod h1:nV3TpRISvwhZQSwo0nmQQnxjCxXr3mvrMi0oASLvzcg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 106e2569b..a53027ab2 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -966,9 +966,7 @@ func (c *Container) completeNetworkSetup() error {
if err := c.syncContainer(); err != nil {
return err
}
- if rootless.IsRootless() {
- return c.runtime.setupRootlessNetNS(c)
- } else if c.config.NetMode.IsSlirp4netns() {
+ if c.config.NetMode.IsSlirp4netns() {
return c.runtime.setupSlirp4netns(c)
}
if err := c.runtime.setupNetNS(c); err != nil {
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index a136fb72d..4fc45e4f0 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -92,11 +92,7 @@ func (c *Container) prepare() error {
// Set up network namespace if not already set up
noNetNS := c.state.NetNS == nil
if c.config.CreateNetNS && noNetNS && !c.config.PostConfigureNetNS {
- if rootless.IsRootless() && len(c.config.Networks) > 0 {
- netNS, networkStatus, createNetNSErr = AllocRootlessCNI(context.Background(), c)
- } else {
- netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
- }
+ netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
if createNetNSErr != nil {
return
}
@@ -380,8 +376,14 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
case "z":
fallthrough
case "Z":
- if err := label.Relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil {
- return nil, err
+ if c.MountLabel() != "" {
+ if c.ProcessLabel() != "" {
+ if err := label.Relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil {
+ return nil, err
+ }
+ } else {
+ logrus.Infof("Not relabeling volume %q in container %s as SELinux is disabled", m.Source, c.ID())
+ }
}
default:
diff --git a/libpod/network/create.go b/libpod/network/create.go
index 1a5aa82fc..4fe9b445f 100644
--- a/libpod/network/create.go
+++ b/libpod/network/create.go
@@ -11,7 +11,6 @@ import (
"github.com/containernetworking/cni/pkg/version"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/pkg/domain/entities"
- "github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -223,9 +222,8 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon
plugins = append(plugins, NewPortMapPlugin())
plugins = append(plugins, NewFirewallPlugin())
plugins = append(plugins, NewTuningPlugin())
- // if we find the dnsname plugin or are rootless, we add configuration for it
- // the rootless-cni-infra container has the dnsname plugin always installed
- if (HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) || rootless.IsRootless()) && !options.DisableDNS {
+ // if we find the dnsname plugin we add configuration for it
+ if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
if options.Internal {
logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name)
} else {
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 8bf532f66..157c85431 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -3,11 +3,8 @@
package libpod
import (
- "bytes"
- "context"
"crypto/rand"
"fmt"
- "io"
"io/ioutil"
"net"
"os"
@@ -27,9 +24,12 @@ import (
"github.com/containers/podman/v3/libpod/network"
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/netns"
+ "github.com/containers/podman/v3/pkg/resolvconf"
"github.com/containers/podman/v3/pkg/rootless"
- "github.com/containers/podman/v3/pkg/rootlessport"
+ "github.com/containers/podman/v3/pkg/util"
+ "github.com/containers/storage/pkg/lockfile"
"github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
@@ -46,6 +46,9 @@ const (
// slirp4netnsMTU the default MTU override
slirp4netnsMTU = 65520
+
+ // rootlessCNINSName is the file name for the rootless network namespace bind mount
+ rootlessCNINSName = "rootless-cni-ns"
)
// Get an OCICNI network config
@@ -102,6 +105,361 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port
return ctrNetwork
}
+type rootlessCNI struct {
+ ns ns.NetNS
+ dir string
+ lock lockfile.Locker
+}
+
+func (r *rootlessCNI) Do(toRun func() error) error {
+ err := r.ns.Do(func(_ ns.NetNS) error {
+ // before we can run the given function
+ // we have to setup all mounts correctly
+
+ // create a new mount namespace
+ // this should happen inside the netns thread
+ err := unix.Unshare(unix.CLONE_NEWNS)
+ if err != nil {
+ return errors.Wrapf(err, "cannot create a new mount namespace")
+ }
+
+ netNsDir, err := netns.GetNSRunDir()
+ if err != nil {
+ return errors.Wrap(err, "could not get network namespace directory")
+ }
+ newNetNsDir := filepath.Join(r.dir, netNsDir)
+ // mount the netns into the new run to keep them accessible
+ // otherwise cni setup will fail because it cannot access the netns files
+ err = unix.Mount(netNsDir, newNetNsDir, "none", unix.MS_BIND|unix.MS_SHARED|unix.MS_REC, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to mount netns directory for rootless cni")
+ }
+
+ // mount resolv.conf to make use of the host dns
+ err = unix.Mount(filepath.Join(r.dir, "resolv.conf"), "/etc/resolv.conf", "none", unix.MS_BIND, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to mount resolv.conf for rootless cni")
+ }
+
+ // also keep /run/systemd if it exists
+ // many files are symlinked into this dir, for example /dev/log
+ runSystemd := "/run/systemd"
+ _, err = os.Stat(runSystemd)
+ if err == nil {
+ newRunSystemd := filepath.Join(r.dir, runSystemd[1:])
+ err = unix.Mount(runSystemd, newRunSystemd, "none", unix.MS_BIND|unix.MS_REC, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to mount /run/systemd directory for rootless cni")
+ }
+ }
+
+ // cni plugins need access to /var and /run
+ runDir := filepath.Join(r.dir, "run")
+ varDir := filepath.Join(r.dir, "var")
+ // make sure to mount var first
+ err = unix.Mount(varDir, "/var", "none", unix.MS_BIND, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to mount /var for rootless cni")
+ }
+ // recursive mount to keep the netns mount
+ err = unix.Mount(runDir, "/run", "none", unix.MS_BIND|unix.MS_REC, "")
+ if err != nil {
+ return errors.Wrap(err, "failed to mount /run for rootless cni")
+ }
+
+ // run the given function in the correct namespace
+ err = toRun()
+ return err
+ })
+ return err
+}
+
+// cleanup the rootless cni namespace if needed
+// check if we have running containers with the bridge network mode
+func (r *rootlessCNI) cleanup(runtime *Runtime) error {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ running := func(c *Container) bool {
+ // we cannot use c.state() because it will try to lock the container
+ // using c.state.State directly should be good enough for this use case
+ state := c.state.State
+ return state == define.ContainerStateRunning
+ }
+ ctrs, err := runtime.GetContainersWithoutLock(running)
+ if err != nil {
+ return err
+ }
+ cleanup := true
+ for _, ctr := range ctrs {
+ if ctr.config.NetMode.IsBridge() {
+ cleanup = false
+ }
+ }
+ if cleanup {
+ // make sure the the cni results (cache) dir is empty
+ // libpod instances with another root dir are not covered by the check above
+ // this allows several libpod instances to use the same rootless cni ns
+ contents, err := ioutil.ReadDir(filepath.Join(r.dir, "var/lib/cni/results"))
+ if (err == nil && len(contents) == 0) || os.IsNotExist(err) {
+ logrus.Debug("Cleaning up rootless cni namespace")
+ err = netns.UnmountNS(r.ns)
+ if err != nil {
+ return err
+ }
+ // make the following errors not fatal
+ err = r.ns.Close()
+ if err != nil {
+ logrus.Error(err)
+ }
+ b, err := ioutil.ReadFile(filepath.Join(r.dir, "rootless-cni-slirp4netns.pid"))
+ if err == nil {
+ var i int
+ i, err = strconv.Atoi(string(b))
+ if err == nil {
+ // kill the slirp process so we do not leak it
+ err = syscall.Kill(i, syscall.SIGTERM)
+ }
+ }
+ if err != nil {
+ logrus.Errorf("failed to kill slirp4netns process: %s", err)
+ }
+ err = os.RemoveAll(r.dir)
+ if err != nil {
+ logrus.Error(err)
+ }
+ } else if err != nil && !os.IsNotExist(err) {
+ logrus.Errorf("could not read rootless cni directory, skipping cleanup: %s", err)
+ }
+ }
+ return nil
+}
+
+// getRootlessCNINetNs returns the rootless cni object. If create is set to true
+// the rootless cni namespace will be created if it does not exists already.
+func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
+ var rootlessCNINS *rootlessCNI
+ if rootless.IsRootless() {
+ runDir, err := util.GetRuntimeDir()
+ if err != nil {
+ return nil, err
+ }
+ cniDir := filepath.Join(runDir, "rootless-cni")
+ err = os.MkdirAll(cniDir, 0700)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not create rootless-cni directory")
+ }
+
+ lfile := filepath.Join(cniDir, "rootless-cni.lck")
+ lock, err := lockfile.GetLockfile(lfile)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get rootless-cni lockfile")
+ }
+ lock.Lock()
+ defer lock.Unlock()
+
+ nsDir, err := netns.GetNSRunDir()
+ if err != nil {
+ return nil, err
+ }
+ path := filepath.Join(nsDir, rootlessCNINSName)
+ ns, err := ns.GetNS(path)
+ if err != nil {
+ if new {
+ // create a new namespace
+ logrus.Debug("creating rootless cni network namespace")
+ ns, err = netns.NewNSWithName(rootlessCNINSName)
+ if err != nil {
+ return nil, errors.Wrap(err, "error creating rootless cni network namespace")
+ }
+
+ // setup slirp4netns here
+ path := r.config.Engine.NetworkCmdPath
+ if path == "" {
+ var err error
+ path, err = exec.LookPath("slirp4netns")
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ syncR, syncW, err := os.Pipe()
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to open pipe")
+ }
+ defer errorhandling.CloseQuiet(syncR)
+ defer errorhandling.CloseQuiet(syncW)
+
+ netOptions, err := parseSlirp4netnsNetworkOptions(r, nil)
+ if err != nil {
+ return nil, err
+ }
+ slirpFeatures, err := checkSlirpFlags(path)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err)
+ }
+ cmdArgs, err := createBasicSlirp4netnsCmdArgs(netOptions, slirpFeatures)
+ if err != nil {
+ return nil, err
+ }
+ // Note we do not use --exit-fd, we kill this process by pid
+ cmdArgs = append(cmdArgs, "-c", "-r", "3")
+ cmdArgs = append(cmdArgs, "--netns-type=path", ns.Path(), "tap0")
+
+ cmd := exec.Command(path, cmdArgs...)
+ logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " "))
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Setpgid: true,
+ }
+
+ // workaround for https://github.com/rootless-containers/slirp4netns/pull/153
+ if !netOptions.noPivotRoot && slirpFeatures.HasEnableSandbox {
+ cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS
+ cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS
+ }
+
+ // Leak one end of the pipe in slirp4netns
+ cmd.ExtraFiles = append(cmd.ExtraFiles, syncW)
+
+ logPath := filepath.Join(r.config.Engine.TmpDir, "slirp4netns-rootless-cni.log")
+ logFile, err := os.Create(logPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to open slirp4netns 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 nil, errors.Wrapf(err, "delete file %s", logPath)
+ }
+ cmd.Stdout = logFile
+ cmd.Stderr = logFile
+ if err := cmd.Start(); err != nil {
+ return nil, errors.Wrapf(err, "failed to start slirp4netns process")
+ }
+ // create pid file for the slirp4netns process
+ // this is need to kill the process in the cleanup
+ pid := strconv.Itoa(cmd.Process.Pid)
+ err = ioutil.WriteFile(filepath.Join(cniDir, "rootless-cni-slirp4netns.pid"), []byte(pid), 0700)
+ if err != nil {
+ errors.Wrap(err, "unable to write rootless-cni slirp4netns pid file")
+ }
+
+ defer func() {
+ if err := cmd.Process.Release(); err != nil {
+ logrus.Errorf("unable to release command process: %q", err)
+ }
+ }()
+
+ if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
+ return nil, err
+ }
+
+ // build a new resolv.conf file which uses the slirp4netns dns server address
+ resolveIP := slirp4netnsDNS
+ if netOptions.cidr != "" {
+ _, cidr, err := net.ParseCIDR(netOptions.cidr)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to parse slirp4netns cidr")
+ }
+ // the slirp dns ip is always the third ip in the subnet
+ cidr.IP[len(cidr.IP)-1] = cidr.IP[len(cidr.IP)-1] + 3
+ resolveIP = cidr.IP.String()
+ }
+ conf, err := resolvconf.Get()
+ if err != nil {
+ return nil, err
+ }
+ searchDomains := resolvconf.GetSearchDomains(conf.Content)
+ dnsOptions := resolvconf.GetOptions(conf.Content)
+
+ _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP}, searchDomains, dnsOptions)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf")
+ }
+
+ // create cni directories to store files
+ // they will be bind mounted to the correct location in a extra mount ns
+ err = os.MkdirAll(filepath.Join(cniDir, "var"), 0700)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not create rootless-cni var directory")
+ }
+ runDir := filepath.Join(cniDir, "run")
+ err = os.MkdirAll(runDir, 0700)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not create rootless-cni run directory")
+ }
+ // relabel the new run directory to the iptables /run label
+ // this is important, otherwise the iptables command will fail
+ err = label.Relabel(runDir, "system_u:object_r:iptables_var_run_t:s0", false)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not create relabel rootless-cni run directory")
+ }
+ // create systemd run directory
+ err = os.MkdirAll(filepath.Join(runDir, "systemd"), 0700)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not create rootless-cni systemd directory")
+ }
+ // create the directory for the netns files at the same location
+ // relative to the rootless-cni location
+ err = os.MkdirAll(filepath.Join(cniDir, nsDir), 0700)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not create rootless-cni netns directory")
+ }
+ } else {
+ // return a error if we could not get the namespace and should no create one
+ return nil, errors.Wrap(err, "error getting rootless cni network namespace")
+ }
+ }
+
+ rootlessCNINS = &rootlessCNI{
+ ns: ns,
+ dir: cniDir,
+ lock: lock,
+ }
+ }
+ return rootlessCNINS, nil
+}
+
+// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
+// networks. If rootless it will join/create the rootless cni namespace.
+func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
+ rootlessCNINS, err := r.getRootlessCNINetNs(true)
+ if err != nil {
+ return nil, err
+ }
+ var results []ocicni.NetResult
+ setUpPod := func() error {
+ results, err = r.netPlugin.SetUpPod(podNetwork)
+ if err != nil {
+ if err2 := r.netPlugin.TearDownPod(podNetwork); err2 != nil {
+ logrus.Errorf("Error tearing down partially created network namespace for container %s: %v", podNetwork.ID, err2)
+ }
+ return errors.Wrapf(err, "error configuring network namespace for container %s", podNetwork.ID)
+ }
+ return nil
+ }
+ // rootlessCNINS is nil if we are root
+ if rootlessCNINS != nil {
+ // execute the cni setup in the rootless net ns
+ err = rootlessCNINS.Do(setUpPod)
+ } else {
+ err = setUpPod()
+ }
+ return results, err
+}
+
+// getCNIPodName return the pod name (hostname) used by CNI and the dnsname plugin.
+// If we are in the pod network namespace use the pod name otherwise the container name
+func getCNIPodName(c *Container) string {
+ if c.config.NetMode.IsPod() || c.IsInfra() {
+ pod, err := c.runtime.GetPod(c.PodID())
+ if err == nil {
+ return pod.Name()
+ }
+ }
+ return c.Name()
+}
+
// Create and configure a new network namespace for a container
func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) {
var requestedIP net.IP
@@ -147,17 +505,10 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
podNetwork.Aliases = aliases
}
- results, err := r.netPlugin.SetUpPod(podNetwork)
+ results, err := r.setUpOCICNIPod(podNetwork)
if err != nil {
- return nil, errors.Wrapf(err, "error configuring network namespace for container %s", ctr.ID())
+ return nil, err
}
- defer func() {
- if err != nil {
- if err2 := r.netPlugin.TearDownPod(podNetwork); err2 != nil {
- logrus.Errorf("Error tearing down partially created network namespace for container %s: %v", ctr.ID(), err2)
- }
- }
- }()
networkStatus := make([]*cnitypes.Result, 0)
for idx, r := range results {
@@ -192,52 +543,12 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result,
logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
networkStatus := []*cnitypes.Result{}
- if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
+ if !ctr.config.NetMode.IsSlirp4netns() {
networkStatus, err = r.configureNetNS(ctr, ctrNS)
}
return ctrNS, networkStatus, err
}
-type slirpFeatures struct {
- HasDisableHostLoopback bool
- HasMTU bool
- HasEnableSandbox bool
- HasEnableSeccomp bool
- HasCIDR bool
- HasOutboundAddr bool
- HasIPv6 bool
-}
-
-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) (*slirpFeatures, error) {
- cmd := exec.Command(path, "--help")
- out, err := cmd.CombinedOutput()
- if err != nil {
- return nil, errors.Wrapf(err, "slirp4netns %q", out)
- }
- return &slirpFeatures{
- HasDisableHostLoopback: strings.Contains(string(out), "--disable-host-loopback"),
- HasMTU: strings.Contains(string(out), "--mtu"),
- HasEnableSandbox: strings.Contains(string(out), "--enable-sandbox"),
- HasEnableSeccomp: strings.Contains(string(out), "--enable-seccomp"),
- HasCIDR: strings.Contains(string(out), "--cidr"),
- HasOutboundAddr: strings.Contains(string(out), "--outbound-addr"),
- HasIPv6: strings.Contains(string(out), "--enable-ipv6"),
- }, nil
-}
-
// Configure the network namespace for a rootless container
func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
if ctr.config.NetMode.IsSlirp4netns() {
@@ -247,7 +558,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
if err != nil {
return err
}
- if len(networks) > 0 {
+ if len(networks) > 0 && len(ctr.config.PortMappings) > 0 {
// set up port forwarder for CNI-in-slirp4netns
netnsPath := ctr.state.NetNS.Path()
// TODO: support slirp4netns port forwarder as well
@@ -256,456 +567,6 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
return nil
}
-// setupSlirp4netns can be called in rootful as well as in rootless
-func (r *Runtime) setupSlirp4netns(ctr *Container) error {
- path := r.config.Engine.NetworkCmdPath
- slirpOptions := r.config.Engine.NetworkCmdOptions
- noPivotRoot := r.config.Engine.NoPivotRoot
- if path == "" {
- var err error
- path, err = exec.LookPath("slirp4netns")
- if err != nil {
- logrus.Errorf("could not find slirp4netns, the network namespace won't be configured: %v", err)
- return nil
- }
- }
-
- syncR, syncW, err := os.Pipe()
- if err != nil {
- return errors.Wrapf(err, "failed to open pipe")
- }
- defer errorhandling.CloseQuiet(syncR)
- defer errorhandling.CloseQuiet(syncW)
-
- havePortMapping := len(ctr.Config().PortMappings) > 0
- logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID))
-
- cidr := ""
- isSlirpHostForward := false
- disableHostLoopback := true
- enableIPv6 := false
- outboundAddr := ""
- outboundAddr6 := ""
- mtu := slirp4netnsMTU
-
- if ctr.config.NetworkOptions != nil {
- slirpOptions = append(slirpOptions, ctr.config.NetworkOptions["slirp4netns"]...)
- }
-
- for _, o := range slirpOptions {
- parts := strings.SplitN(o, "=", 2)
- if len(parts) < 2 {
- return errors.Errorf("unknown option for slirp4netns: %q", o)
- }
- option, value := parts[0], parts[1]
- switch option {
- case "cidr":
- ipv4, _, err := net.ParseCIDR(value)
- if err != nil || ipv4.To4() == nil {
- return errors.Errorf("invalid cidr %q", value)
- }
- cidr = value
- case "port_handler":
- switch value {
- case "slirp4netns":
- isSlirpHostForward = true
- case "rootlesskit":
- isSlirpHostForward = false
- default:
- return errors.Errorf("unknown port_handler for slirp4netns: %q", value)
- }
- case "allow_host_loopback":
- switch value {
- case "true":
- disableHostLoopback = false
- case "false":
- disableHostLoopback = true
- default:
- return errors.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value)
- }
- case "enable_ipv6":
- switch value {
- case "true":
- enableIPv6 = true
- case "false":
- enableIPv6 = false
- default:
- return errors.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value)
- }
- case "outbound_addr":
- ipv4 := net.ParseIP(value)
- if ipv4 == nil || ipv4.To4() == nil {
- _, err := net.InterfaceByName(value)
- if err != nil {
- return errors.Errorf("invalid outbound_addr %q", value)
- }
- }
- outboundAddr = value
- case "outbound_addr6":
- ipv6 := net.ParseIP(value)
- if ipv6 == nil || ipv6.To4() != nil {
- _, err := net.InterfaceByName(value)
- if err != nil {
- return errors.Errorf("invalid outbound_addr6: %q", value)
- }
- }
- outboundAddr6 = value
- case "mtu":
- mtu, err = strconv.Atoi(value)
- if mtu < 68 || err != nil {
- return errors.Errorf("invalid mtu %q", value)
- }
- default:
- return errors.Errorf("unknown option for slirp4netns: %q", o)
- }
- }
-
- cmdArgs := []string{}
- slirpFeatures, err := checkSlirpFlags(path)
- if err != nil {
- return errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err)
- }
- if disableHostLoopback && slirpFeatures.HasDisableHostLoopback {
- cmdArgs = append(cmdArgs, "--disable-host-loopback")
- }
- if mtu > -1 && slirpFeatures.HasMTU {
- cmdArgs = append(cmdArgs, fmt.Sprintf("--mtu=%d", mtu))
- }
- if !noPivotRoot && slirpFeatures.HasEnableSandbox {
- cmdArgs = append(cmdArgs, "--enable-sandbox")
- }
- if slirpFeatures.HasEnableSeccomp {
- cmdArgs = append(cmdArgs, "--enable-seccomp")
- }
-
- if cidr != "" {
- if !slirpFeatures.HasCIDR {
- return errors.Errorf("cidr not supported")
- }
- cmdArgs = append(cmdArgs, fmt.Sprintf("--cidr=%s", cidr))
- }
-
- if enableIPv6 {
- if !slirpFeatures.HasIPv6 {
- return errors.Errorf("enable_ipv6 not supported")
- }
- cmdArgs = append(cmdArgs, "--enable-ipv6")
- }
-
- if outboundAddr != "" {
- if !slirpFeatures.HasOutboundAddr {
- return errors.Errorf("outbound_addr not supported")
- }
- cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr=%s", outboundAddr))
- }
-
- if outboundAddr6 != "" {
- if !slirpFeatures.HasOutboundAddr || !slirpFeatures.HasIPv6 {
- return errors.Errorf("outbound_addr6 not supported")
- }
- if !enableIPv6 {
- return errors.Errorf("enable_ipv6=true is required for outbound_addr6")
- }
- cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr6=%s", outboundAddr6))
- }
-
- var apiSocket string
- if havePortMapping && isSlirpHostForward {
- apiSocket = filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
- cmdArgs = append(cmdArgs, "--api-socket", apiSocket)
- }
-
- // the slirp4netns arguments being passed are describes as follows:
- // from the slirp4netns documentation: https://github.com/rootless-containers/slirp4netns
- // -c, --configure Brings up the tap interface
- // -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")
- }
- 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")
- }
-
- cmd := exec.Command(path, cmdArgs...)
- logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " "))
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setpgid: true,
- }
-
- // workaround for https://github.com/rootless-containers/slirp4netns/pull/153
- if !noPivotRoot && slirpFeatures.HasEnableSandbox {
- cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS
- cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS
- }
-
- // Leak one end of the pipe in slirp4netns, the other will be sent to conmon
- cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncR, syncW)
-
- logFile, err := os.Create(logPath)
- if err != nil {
- return errors.Wrapf(err, "failed to open slirp4netns 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)
- }
- cmd.Stdout = logFile
- cmd.Stderr = logFile
- if err := cmd.Start(); err != nil {
- return errors.Wrapf(err, "failed to start slirp4netns process")
- }
- defer func() {
- if err := cmd.Process.Release(); err != nil {
- logrus.Errorf("unable to release command process: %q", err)
- }
- }()
-
- if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
- return err
- }
-
- if havePortMapping {
- if isSlirpHostForward {
- return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket)
- }
- return r.setupRootlessPortMappingViaRLK(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(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 {
- if os.IsTimeout(err) {
- // 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 {
- return errors.Wrapf(err, "failed to read %s process status", prog)
- }
- if pid != cmd.Process.Pid {
- continue
- }
- if status.Exited() {
- // Seek at the beginning of the file and read all its content
- if _, err := logFile.Seek(0, 0); err != nil {
- logrus.Errorf("could not seek log file: %q", err)
- }
- logContent, err := ioutil.ReadAll(logFile)
- if err != nil {
- return errors.Wrapf(err, "%s failed", prog)
- }
- return errors.Errorf("%s failed: %q", prog, logContent)
- }
- if status.Signaled() {
- return errors.Errorf("%s killed by signal", prog)
- }
- continue
- }
- return errors.Wrapf(err, "failed to read from %s sync pipe", prog)
- }
- }
- return nil
-}
-
-func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string) 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)
-
- logPath := filepath.Join(ctr.runtime.config.Engine.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)
- }
-
- if !ctr.config.PostConfigureNetNS {
- ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe()
- if err != nil {
- return errors.Wrapf(err, "failed to create rootless port sync pipe")
- }
- }
-
- childIP := slirp4netnsIP
-outer:
- for _, r := range ctr.state.NetworkStatus {
- for _, i := range r.IPs {
- ipv4 := i.Address.IP.To4()
- if ipv4 != nil {
- childIP = ipv4.String()
- break outer
- }
- }
- }
-
- cfg := rootlessport.Config{
- Mappings: ctr.config.PortMappings,
- NetNSPath: netnsPath,
- ExitFD: 3,
- ReadyFD: 4,
- TmpDir: ctr.runtime.config.Engine.TmpDir,
- ChildIP: childIP,
- }
- 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
-
- if ctr.rootlessPortSyncR != nil {
- defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR)
- }
-
- 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("rootlessport " + strings.TrimSuffix(stdoutStr, "\n"))
- }
- return err
- }
- logrus.Debug("rootlessport is ready")
- return nil
-}
-
-func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd, apiSocket string) (err error) {
- 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)
-
- // 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)
- }
-
- // 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"
- }
- apiCmd := 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(&apiCmd)
- 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)
- }
- }
- logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready")
- return nil
-}
-
// Configure the network namespace using the container process
func (r *Runtime) setupNetNS(ctr *Container) error {
nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
@@ -715,8 +576,11 @@ func (r *Runtime) setupNetNS(ctr *Container) error {
if _, err := rand.Reader.Read(b); err != nil {
return errors.Wrapf(err, "failed to generate random netns name")
}
-
- nsPath := fmt.Sprintf("/run/netns/cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
+ nsPath, err := netns.GetNSRunDir()
+ if err != nil {
+ return err
+ }
+ nsPath = filepath.Join(nsPath, fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]))
if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil {
return err
@@ -774,6 +638,31 @@ func (r *Runtime) closeNetNS(ctr *Container) error {
return nil
}
+// Tear down a container's CNI network configuration and joins the
+// rootless net ns as rootless user
+func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error {
+ rootlessCNINS, err := r.getRootlessCNINetNs(false)
+ if err != nil {
+ return err
+ }
+ tearDownPod := func() error {
+ err := r.netPlugin.TearDownPod(podNetwork)
+ return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", podNetwork.ID)
+ }
+
+ // rootlessCNINS is nil if we are root
+ if rootlessCNINS != nil {
+ // execute the cni setup in the rootless net ns
+ err = rootlessCNINS.Do(tearDownPod)
+ if err == nil {
+ err = rootlessCNINS.cleanup(r)
+ }
+ } else {
+ err = tearDownPod()
+ }
+ return err
+}
+
// Tear down a container's CNI network configuration, but do not tear down the
// namespace itself.
func (r *Runtime) teardownCNI(ctr *Container) error {
@@ -789,8 +678,7 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
return err
}
- // rootless containers do not use the CNI plugin directly
- if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 {
+ if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 {
var requestedIP net.IP
if ctr.requestedIP != nil {
requestedIP = ctr.requestedIP
@@ -810,10 +698,8 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
}
podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions)
-
- if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
- return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
- }
+ err = r.teardownOCICNIPod(podNetwork)
+ return err
}
return nil
}
@@ -824,18 +710,6 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
return err
}
- networks, _, err := ctr.networks()
- if err != nil {
- return err
- }
-
- // CNI-in-slirp4netns
- if rootless.IsRootless() && len(networks) != 0 {
- if err := DeallocRootlessCNI(context.Background(), ctr); err != nil {
- return errors.Wrapf(err, "error tearing down CNI-in-slirp4netns for container %s", ctr.ID())
- }
- }
-
// First unmount the namespace
if err := netns.UnmountNS(ctr.state.NetNS); err != nil {
return errors.Wrapf(err, "error unmounting network namespace for container %s", ctr.ID())
@@ -1177,7 +1051,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
}
podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions)
- if err := c.runtime.netPlugin.TearDownPod(podConfig); err != nil {
+ if err := c.runtime.teardownOCICNIPod(podConfig); err != nil {
return err
}
@@ -1241,7 +1115,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions)
podConfig.Aliases = make(map[string][]string, 1)
podConfig.Aliases[netName] = aliases
- results, err := c.runtime.netPlugin.SetUpPod(podConfig)
+ results, err := c.runtime.setUpOCICNIPod(podConfig)
if err != nil {
return err
}
@@ -1288,9 +1162,6 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
// DisconnectContainerFromNetwork removes a container from its CNI network
func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error {
- if rootless.IsRootless() {
- return errors.New("network connect is not enabled for rootless containers")
- }
ctr, err := r.LookupContainer(nameOrID)
if err != nil {
return err
@@ -1300,9 +1171,6 @@ func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force
// ConnectContainerToNetwork connects a container to a CNI network
func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, aliases []string) error {
- if rootless.IsRootless() {
- return errors.New("network disconnect is not enabled for rootless containers")
- }
ctr, err := r.LookupContainer(nameOrID)
if err != nil {
return err
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
new file mode 100644
index 000000000..72ab3c919
--- /dev/null
+++ b/libpod/networking_slirp4netns.go
@@ -0,0 +1,538 @@
+// +build linux
+
+package libpod
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/containers/podman/v3/pkg/errorhandling"
+ "github.com/containers/podman/v3/pkg/rootlessport"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+type slirpFeatures struct {
+ HasDisableHostLoopback bool
+ HasMTU bool
+ HasEnableSandbox bool
+ HasEnableSeccomp bool
+ HasCIDR bool
+ HasOutboundAddr bool
+ HasIPv6 bool
+}
+
+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"`
+}
+
+type slirp4netnsNetworkOptions struct {
+ cidr string
+ disableHostLoopback bool
+ enableIPv6 bool
+ isSlirpHostForward bool
+ noPivotRoot bool
+ mtu int
+ outboundAddr string
+ outboundAddr6 string
+}
+
+func checkSlirpFlags(path string) (*slirpFeatures, error) {
+ cmd := exec.Command(path, "--help")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return nil, errors.Wrapf(err, "slirp4netns %q", out)
+ }
+ return &slirpFeatures{
+ HasDisableHostLoopback: strings.Contains(string(out), "--disable-host-loopback"),
+ HasMTU: strings.Contains(string(out), "--mtu"),
+ HasEnableSandbox: strings.Contains(string(out), "--enable-sandbox"),
+ HasEnableSeccomp: strings.Contains(string(out), "--enable-seccomp"),
+ HasCIDR: strings.Contains(string(out), "--cidr"),
+ HasOutboundAddr: strings.Contains(string(out), "--outbound-addr"),
+ HasIPv6: strings.Contains(string(out), "--enable-ipv6"),
+ }, nil
+}
+
+func parseSlirp4netnsNetworkOptions(r *Runtime, extraOptions []string) (*slirp4netnsNetworkOptions, error) {
+ slirpOptions := append(r.config.Engine.NetworkCmdOptions, extraOptions...)
+ slirp4netnsOpts := &slirp4netnsNetworkOptions{
+ // overwrite defaults
+ disableHostLoopback: true,
+ mtu: slirp4netnsMTU,
+ noPivotRoot: r.config.Engine.NoPivotRoot,
+ }
+ for _, o := range slirpOptions {
+ parts := strings.SplitN(o, "=", 2)
+ if len(parts) < 2 {
+ return nil, errors.Errorf("unknown option for slirp4netns: %q", o)
+ }
+ option, value := parts[0], parts[1]
+ switch option {
+ case "cidr":
+ ipv4, _, err := net.ParseCIDR(value)
+ if err != nil || ipv4.To4() == nil {
+ return nil, errors.Errorf("invalid cidr %q", value)
+ }
+ slirp4netnsOpts.cidr = value
+ case "port_handler":
+ switch value {
+ case "slirp4netns":
+ slirp4netnsOpts.isSlirpHostForward = true
+ case "rootlesskit":
+ slirp4netnsOpts.isSlirpHostForward = false
+ default:
+ return nil, errors.Errorf("unknown port_handler for slirp4netns: %q", value)
+ }
+ case "allow_host_loopback":
+ switch value {
+ case "true":
+ slirp4netnsOpts.disableHostLoopback = false
+ case "false":
+ slirp4netnsOpts.disableHostLoopback = true
+ default:
+ return nil, errors.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value)
+ }
+ case "enable_ipv6":
+ switch value {
+ case "true":
+ slirp4netnsOpts.enableIPv6 = true
+ case "false":
+ slirp4netnsOpts.enableIPv6 = false
+ default:
+ return nil, errors.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value)
+ }
+ case "outbound_addr":
+ ipv4 := net.ParseIP(value)
+ if ipv4 == nil || ipv4.To4() == nil {
+ _, err := net.InterfaceByName(value)
+ if err != nil {
+ return nil, errors.Errorf("invalid outbound_addr %q", value)
+ }
+ }
+ slirp4netnsOpts.outboundAddr = value
+ case "outbound_addr6":
+ ipv6 := net.ParseIP(value)
+ if ipv6 == nil || ipv6.To4() != nil {
+ _, err := net.InterfaceByName(value)
+ if err != nil {
+ return nil, errors.Errorf("invalid outbound_addr6: %q", value)
+ }
+ }
+ slirp4netnsOpts.outboundAddr6 = value
+ case "mtu":
+ var err error
+ slirp4netnsOpts.mtu, err = strconv.Atoi(value)
+ if slirp4netnsOpts.mtu < 68 || err != nil {
+ return nil, errors.Errorf("invalid mtu %q", value)
+ }
+ default:
+ return nil, errors.Errorf("unknown option for slirp4netns: %q", o)
+ }
+ }
+ return slirp4netnsOpts, nil
+}
+
+func createBasicSlirp4netnsCmdArgs(options *slirp4netnsNetworkOptions, features *slirpFeatures) ([]string, error) {
+ cmdArgs := []string{}
+ if options.disableHostLoopback && features.HasDisableHostLoopback {
+ cmdArgs = append(cmdArgs, "--disable-host-loopback")
+ }
+ if options.mtu > -1 && features.HasMTU {
+ cmdArgs = append(cmdArgs, fmt.Sprintf("--mtu=%d", options.mtu))
+ }
+ if !options.noPivotRoot && features.HasEnableSandbox {
+ cmdArgs = append(cmdArgs, "--enable-sandbox")
+ }
+ if features.HasEnableSeccomp {
+ cmdArgs = append(cmdArgs, "--enable-seccomp")
+ }
+
+ if options.cidr != "" {
+ if !features.HasCIDR {
+ return nil, errors.Errorf("cidr not supported")
+ }
+ cmdArgs = append(cmdArgs, fmt.Sprintf("--cidr=%s", options.cidr))
+ }
+
+ if options.enableIPv6 {
+ if !features.HasIPv6 {
+ return nil, errors.Errorf("enable_ipv6 not supported")
+ }
+ cmdArgs = append(cmdArgs, "--enable-ipv6")
+ }
+
+ if options.outboundAddr != "" {
+ if !features.HasOutboundAddr {
+ return nil, errors.Errorf("outbound_addr not supported")
+ }
+ cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr=%s", options.outboundAddr))
+ }
+
+ if options.outboundAddr6 != "" {
+ if !features.HasOutboundAddr || !features.HasIPv6 {
+ return nil, errors.Errorf("outbound_addr6 not supported")
+ }
+ if !options.enableIPv6 {
+ return nil, errors.Errorf("enable_ipv6=true is required for outbound_addr6")
+ }
+ cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr6=%s", options.outboundAddr6))
+ }
+
+ return cmdArgs, nil
+}
+
+// setupSlirp4netns can be called in rootful as well as in rootless
+func (r *Runtime) setupSlirp4netns(ctr *Container) error {
+ path := r.config.Engine.NetworkCmdPath
+ if path == "" {
+ var err error
+ path, err = exec.LookPath("slirp4netns")
+ if err != nil {
+ logrus.Errorf("could not find slirp4netns, the network namespace won't be configured: %v", err)
+ return nil
+ }
+ }
+
+ syncR, syncW, err := os.Pipe()
+ if err != nil {
+ return errors.Wrapf(err, "failed to open pipe")
+ }
+ defer errorhandling.CloseQuiet(syncR)
+ defer errorhandling.CloseQuiet(syncW)
+
+ havePortMapping := len(ctr.Config().PortMappings) > 0
+ logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID))
+
+ ctrNetworkSlipOpts := []string{}
+ if ctr.config.NetworkOptions != nil {
+ ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, ctr.config.NetworkOptions["slirp4netns"]...)
+ }
+ netOptions, err := parseSlirp4netnsNetworkOptions(r, ctrNetworkSlipOpts)
+ if err != nil {
+ return err
+ }
+ slirpFeatures, err := checkSlirpFlags(path)
+ if err != nil {
+ return errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err)
+ }
+ cmdArgs, err := createBasicSlirp4netnsCmdArgs(netOptions, slirpFeatures)
+ if err != nil {
+ return err
+ }
+
+ // the slirp4netns arguments being passed are describes as follows:
+ // from the slirp4netns documentation: https://github.com/rootless-containers/slirp4netns
+ // -c, --configure Brings up the tap interface
+ // -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")
+
+ var apiSocket string
+ if havePortMapping && netOptions.isSlirpHostForward {
+ apiSocket = filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
+ cmdArgs = append(cmdArgs, "--api-socket", apiSocket)
+ }
+ 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")
+ }
+ 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")
+ }
+
+ cmd := exec.Command(path, cmdArgs...)
+ logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " "))
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Setpgid: true,
+ }
+
+ // workaround for https://github.com/rootless-containers/slirp4netns/pull/153
+ if !netOptions.noPivotRoot && slirpFeatures.HasEnableSandbox {
+ cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS
+ cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS
+ }
+
+ // Leak one end of the pipe in slirp4netns, the other will be sent to conmon
+ cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncR, syncW)
+
+ logFile, err := os.Create(logPath)
+ if err != nil {
+ return errors.Wrapf(err, "failed to open slirp4netns 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)
+ }
+ cmd.Stdout = logFile
+ cmd.Stderr = logFile
+ if err := cmd.Start(); err != nil {
+ return errors.Wrapf(err, "failed to start slirp4netns process")
+ }
+ defer func() {
+ if err := cmd.Process.Release(); err != nil {
+ logrus.Errorf("unable to release command process: %q", err)
+ }
+ }()
+
+ if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
+ return err
+ }
+
+ if havePortMapping {
+ if netOptions.isSlirpHostForward {
+ return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket)
+ }
+ return r.setupRootlessPortMappingViaRLK(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(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 {
+ if os.IsTimeout(err) {
+ // 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 {
+ return errors.Wrapf(err, "failed to read %s process status", prog)
+ }
+ if pid != cmd.Process.Pid {
+ continue
+ }
+ if status.Exited() {
+ // Seek at the beginning of the file and read all its content
+ if _, err := logFile.Seek(0, 0); err != nil {
+ logrus.Errorf("could not seek log file: %q", err)
+ }
+ logContent, err := ioutil.ReadAll(logFile)
+ if err != nil {
+ return errors.Wrapf(err, "%s failed", prog)
+ }
+ return errors.Errorf("%s failed: %q", prog, logContent)
+ }
+ if status.Signaled() {
+ return errors.Errorf("%s killed by signal", prog)
+ }
+ continue
+ }
+ return errors.Wrapf(err, "failed to read from %s sync pipe", prog)
+ }
+ }
+ return nil
+}
+
+func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string) 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)
+
+ logPath := filepath.Join(ctr.runtime.config.Engine.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)
+ }
+
+ if !ctr.config.PostConfigureNetNS {
+ ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe()
+ if err != nil {
+ return errors.Wrapf(err, "failed to create rootless port sync pipe")
+ }
+ }
+
+ childIP := slirp4netnsIP
+outer:
+ for _, r := range ctr.state.NetworkStatus {
+ for _, i := range r.IPs {
+ ipv4 := i.Address.IP.To4()
+ if ipv4 != nil {
+ childIP = ipv4.String()
+ break outer
+ }
+ }
+ }
+
+ cfg := rootlessport.Config{
+ Mappings: ctr.config.PortMappings,
+ NetNSPath: netnsPath,
+ ExitFD: 3,
+ ReadyFD: 4,
+ TmpDir: ctr.runtime.config.Engine.TmpDir,
+ ChildIP: childIP,
+ }
+ 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
+
+ if ctr.rootlessPortSyncR != nil {
+ defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR)
+ }
+
+ 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("rootlessport " + strings.TrimSuffix(stdoutStr, "\n"))
+ }
+ return err
+ }
+ logrus.Debug("rootlessport is ready")
+ return nil
+}
+
+func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd, apiSocket string) (err error) {
+ 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)
+
+ // 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)
+ }
+
+ // 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"
+ }
+ apiCmd := 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(&apiCmd)
+ 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)
+ }
+ }
+ logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready")
+ return nil
+}
diff --git a/libpod/options.go b/libpod/options.go
index 24e9d74f4..333a7c4a5 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -77,8 +77,7 @@ func WithStorageConfig(config storage.StoreOptions) RuntimeOption {
rt.storageConfig.GraphDriverOptions = make([]string, len(config.GraphDriverOptions))
copy(rt.storageConfig.GraphDriverOptions, config.GraphDriverOptions)
} else {
- // append new options after what is specified in the config files
- rt.storageConfig.GraphDriverOptions = append(rt.storageConfig.GraphDriverOptions, config.GraphDriverOptions...)
+ rt.storageConfig.GraphDriverOptions = config.GraphDriverOptions
}
setField = true
}
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
deleted file mode 100644
index df690e914..000000000
--- a/libpod/rootless_cni_linux.go
+++ /dev/null
@@ -1,372 +0,0 @@
-// +build linux
-
-package libpod
-
-import (
- "bytes"
- "context"
- "io"
- "path/filepath"
- "runtime"
-
- cnitypes "github.com/containernetworking/cni/pkg/types/current"
- "github.com/containernetworking/plugins/pkg/ns"
- "github.com/containers/podman/v3/libpod/define"
- "github.com/containers/podman/v3/libpod/image"
- "github.com/containers/podman/v3/pkg/env"
- "github.com/containers/podman/v3/pkg/util"
- "github.com/containers/storage/pkg/lockfile"
- "github.com/hashicorp/go-multierror"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// Built from ../contrib/rootless-cni-infra.
-var rootlessCNIInfraImage = map[string]string{
- "amd64": "quay.io/libpod/rootless-cni-infra@sha256:adf352454666f7ce9ca3e1098448b5ee18f89c4516471ec99447ec9ece917f36", // 5-amd64
-}
-
-const (
- rootlessCNIInfraContainerNamespace = "podman-system"
- rootlessCNIInfraContainerName = "rootless-cni-infra"
-)
-
-// AllocRootlessCNI allocates a CNI netns inside the rootless CNI infra container.
-// Locks "rootless-cni-infra.lck".
-//
-// When the infra container is not running, it is created.
-//
-// AllocRootlessCNI does not lock c. c should be already locked.
-func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.Result, error) {
- networks, _, err := c.networks()
- if err != nil {
- return nil, nil, err
- }
- if len(networks) == 0 {
- return nil, nil, errors.New("rootless CNI networking requires that the container has joined at least one CNI network")
- }
- l, err := getRootlessCNIInfraLock(c.runtime)
- if err != nil {
- return nil, nil, err
- }
- l.Lock()
- defer l.Unlock()
- infra, err := ensureRootlessCNIInfraContainerRunning(ctx, c.runtime)
- if err != nil {
- return nil, nil, err
- }
- k8sPodName := getCNIPodName(c) // passed to CNI as K8S_POD_NAME
- ip := ""
- if c.config.StaticIP != nil {
- ip = c.config.StaticIP.String()
- }
- mac := ""
- if c.config.StaticMAC != nil {
- mac = c.config.StaticMAC.String()
- }
- aliases, err := c.runtime.state.GetAllNetworkAliases(c)
- if err != nil {
- return nil, nil, err
- }
- capArgs := ""
- // add network aliases json encoded as capabilityArgs for cni
- if len(aliases) > 0 {
- capabilityArgs := make(map[string]interface{})
- capabilityArgs["aliases"] = aliases
- b, err := json.Marshal(capabilityArgs)
- if err != nil {
- return nil, nil, err
- }
- capArgs = string(b)
- }
-
- cniResults := make([]*cnitypes.Result, len(networks))
- for i, nw := range networks {
- cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName, ip, mac, capArgs)
- if err != nil {
- return nil, nil, err
- }
- cniResults[i] = cniRes
- }
- nsObj, err := rootlessCNIInfraGetNS(infra, c.ID())
- if err != nil {
- return nil, nil, err
- }
- logrus.Debugf("rootless CNI: container %q will join %q", c.ID(), nsObj.Path())
- return nsObj, cniResults, nil
-}
-
-// DeallocRootlessCNI deallocates a CNI netns inside the rootless CNI infra container.
-// Locks "rootless-cni-infra.lck".
-//
-// When the infra container is no longer needed, it is removed.
-//
-// DeallocRootlessCNI does not lock c. c should be already locked.
-func DeallocRootlessCNI(ctx context.Context, c *Container) error {
- networks, _, err := c.networks()
- if err != nil {
- return err
- }
- if len(networks) == 0 {
- return errors.New("rootless CNI networking requires that the container has joined at least one CNI network")
- }
- l, err := getRootlessCNIInfraLock(c.runtime)
- if err != nil {
- return err
- }
- l.Lock()
- defer l.Unlock()
- infra, _ := getRootlessCNIInfraContainer(c.runtime)
- if infra == nil {
- return nil
- }
- var errs *multierror.Error
- for _, nw := range networks {
- err := rootlessCNIInfraCallDealloc(infra, c.ID(), nw)
- if err != nil {
- errs = multierror.Append(errs, err)
- }
- }
- if isIdle, err := rootlessCNIInfraIsIdle(infra); isIdle || err != nil {
- if err != nil {
- logrus.Warn(err)
- }
- logrus.Debugf("rootless CNI: removing infra container %q", infra.ID())
- infra.lock.Lock()
- defer infra.lock.Unlock()
- if err := c.runtime.removeContainer(ctx, infra, true, false, true); err != nil {
- return err
- }
- logrus.Debugf("rootless CNI: removed infra container %q", infra.ID())
- }
- return errs.ErrorOrNil()
-}
-
-func getRootlessCNIInfraLock(r *Runtime) (lockfile.Locker, error) {
- fname := filepath.Join(r.config.Engine.TmpDir, "rootless-cni-infra.lck")
- return lockfile.GetLockfile(fname)
-}
-
-// getCNIPodName return the pod name (hostname) used by CNI and the dnsname plugin.
-// If we are in the pod network namespace use the pod name otherwise the container name
-func getCNIPodName(c *Container) string {
- if c.config.NetMode.IsPod() || c.IsInfra() {
- pod, err := c.runtime.GetPod(c.PodID())
- if err == nil {
- return pod.Name()
- }
- }
- return c.Name()
-}
-
-func rootlessCNIInfraCallAlloc(infra *Container, id, nw, k8sPodName, ip, mac, capArgs string) (*cnitypes.Result, error) {
- logrus.Debugf("rootless CNI: alloc %q, %q, %q, %q, %q, %q", id, nw, k8sPodName, ip, mac, capArgs)
- var err error
-
- _, err = rootlessCNIInfraExec(infra, "alloc", id, nw, k8sPodName, ip, mac, capArgs)
- if err != nil {
- return nil, err
- }
- cniResStr, err := rootlessCNIInfraExec(infra, "print-cni-result", id, nw)
- if err != nil {
- return nil, err
- }
- var cniRes cnitypes.Result
- if err := json.Unmarshal([]byte(cniResStr), &cniRes); err != nil {
- return nil, errors.Wrapf(err, "unmarshaling as cnitypes.Result: %q", cniResStr)
- }
- return &cniRes, nil
-}
-
-func rootlessCNIInfraCallDealloc(infra *Container, id, nw string) error {
- logrus.Debugf("rootless CNI: dealloc %q, %q", id, nw)
- _, err := rootlessCNIInfraExec(infra, "dealloc", id, nw)
- return err
-}
-
-func rootlessCNIInfraIsIdle(infra *Container) (bool, error) {
- type isIdle struct {
- Idle bool `json:"idle"`
- }
- resStr, err := rootlessCNIInfraExec(infra, "is-idle")
- if err != nil {
- return false, err
- }
- var res isIdle
- if err := json.Unmarshal([]byte(resStr), &res); err != nil {
- return false, errors.Wrapf(err, "unmarshaling as isIdle: %q", resStr)
- }
- return res.Idle, nil
-}
-
-func rootlessCNIInfraGetNS(infra *Container, id string) (ns.NetNS, error) {
- type printNetnsPath struct {
- Path string `json:"path"`
- }
- resStr, err := rootlessCNIInfraExec(infra, "print-netns-path", id)
- if err != nil {
- return nil, err
- }
- var res printNetnsPath
- if err := json.Unmarshal([]byte(resStr), &res); err != nil {
- return nil, errors.Wrapf(err, "unmarshaling as printNetnsPath: %q", resStr)
- }
- nsObj, err := ns.GetNS(res.Path)
- if err != nil {
- return nil, err
- }
- return nsObj, nil
-}
-
-func getRootlessCNIInfraContainer(r *Runtime) (*Container, error) {
- containers, err := r.GetContainersWithoutLock(func(c *Container) bool {
- return c.Namespace() == rootlessCNIInfraContainerNamespace &&
- c.Name() == rootlessCNIInfraContainerName
- })
- if err != nil {
- return nil, err
- }
- if len(containers) == 0 {
- return nil, nil
- }
- return containers[0], nil
-}
-
-func ensureRootlessCNIInfraContainerRunning(ctx context.Context, r *Runtime) (*Container, error) {
- c, err := getRootlessCNIInfraContainer(r)
- if err != nil {
- return nil, err
- }
- if c == nil {
- return startRootlessCNIInfraContainer(ctx, r)
- }
- st, err := c.ContainerState()
- if err != nil {
- return nil, err
- }
- if st.State == define.ContainerStateRunning {
- logrus.Debugf("rootless CNI: infra container %q is already running", c.ID())
- return c, nil
- }
- logrus.Debugf("rootless CNI: infra container %q is %q, being started", c.ID(), st.State)
- if err := c.initAndStart(ctx); err != nil {
- return nil, err
- }
- logrus.Debugf("rootless CNI: infra container %q is running", c.ID())
- return c, nil
-}
-
-func startRootlessCNIInfraContainer(ctx context.Context, r *Runtime) (*Container, error) {
- imageName, ok := rootlessCNIInfraImage[runtime.GOARCH]
- if !ok {
- return nil, errors.Errorf("cannot find rootless-podman-network-sandbox image for %s", runtime.GOARCH)
- }
- logrus.Debugf("rootless CNI: ensuring image %q to exist", imageName)
- newImage, err := r.ImageRuntime().New(ctx, imageName, "", "", nil, nil,
- image.SigningOptions{}, nil, util.PullImageMissing, nil)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("rootless CNI: image %q is ready", imageName)
-
- g, err := generate.New("linux")
- if err != nil {
- return nil, err
- }
- g.SetupPrivileged(true)
- // Set --pid=host for ease of propagating "/proc/PID/ns/net" string
- if err := g.RemoveLinuxNamespace(string(spec.PIDNamespace)); err != nil {
- return nil, err
- }
- g.RemoveMount("/proc")
- procMount := spec.Mount{
- Destination: "/proc",
- Type: "bind",
- Source: "/proc",
- Options: []string{"rbind", "nosuid", "noexec", "nodev"},
- }
- g.AddMount(procMount)
- // Mount CNI networks
- etcCNINetD := spec.Mount{
- Destination: "/etc/cni/net.d",
- Type: "bind",
- Source: r.config.Network.NetworkConfigDir,
- Options: []string{"ro", "bind"},
- }
- g.AddMount(etcCNINetD)
-
- inspectData, err := newImage.Inspect(ctx)
- if err != nil {
- return nil, err
- }
- imageEnv, err := env.ParseSlice(inspectData.Config.Env)
- if err != nil {
- return nil, err
- }
- for k, v := range imageEnv {
- g.AddProcessEnv(k, v)
- }
- if len(inspectData.Config.Cmd) == 0 {
- return nil, errors.Errorf("rootless CNI infra image %q has no command specified", imageName)
- }
- g.SetProcessArgs(inspectData.Config.Cmd)
-
- var options []CtrCreateOption
- options = append(options, WithRootFSFromImage(newImage.ID(), imageName, imageName))
- options = append(options, WithCtrNamespace(rootlessCNIInfraContainerNamespace))
- options = append(options, WithName(rootlessCNIInfraContainerName))
- options = append(options, WithPrivileged(true))
- options = append(options, WithSecLabels([]string{"disable"}))
- options = append(options, WithRestartPolicy("always"))
- options = append(options, WithNetNS(nil, false, "slirp4netns", nil))
- c, err := r.NewContainer(ctx, g.Config, options...)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("rootless CNI infra container %q is created, now being started", c.ID())
- if err := c.initAndStart(ctx); err != nil {
- return nil, err
- }
- logrus.Debugf("rootless CNI: infra container %q is running", c.ID())
-
- return c, nil
-}
-
-func rootlessCNIInfraExec(c *Container, args ...string) (string, error) {
- cmd := "rootless-cni-infra"
- var (
- outB bytes.Buffer
- errB bytes.Buffer
- streams define.AttachStreams
- config ExecConfig
- )
- streams.OutputStream = &nopWriteCloser{Writer: &outB}
- streams.ErrorStream = &nopWriteCloser{Writer: &errB}
- streams.AttachOutput = true
- streams.AttachError = true
- config.Command = append([]string{cmd}, args...)
- config.Privileged = true
- logrus.Debugf("rootlessCNIInfraExec: c.ID()=%s, config=%+v, streams=%v, begin",
- c.ID(), config, streams)
- code, err := c.Exec(&config, &streams, nil)
- logrus.Debugf("rootlessCNIInfraExec: c.ID()=%s, config=%+v, streams=%v, end (code=%d, err=%v)",
- c.ID(), config, streams, code, err)
- if err != nil {
- return "", err
- }
- if code != 0 {
- return "", errors.Errorf("command %s %v in container %s failed with status %d, stdout=%q, stderr=%q",
- cmd, args, c.ID(), code, outB.String(), errB.String())
- }
- return outB.String(), nil
-}
-
-type nopWriteCloser struct {
- io.Writer
-}
-
-func (nwc *nopWriteCloser) Close() error {
- return nil
-}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 201482c65..d4bb691ef 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -436,13 +436,11 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
}
// Set up the CNI net plugin
- if !rootless.IsRootless() {
- netPlugin, err := ocicni.InitCNI(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, runtime.config.Network.CNIPluginDirs...)
- if err != nil {
- return errors.Wrapf(err, "error configuring CNI network plugin")
- }
- runtime.netPlugin = netPlugin
+ netPlugin, err := ocicni.InitCNI(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, runtime.config.Network.CNIPluginDirs...)
+ if err != nil {
+ return errors.Wrapf(err, "error configuring CNI network plugin")
}
+ runtime.netPlugin = netPlugin
// We now need to see if the system has restarted
// We check for the presence of a file in our tmp directory to verify this
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index 0a09e40ea..1ae375ed9 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -104,7 +104,7 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
default:
// Since user namespace sharing is not implemented, we only need to check if it's rootless
netmode := "bridge"
- if isRootless || p.config.InfraContainer.Slirp4netns {
+ if p.config.InfraContainer.Slirp4netns {
netmode = "slirp4netns"
if len(p.config.InfraContainer.NetworkOptions) != 0 {
options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions))
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 6dac789d6..ab92434b1 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"strconv"
+ "strings"
"time"
"github.com/containers/buildah"
@@ -63,52 +64,55 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}()
query := struct {
- AddHosts string `schema:"extrahosts"`
- AdditionalCapabilities string `schema:"addcaps"`
- Annotations string `schema:"annotations"`
- BuildArgs string `schema:"buildargs"`
- CacheFrom string `schema:"cachefrom"`
- Compression uint64 `schema:"compression"`
- ConfigureNetwork string `schema:"networkmode"`
- CpuPeriod uint64 `schema:"cpuperiod"` // nolint
- CpuQuota int64 `schema:"cpuquota"` // nolint
- CpuSetCpus string `schema:"cpusetcpus"` // nolint
- CpuShares uint64 `schema:"cpushares"` // nolint
- Devices string `schema:"devices"`
- Dockerfile string `schema:"dockerfile"`
- DropCapabilities string `schema:"dropcaps"`
- DNSServers string `schema:"dnsservers"`
- DNSOptions string `schema:"dnsoptions"`
- DNSSearch string `schema:"dnssearch"`
- Excludes string `schema:"excludes"`
- ForceRm bool `schema:"forcerm"`
- From string `schema:"from"`
- HTTPProxy bool `schema:"httpproxy"`
- Isolation string `schema:"isolation"`
- Ignore bool `schema:"ignore"`
- Jobs int `schema:"jobs"` // nolint
- Labels string `schema:"labels"`
- Layers bool `schema:"layers"`
- LogRusage bool `schema:"rusage"`
- Manifest string `schema:"manifest"`
- MemSwap int64 `schema:"memswap"`
- Memory int64 `schema:"memory"`
- NamespaceOptions string `schema:"nsoptions"`
- NoCache bool `schema:"nocache"`
- OutputFormat string `schema:"outputformat"`
- Platform string `schema:"platform"`
- Pull bool `schema:"pull"`
- PullPolicy string `schema:"pullpolicy"`
- Quiet bool `schema:"q"`
- Registry string `schema:"registry"`
- Rm bool `schema:"rm"`
- //FIXME SecurityOpt in remote API is not handled
- SecurityOpt string `schema:"securityopt"`
- ShmSize int `schema:"shmsize"`
- Squash bool `schema:"squash"`
- Tag []string `schema:"t"`
- Target string `schema:"target"`
- Timestamp int64 `schema:"timestamp"`
+ AddHosts string `schema:"extrahosts"`
+ AdditionalCapabilities string `schema:"addcaps"`
+ Annotations string `schema:"annotations"`
+ AppArmor string `schema:"apparmor"`
+ BuildArgs string `schema:"buildargs"`
+ CacheFrom string `schema:"cachefrom"`
+ Compression uint64 `schema:"compression"`
+ ConfigureNetwork string `schema:"networkmode"`
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
+ DNSOptions string `schema:"dnsoptions"`
+ DNSSearch string `schema:"dnssearch"`
+ DNSServers string `schema:"dnsservers"`
+ Devices string `schema:"devices"`
+ Dockerfile string `schema:"dockerfile"`
+ DropCapabilities string `schema:"dropcaps"`
+ Excludes string `schema:"excludes"`
+ ForceRm bool `schema:"forcerm"`
+ From string `schema:"from"`
+ HTTPProxy bool `schema:"httpproxy"`
+ Ignore bool `schema:"ignore"`
+ Isolation string `schema:"isolation"`
+ Jobs int `schema:"jobs"` // nolint
+ LabelOpts string `schema:"labelopts"`
+ Labels string `schema:"labels"`
+ Layers bool `schema:"layers"`
+ LogRusage bool `schema:"rusage"`
+ Manifest string `schema:"manifest"`
+ MemSwap int64 `schema:"memswap"`
+ Memory int64 `schema:"memory"`
+ NamespaceOptions string `schema:"nsoptions"`
+ NoCache bool `schema:"nocache"`
+ OutputFormat string `schema:"outputformat"`
+ Platform string `schema:"platform"`
+ Pull bool `schema:"pull"`
+ PullPolicy string `schema:"pullpolicy"`
+ Quiet bool `schema:"q"`
+ Registry string `schema:"registry"`
+ Rm bool `schema:"rm"`
+ Seccomp string `schema:"seccomp"`
+ SecurityOpt string `schema:"securityopt"`
+ ShmSize int `schema:"shmsize"`
+ Squash bool `schema:"squash"`
+ Tag []string `schema:"t"`
+ Target string `schema:"target"`
+ Timestamp int64 `schema:"timestamp"`
+ Ulimits string `schema:"ulimits"`
}{
Dockerfile: "Dockerfile",
Registry: "docker.io",
@@ -123,7 +127,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
return
}
- // convert label formats
+ // convert addcaps formats
var addCaps = []string{}
if _, found := r.URL.Query()["addcaps"]; found {
var m = []string{}
@@ -133,6 +137,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
addCaps = m
}
+
addhosts := []string{}
if _, found := r.URL.Query()["extrahosts"]; found {
if err := json.Unmarshal([]byte(query.AddHosts), &addhosts); err != nil {
@@ -142,7 +147,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
compression := archive.Compression(query.Compression)
- // convert label formats
+
+ // convert dropcaps formats
var dropCaps = []string{}
if _, found := r.URL.Query()["dropcaps"]; found {
var m = []string{}
@@ -153,7 +159,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
dropCaps = m
}
- // convert label formats
+ // convert devices formats
var devices = []string{}
if _, found := r.URL.Query()["devices"]; found {
var m = []string{}
@@ -233,7 +239,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
- // convert label formats
+ // convert annotations formats
var annotations = []string{}
if _, found := r.URL.Query()["annotations"]; found {
if err := json.Unmarshal([]byte(query.Annotations), &annotations); err != nil {
@@ -242,7 +248,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
- // convert label formats
+ // convert nsoptions formats
nsoptions := buildah.NamespaceOptions{}
if _, found := r.URL.Query()["nsoptions"]; found {
if err := json.Unmarshal([]byte(query.NamespaceOptions), &nsoptions); err != nil {
@@ -271,11 +277,75 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
}
+
jobs := 1
if _, found := r.URL.Query()["jobs"]; found {
jobs = query.Jobs
}
+ var (
+ labelOpts = []string{}
+ seccomp string
+ apparmor string
+ )
+
+ if utils.IsLibpodRequest(r) {
+ seccomp = query.Seccomp
+ apparmor = query.AppArmor
+ // convert labelopts formats
+ if _, found := r.URL.Query()["labelopts"]; found {
+ var m = []string{}
+ if err := json.Unmarshal([]byte(query.LabelOpts), &m); err != nil {
+ utils.BadRequest(w, "labelopts", query.LabelOpts, err)
+ return
+ }
+ labelOpts = m
+ }
+ } else {
+ // handle security-opt
+ if _, found := r.URL.Query()["securityopt"]; found {
+ var securityOpts = []string{}
+ if err := json.Unmarshal([]byte(query.SecurityOpt), &securityOpts); err != nil {
+ utils.BadRequest(w, "securityopt", query.SecurityOpt, err)
+ return
+ }
+ for _, opt := range securityOpts {
+ if opt == "no-new-privileges" {
+ utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.New("no-new-privileges is not supported"))
+ return
+ }
+ con := strings.SplitN(opt, "=", 2)
+ if len(con) != 2 {
+ utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt name=value pair: %q", opt))
+ return
+ }
+
+ switch con[0] {
+ case "label":
+ labelOpts = append(labelOpts, con[1])
+ case "apparmor":
+ apparmor = con[1]
+ case "seccomp":
+ seccomp = con[1]
+ default:
+ utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt 2: %q", opt))
+ return
+ }
+ }
+ }
+ }
+
+ // convert ulimits formats
+ var ulimits = []string{}
+ if _, found := r.URL.Query()["ulimits"]; found {
+ var m = []string{}
+ if err := json.Unmarshal([]byte(query.Ulimits), &m); err != nil {
+ utils.BadRequest(w, "ulimits", query.Ulimits, err)
+ return
+ }
+ ulimits = m
+ }
+
pullPolicy := buildahDefine.PullIfMissing
if utils.IsLibpodRequest(r) {
pullPolicy = buildahDefine.PolicyMap[query.PullPolicy]
@@ -320,18 +390,22 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Annotations: annotations,
Args: buildArgs,
CommonBuildOpts: &buildah.CommonBuildOptions{
- AddHost: addhosts,
- CPUPeriod: query.CpuPeriod,
- CPUQuota: query.CpuQuota,
- CPUShares: query.CpuShares,
- CPUSetCPUs: query.CpuSetCpus,
- DNSServers: dnsservers,
- DNSOptions: dnsoptions,
- DNSSearch: dnssearch,
- HTTPProxy: query.HTTPProxy,
- Memory: query.Memory,
- MemorySwap: query.MemSwap,
- ShmSize: strconv.Itoa(query.ShmSize),
+ AddHost: addhosts,
+ ApparmorProfile: apparmor,
+ CPUPeriod: query.CpuPeriod,
+ CPUQuota: query.CpuQuota,
+ CPUSetCPUs: query.CpuSetCpus,
+ CPUShares: query.CpuShares,
+ DNSOptions: dnsoptions,
+ DNSSearch: dnssearch,
+ DNSServers: dnsservers,
+ HTTPProxy: query.HTTPProxy,
+ LabelOpts: labelOpts,
+ Memory: query.Memory,
+ MemorySwap: query.MemSwap,
+ SeccompProfilePath: seccomp,
+ ShmSize: strconv.Itoa(query.ShmSize),
+ Ulimit: ulimits,
},
CNIConfigDir: rtc.Network.CNIPluginDirs[0],
CNIPluginPath: util.DefaultCNIPluginPath,
@@ -364,11 +438,11 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
RemoveIntermediateCtrs: query.Rm,
ReportWriter: reporter,
Squash: query.Squash,
+ Target: query.Target,
SystemContext: &types.SystemContext{
AuthFilePath: authfile,
DockerAuthConfig: creds,
},
- Target: query.Target,
}
if _, found := r.URL.Query()["timestamp"]; found {
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index c79d79136..c47a16551 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -120,6 +120,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if options.ForceRmIntermediateCtrs {
params.Set("forcerm", "1")
}
+ if options.RemoveIntermediateCtrs {
+ params.Set("rm", "1")
+ }
if len(options.From) > 0 {
params.Set("from", options.From)
}
@@ -140,6 +143,23 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("labels", l)
}
+
+ if opt := options.CommonBuildOpts.LabelOpts; len(opt) > 0 {
+ o, err := jsoniter.MarshalToString(opt)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("labelopts", o)
+ }
+
+ if len(options.CommonBuildOpts.SeccompProfilePath) > 0 {
+ params.Set("seccomp", options.CommonBuildOpts.SeccompProfilePath)
+ }
+
+ if len(options.CommonBuildOpts.ApparmorProfile) > 0 {
+ params.Set("apparmor", options.CommonBuildOpts.ApparmorProfile)
+ }
+
if options.Layers {
params.Set("layers", "1")
}
@@ -174,6 +194,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if len(platform) > 0 {
params.Set("platform", platform)
}
+
params.Set("pullpolicy", options.PullPolicy.String())
if options.Quiet {
@@ -182,6 +203,10 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if options.RemoveIntermediateCtrs {
params.Set("rm", "1")
}
+ if len(options.Target) > 0 {
+ params.Set("target", options.Target)
+ }
+
if hosts := options.CommonBuildOpts.AddHost; len(hosts) > 0 {
h, err := jsoniter.MarshalToString(hosts)
if err != nil {
@@ -212,6 +237,13 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
params.Set("timestamp", strconv.FormatInt(t.Unix(), 10))
}
+ if len(options.CommonBuildOpts.Ulimit) > 0 {
+ ulimitsJSON, err := json.Marshal(options.CommonBuildOpts.Ulimit)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("ulimits", string(ulimitsJSON))
+ }
var (
headers map[string]string
err error
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index b0d9dc797..a98c9168a 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -146,7 +146,11 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
// This should always be checked after storage-driver is checked
if len(cfg.StorageOpts) > 0 {
storageSet = true
- storageOpts.GraphDriverOptions = cfg.StorageOpts
+ if len(cfg.StorageOpts) == 1 && cfg.StorageOpts[0] == "" {
+ storageOpts.GraphDriverOptions = []string{}
+ } else {
+ storageOpts.GraphDriverOptions = cfg.StorageOpts
+ }
}
if opts.migrate {
options = append(options, libpod.WithMigrate())
diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go
index 0b7d1782c..ecefb65ff 100644
--- a/pkg/netns/netns_linux.go
+++ b/pkg/netns/netns_linux.go
@@ -35,9 +35,9 @@ import (
"golang.org/x/sys/unix"
)
-// get NSRunDir returns the dir of where to create the netNS. When running
+// GetNSRunDir returns the dir of where to create the netNS. When running
// rootless, it needs to be at a location writable by user.
-func getNSRunDir() (string, error) {
+func GetNSRunDir() (string, error) {
if rootless.IsRootless() {
rootlessDir, err := util.GetRuntimeDir()
if err != nil {
@@ -51,15 +51,21 @@ func getNSRunDir() (string, error) {
// NewNS creates a new persistent (bind-mounted) network namespace and returns
// an object representing that namespace, without switching to it.
func NewNS() (ns.NetNS, error) {
- nsRunDir, err := getNSRunDir()
+ b := make([]byte, 16)
+ _, err := rand.Reader.Read(b)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to generate random netns name: %v", err)
}
+ nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
+ return NewNSWithName(nsName)
+}
- b := make([]byte, 16)
- _, err = rand.Reader.Read(b)
+// NewNSWithName creates a new persistent (bind-mounted) network namespace and returns
+// an object representing that namespace, without switching to it.
+func NewNSWithName(name string) (ns.NetNS, error) {
+ nsRunDir, err := GetNSRunDir()
if err != nil {
- return nil, fmt.Errorf("failed to generate random netns name: %v", err)
+ return nil, err
}
// Create the directory for mounting network namespaces
@@ -93,10 +99,8 @@ func NewNS() (ns.NetNS, error) {
}
}
- nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
-
// create an empty file at the mount point
- nsPath := path.Join(nsRunDir, nsName)
+ nsPath := path.Join(nsRunDir, name)
mountPointFd, err := os.Create(nsPath)
if err != nil {
return nil, err
@@ -177,7 +181,7 @@ func NewNS() (ns.NetNS, error) {
// UnmountNS unmounts the NS held by the netns object
func UnmountNS(ns ns.NetNS) error {
- nsRunDir, err := getNSRunDir()
+ nsRunDir, err := GetNSRunDir()
if err != nil {
return err
}
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index 7a2bf0377..918b9a7e6 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -61,6 +61,10 @@ static int open_files_max_fd;
static fd_set *open_files_set;
static uid_t rootless_uid_init;
static gid_t rootless_gid_init;
+static bool do_socket_activation = false;
+static char *saved_systemd_listen_fds;
+static char *saved_systemd_listen_pid;
+static char *saved_systemd_listen_fdnames;
static int
syscall_setresuid (uid_t ruid, uid_t euid, uid_t suid)
@@ -242,6 +246,10 @@ static void __attribute__((constructor)) init()
{
const char *xdg_runtime_dir;
const char *pause;
+ const char *listen_pid;
+ const char *listen_fds;
+ const char *listen_fdnames;
+
DIR *d;
pause = getenv ("_PODMAN_PAUSE");
@@ -293,6 +301,26 @@ static void __attribute__((constructor)) init()
closedir (d);
}
+ listen_pid = getenv("LISTEN_PID");
+ listen_fds = getenv("LISTEN_FDS");
+ listen_fdnames = getenv("LISTEN_FDNAMES");
+
+ if (listen_pid != NULL && listen_fds != NULL && strtol(listen_pid, NULL, 10) == getpid())
+ {
+ // save systemd socket environment for rootless child
+ do_socket_activation = true;
+ saved_systemd_listen_pid = strdup(listen_pid);
+ saved_systemd_listen_fds = strdup(listen_fds);
+ saved_systemd_listen_fdnames = strdup(listen_fdnames);
+ if (saved_systemd_listen_pid == NULL
+ || saved_systemd_listen_fds == NULL
+ || saved_systemd_listen_fdnames == NULL)
+ {
+ fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+ }
+
/* Shortcut. If we are able to join the pause pid file, do it now so we don't
need to re-exec. */
xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
@@ -635,6 +663,12 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
for (f = 3; f <= open_files_max_fd; f++)
if (is_fd_inherited (f))
close (f);
+ if (do_socket_activation)
+ {
+ unsetenv ("LISTEN_PID");
+ unsetenv ("LISTEN_FDS");
+ unsetenv ("LISTEN_FDNAMES");
+ }
return pid;
}
@@ -660,6 +694,15 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
_exit (EXIT_FAILURE);
}
+ if (do_socket_activation)
+ {
+ char s[32];
+ sprintf (s, "%d", getpid());
+ setenv ("LISTEN_PID", s, true);
+ setenv ("LISTEN_FDS", saved_systemd_listen_fds, true);
+ setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true);
+ }
+
setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1);
setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1);
@@ -777,9 +820,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
char **argv;
char uid[16];
char gid[16];
- char *listen_fds = NULL;
- char *listen_pid = NULL;
- bool do_socket_activation = false;
char *cwd = getcwd (NULL, 0);
sigset_t sigset, oldsigset;
@@ -789,14 +829,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
_exit (EXIT_FAILURE);
}
- listen_pid = getenv("LISTEN_PID");
- listen_fds = getenv("LISTEN_FDS");
-
- if (listen_pid != NULL && listen_fds != NULL)
- {
- if (strtol(listen_pid, NULL, 10) == getpid())
- do_socket_activation = true;
- }
sprintf (uid, "%d", geteuid ());
sprintf (gid, "%d", getegid ());
@@ -814,7 +846,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
{
long num_fds;
- num_fds = strtol (listen_fds, NULL, 10);
+ num_fds = strtol (saved_systemd_listen_fds, NULL, 10);
if (num_fds != LONG_MIN && num_fds != LONG_MAX)
{
int f;
@@ -863,6 +895,8 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
char s[32];
sprintf (s, "%d", getpid());
setenv ("LISTEN_PID", s, true);
+ setenv ("LISTEN_FDS", saved_systemd_listen_fds, true);
+ setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true);
}
setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
index b87375a92..845dfdad7 100644
--- a/pkg/specgen/generate/namespaces.go
+++ b/pkg/specgen/generate/namespaces.go
@@ -236,9 +236,6 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
case specgen.Private:
fallthrough
case specgen.Bridge:
- if postConfigureNetNS && rootless.IsRootless() {
- return nil, errors.New("CNI networks not supported with user namespaces")
- }
portMappings, err := createPortMappings(ctx, s, img)
if err != nil {
return nil, err
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index 5d7bf1930..20151f016 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -4,6 +4,7 @@ import (
"context"
"github.com/containers/podman/v3/libpod"
+ "github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -94,8 +95,19 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod
}
switch p.NetNS.NSMode {
- case specgen.Bridge, specgen.Default, "":
- logrus.Debugf("Pod using default network mode")
+ case specgen.Default, "":
+ if p.NoInfra {
+ logrus.Debugf("No networking because the infra container is missing")
+ break
+ }
+ if rootless.IsRootless() {
+ logrus.Debugf("Pod will use slirp4netns")
+ options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions))
+ } else {
+ logrus.Debugf("Pod using bridge network mode")
+ }
+ case specgen.Bridge:
+ logrus.Debugf("Pod using bridge network mode")
case specgen.Host:
logrus.Debugf("Pod will use host networking")
options = append(options, libpod.WithPodHostNetwork())
diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at
index 21840a42d..59947faac 100644
--- a/test/apiv2/35-networks.at
+++ b/test/apiv2/35-networks.at
@@ -106,7 +106,7 @@ t POST libpod/networks/prune?filters='{"label":["tes' 500 \
# prune networks using filter - compat api
t POST networks/prune?filters='{"label":["xyz"]}' 200
-t GET networks/json?filters='{"label":["xyz"]}' 404
+t GET networks?filters='{"label":["xyz"]}' 200 length=0
# prune networks using filter - libpod api
t POST libpod/networks/prune?filters='{"label":["zaq=val"]}' 200
@@ -120,5 +120,26 @@ t DELETE libpod/networks/network2 200 \
.[0].Name~network2 \
.[0].Err=null
+# test until filter - libpod api
+podman network create network5 --label xyz
+
+# with date way back in the past, network should not be deleted
+t POST libpod/networks/prune?filters='{"until":["500000"]}' 200
+t GET libpod/networks/json?filters='{"label":["xyz"]}' 200 length=1
+
+# with date far in the future, network should be deleted
+t POST libpod/networks/prune?filters='{"until":["5000000000"]}' 200
+t GET libpod/networks/json?filters='{"label":["xyz"]}' 200 length=0
+
+# test until filter - compat api
+podman network create network6 --label zaq
+
+# with date way back in the past, network should not be deleted
+t POST networks/prune?filters='{"until":["500000"]}' 200
+t GET networks?filters='{"label":["zaq"]}' 200 length=1
+
+# with date far in the future, network should be deleted
+t POST networks/prune?filters='{"until":["5000000000"]}' 200
+t GET networks?filters='{"label":["zaq"]}' 200 length=0
# vim: filetype=sh
diff --git a/test/compose/mount_and_label/tests.sh b/test/compose/mount_and_label/tests.sh
index 07ff089b5..fa929bed6 100644
--- a/test/compose/mount_and_label/tests.sh
+++ b/test/compose/mount_and_label/tests.sh
@@ -1,4 +1,5 @@
# -*- bash -*-
test_port 5000 = "Podman rulez!"
-podman container inspect -l --format '{{.Config.Labels}}' | grep "the_best"
+podman container inspect -l --format '{{.Config.Labels}}'
+like "$output" "io.podman:the_best" "$testname : Container label is set"
diff --git a/test/compose/test-compose b/test/compose/test-compose
index 9558fbf58..704c71a9f 100755
--- a/test/compose/test-compose
+++ b/test/compose/test-compose
@@ -13,7 +13,8 @@ TEST_ROOTDIR=$(realpath $(dirname $0))
# Podman executable
PODMAN_BIN=$(realpath $TEST_ROOTDIR/../../bin)/podman
-# Local path to docker socket (we will add the unix:/ prefix when we need it)
+# Local path to docker socket with unix prefix
+# The path will be changed for rootless users
DOCKER_SOCK=/var/run/docker.sock
# END stuff you can but probably shouldn't customize
@@ -40,6 +41,13 @@ echo 0 >$failures_file
###############################################################################
# BEGIN infrastructure code - the helper functions used in tests themselves
+#################
+# is_rootless # Check if we run as normal user
+#################
+function is_rootless() {
+ [ "$(id -u)" -ne 0 ]
+}
+
#########
# die # Exit error with a message to stderr
#########
@@ -155,7 +163,7 @@ function test_port() {
local op="$2" # '=' or '~'
local expect="$3" # what to expect from curl output
- local actual=$(curl --retry 5 --retry-connrefused -s http://127.0.0.1:$port/)
+ local actual=$(curl --retry 10 --retry-all-errors -s http://127.0.0.1:$port/)
local curl_rc=$?
if [ $curl_rc -ne 0 ]; then
_show_ok 0 "$testname - curl failed with status $curl_rc"
@@ -179,7 +187,12 @@ function start_service() {
test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN"
# FIXME: use ${testname} subdir but we can't: 50-char limit in runroot
- rm -rf $WORKDIR/{root,runroot,cni}
+ if ! is_rootless; then
+ rm -rf $WORKDIR/{root,runroot,cni}
+ else
+ $PODMAN_BIN unshare rm -rf $WORKDIR/{root,runroot,cni}
+ fi
+ rm -f $DOCKER_SOCK
mkdir --mode 0755 $WORKDIR/{root,runroot,cni}
chcon --reference=/var/lib/containers $WORKDIR/root
cp /etc/cni/net.d/*podman*conflist $WORKDIR/cni/
@@ -190,7 +203,7 @@ function start_service() {
--cgroup-manager=systemd \
--cni-config-dir $WORKDIR/cni \
system service \
- --time 0 unix:/$DOCKER_SOCK \
+ --time 0 unix://$DOCKER_SOCK \
&> $WORKDIR/server.log &
service_pid=$!
@@ -211,10 +224,11 @@ function start_service() {
############
function podman() {
echo "\$ podman $*" >>$WORKDIR/output.log
- $PODMAN_BIN \
+ output=$($PODMAN_BIN \
--root $WORKDIR/root \
--runroot $WORKDIR/runroot \
- "$@" >>$WORKDIR/output.log 2>&1
+ "$@")
+ echo -n "$output" >>$WORKDIR/output.log
}
###################
@@ -239,6 +253,14 @@ done
###############################################################################
# BEGIN entry handler (subtest invoker)
+# When rootless use a socket path accessible by the rootless user
+if is_rootless; then
+ DOCKER_SOCK="$WORKDIR/docker.sock"
+ DOCKER_HOST="unix://$DOCKER_SOCK"
+ # export DOCKER_HOST docker-compose will use it
+ export DOCKER_HOST
+fi
+
# Identify the tests to run. If called with args, use those as globs.
tests_to_run=()
if [ -n "$*" ]; then
@@ -308,7 +330,7 @@ for t in ${tests_to_run[@]}; do
fi
# Done. Clean up.
- docker-compose down &> $logfile
+ docker-compose down &>> $logfile
rc=$?
if [[ $rc -eq 0 ]]; then
_show_ok 1 "$testname - down"
@@ -322,7 +344,11 @@ for t in ${tests_to_run[@]}; do
wait $service_pid
# FIXME: otherwise we get EBUSY
- umount $WORKDIR/root/overlay &>/dev/null
+ if ! is_rootless; then
+ umount $WORKDIR/root/overlay &>/dev/null
+ else
+ $PODMAN_BIN unshare umount $WORKDIR/root/overlay &>/dev/null
+ fi
# FIXME: run 'podman ps'?
# rm -rf $WORKDIR/${testname}
@@ -336,9 +362,13 @@ done
test_count=$(<$testcounter_file)
failure_count=$(<$failures_file)
-#if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then
-# rm -rf $WORKDIR
-#fi
+if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then
+ if ! is_rootless; then
+ rm -rf $WORKDIR
+ else
+ $PODMAN_BIN unshare rm -rf $WORKDIR
+ fi
+fi
echo "1..${test_count}"
diff --git a/test/compose/two_networks/Readme.md b/test/compose/two_networks/Readme.md
new file mode 100644
index 000000000..471004f7d
--- /dev/null
+++ b/test/compose/two_networks/Readme.md
@@ -0,0 +1,8 @@
+two networks
+===============
+
+This test checks that we can create containers with more than one network.
+
+Validation
+------------
+* podman container inspect two_networks_con1_1 --format '{{len .NetworkSettings.Networks}}' shows 2
diff --git a/test/compose/two_networks/docker-compose.yml b/test/compose/two_networks/docker-compose.yml
new file mode 100644
index 000000000..686396ccc
--- /dev/null
+++ b/test/compose/two_networks/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3'
+services:
+ con1:
+ image: alpine
+ command: top
+ networks:
+ - net1
+ - net2
+networks:
+ net1:
+ net2:
diff --git a/test/compose/two_networks/tests.sh b/test/compose/two_networks/tests.sh
new file mode 100644
index 000000000..1cc88aa5f
--- /dev/null
+++ b/test/compose/two_networks/tests.sh
@@ -0,0 +1,7 @@
+# -*- bash -*-
+
+podman container inspect two_networks_con1_1 --format '{{len .NetworkSettings.Networks}}'
+is "$output" "2" "$testname : Container is connected to both networks"
+podman container inspect two_networks_con1_1 --format '{{.NetworkSettings.Networks}}'
+like "$output" "two_networks_net1" "$testname : First network name exists"
+like "$output" "two_networks_net2" "$testname : Second network name exists"
diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go
index e9a7b421f..6974c7614 100644
--- a/test/e2e/network_connect_disconnect_test.go
+++ b/test/e2e/network_connect_disconnect_test.go
@@ -33,14 +33,12 @@ var _ = Describe("Podman network connect and disconnect", func() {
})
It("bad network name in disconnect should result in error", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
dis := podmanTest.Podman([]string{"network", "disconnect", "foobar", "test"})
dis.WaitWithDefaultTimeout()
Expect(dis.ExitCode()).ToNot(BeZero())
})
It("bad container name in network disconnect should result in error", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
netName := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
@@ -72,7 +70,6 @@ var _ = Describe("Podman network connect and disconnect", func() {
})
It("podman network disconnect", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
netName := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
@@ -102,14 +99,12 @@ var _ = Describe("Podman network connect and disconnect", func() {
})
It("bad network name in connect should result in error", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
dis := podmanTest.Podman([]string{"network", "connect", "foobar", "test"})
dis.WaitWithDefaultTimeout()
Expect(dis.ExitCode()).ToNot(BeZero())
})
It("bad container name in network connect should result in error", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
netName := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
@@ -141,7 +136,6 @@ var _ = Describe("Podman network connect and disconnect", func() {
})
It("podman connect on a container that already is connected to the network should error", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
netName := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
@@ -159,7 +153,6 @@ var _ = Describe("Podman network connect and disconnect", func() {
It("podman network connect", func() {
SkipIfRemote("This requires a pending PR to be merged before it will work")
- SkipIfRootless("network connect and disconnect are only rootful")
netName := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
@@ -203,18 +196,23 @@ var _ = Describe("Podman network connect and disconnect", func() {
})
It("podman network connect when not running", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
- netName := "aliasTest" + stringid.GenerateNonCryptoID()
- session := podmanTest.Podman([]string{"network", "create", netName})
+ netName1 := "connect1" + stringid.GenerateNonCryptoID()
+ session := podmanTest.Podman([]string{"network", "create", netName1})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
- defer podmanTest.removeCNINetwork(netName)
+ defer podmanTest.removeCNINetwork(netName1)
- ctr := podmanTest.Podman([]string{"create", "--name", "test", ALPINE, "top"})
+ netName2 := "connect2" + stringid.GenerateNonCryptoID()
+ session = podmanTest.Podman([]string{"network", "create", netName2})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName2)
+
+ ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName1, ALPINE, "top"})
ctr.WaitWithDefaultTimeout()
Expect(ctr.ExitCode()).To(BeZero())
- dis := podmanTest.Podman([]string{"network", "connect", netName, "test"})
+ dis := podmanTest.Podman([]string{"network", "connect", netName2, "test"})
dis.WaitWithDefaultTimeout()
Expect(dis.ExitCode()).To(BeZero())
@@ -286,7 +284,6 @@ var _ = Describe("Podman network connect and disconnect", func() {
})
It("podman network disconnect when not running", func() {
- SkipIfRootless("network connect and disconnect are only rootful")
netName1 := "aliasTest" + stringid.GenerateNonCryptoID()
session := podmanTest.Podman([]string{"network", "create", netName1})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 0e6e636bc..4c66e2823 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -641,22 +641,26 @@ var _ = Describe("Podman run networking", func() {
Expect(run.OutputToString()).To(ContainSubstring(ipAddr))
})
- It("podman rootless fails custom CNI network with --uidmap", func() {
- SkipIfNotRootless("The configuration works with rootless")
-
+ It("podman cni network works across user ns", func() {
netName := stringid.GenerateNonCryptoID()
create := podmanTest.Podman([]string{"network", "create", netName})
create.WaitWithDefaultTimeout()
Expect(create.ExitCode()).To(BeZero())
defer podmanTest.removeCNINetwork(netName)
- run := podmanTest.Podman([]string{"run", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "true"})
+ name := "nc-server"
+ run := podmanTest.Podman([]string{"run", "-d", "--name", name, "--net", netName, ALPINE, "nc", "-l", "-p", "8080"})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ run = podmanTest.Podman([]string{"run", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "sh", "-c", fmt.Sprintf("echo podman | nc -w 1 %s.dns.podman 8080", name)})
run.WaitWithDefaultTimeout()
- Expect(run.ExitCode()).To(Equal(125))
+ Expect(run.ExitCode()).To(Equal(0))
- remove := podmanTest.Podman([]string{"network", "rm", netName})
- remove.WaitWithDefaultTimeout()
- Expect(remove.ExitCode()).To(BeZero())
+ log := podmanTest.Podman([]string{"logs", name})
+ log.WaitWithDefaultTimeout()
+ Expect(log.ExitCode()).To(Equal(0))
+ Expect(log.OutputToString()).To(Equal("podman"))
})
It("podman run with new:pod and static-ip", func() {
@@ -762,7 +766,7 @@ var _ = Describe("Podman run networking", func() {
Expect(session.ExitCode()).To(Equal(1))
Expect(session.ErrorToString()).To(ContainSubstring("can't resolve 'con1'"))
- session = podmanTest.Podman([]string{"run", "--name", "con4", "--network", net, ALPINE, "nslookup", pod2})
+ session = podmanTest.Podman([]string{"run", "--name", "con4", "--network", net, ALPINE, "nslookup", pod2 + ".dns.podman"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
})
diff --git a/test/system/001-basic.bats b/test/system/001-basic.bats
index d276cfda1..081bb1171 100644
--- a/test/system/001-basic.bats
+++ b/test/system/001-basic.bats
@@ -10,6 +10,13 @@ function setup() {
:
}
+@test "podman --context emits reasonable output" {
+ run_podman 125 --context=swarm version
+ is "$output" "Error: Podman does not support swarm, the only --context value allowed is \"default\"" "--context=default or fail"
+
+ run_podman --context=default version
+}
+
@test "podman version emits reasonable output" {
run_podman version
diff --git a/test/system/005-info.bats b/test/system/005-info.bats
index 7452c1901..c0af2e937 100644
--- a/test/system/005-info.bats
+++ b/test/system/005-info.bats
@@ -53,4 +53,13 @@ store.imageStore.number | 1
}
+@test "podman info --storage-opt='' " {
+ skip_if_remote "--storage-opt flag is not supported for remote"
+ skip_if_rootless "storage opts are required for rootless running"
+ run_podman --storage-opt='' info
+ # Note this will not work in rootless mode, unless you specify
+ # storage-driver=vfs, until we have kernels that support rootless overlay
+ # mounts.
+ is "$output" ".*graphOptions: {}" "output includes graphOptions: {}"
+}
# vim: filetype=sh
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index e5b68a0d8..5a887c71e 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -712,6 +712,51 @@ EOF
run_podman rmi -f build_test
}
+@test "podman build check_label" {
+ skip_if_no_selinux
+ tmpdir=$PODMAN_TMPDIR/build-test
+ mkdir -p $tmpdir
+ tmpbuilddir=$tmpdir/build
+ mkdir -p $tmpbuilddir
+ dockerfile=$tmpbuilddir/Dockerfile
+ cat >$dockerfile <<EOF
+FROM $IMAGE
+RUN cat /proc/self/attr/current
+EOF
+
+ run_podman build -t build_test --security-opt label=level:s0:c3,c4 --format=docker $tmpbuilddir
+ is "$output" ".*s0:c3,c4STEP 3: COMMIT" "label setting level"
+
+ run_podman rmi -f build_test
+}
+
+@test "podman build check_seccomp_ulimits" {
+ tmpdir=$PODMAN_TMPDIR/build-test
+ mkdir -p $tmpdir
+ tmpbuilddir=$tmpdir/build
+ mkdir -p $tmpbuilddir
+ dockerfile=$tmpbuilddir/Dockerfile
+ cat >$dockerfile <<EOF
+FROM $IMAGE
+RUN grep Seccomp: /proc/self/status |awk '{ print \$1\$2 }'
+RUN grep "Max open files" /proc/self/limits |awk '{ print \$4":"\$5 }'
+EOF
+
+ run_podman build --ulimit nofile=101:102 -t build_test $tmpbuilddir
+ is "$output" ".*Seccomp:2" "setting seccomp"
+ is "$output" ".*101:102" "setting ulimits"
+ run_podman rmi -f build_test
+
+ run_podman build -t build_test --security-opt seccomp=unconfined $tmpbuilddir
+ is "$output" ".*Seccomp:0" "setting seccomp"
+ run_podman rmi -f build_test
+}
+
+@test "podman build --authfile bogus test" {
+ run_podman 125 build --authfile=/tmp/bogus - <<< "from scratch"
+ is "$output" ".*/tmp/bogus: no such file or directory"
+}
+
function teardown() {
# A timeout or other error in 'build' can leave behind stale images
# that podman can't even see and which will cascade into subsequent
diff --git a/test/system/270-socket-activation.bats b/test/system/270-socket-activation.bats
new file mode 100644
index 000000000..25206c6a7
--- /dev/null
+++ b/test/system/270-socket-activation.bats
@@ -0,0 +1,103 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# Tests podman system service under systemd socket activation
+#
+
+load helpers
+
+SERVICE_NAME="podman_test_$(random_string)"
+
+SYSTEMCTL="systemctl"
+UNIT_DIR="/usr/lib/systemd/system"
+SERVICE_SOCK_ADDR="/run/podman/podman.sock"
+
+if is_rootless; then
+ UNIT_DIR="$HOME/.config/systemd/user"
+ mkdir -p $UNIT_DIR
+
+ SYSTEMCTL="$SYSTEMCTL --user"
+ if [ -z "$XDG_RUNTIME_DIR" ]; then
+ export XDG_RUNTIME_DIR=/run/user/$(id -u)
+ fi
+ SERVICE_SOCK_ADDR="$XDG_RUNTIME_DIR/podman/podman.sock"
+fi
+
+SERVICE_FILE="$UNIT_DIR/$SERVICE_NAME.service"
+SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket"
+
+
+function setup() {
+ skip_if_remote "systemd tests are meaningless over remote"
+
+ basic_setup
+
+ cat > $SERVICE_FILE <<EOF
+[Unit]
+Description=Podman API Service
+Requires=podman.socket
+After=podman.socket
+Documentation=man:podman-system-service(1)
+StartLimitIntervalSec=0
+
+[Service]
+Type=exec
+KillMode=process
+Environment=LOGGING="--log-level=info"
+ExecStart=$PODMAN $LOGGING system service -t 2
+EOF
+ cat > $SOCKET_FILE <<EOF
+[Unit]
+Description=Podman API Socket
+Documentation=man:podman-system-service(1)
+
+[Socket]
+ListenStream=%t/podman/podman.sock
+SocketMode=0660
+
+[Install]
+WantedBy=sockets.target
+EOF
+
+ # ensure pause die before each test runs
+ if is_rootless; then
+ local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid"
+ if [ -f $pause_pid ]; then
+ kill -9 $(cat $pause_pid) 2> /dev/null
+ rm -f $pause_pid
+ fi
+ fi
+ $SYSTEMCTL start "$SERVICE_NAME.socket"
+}
+
+function teardown() {
+ $SYSTEMCTL stop "$SERVICE_NAME.socket"
+ rm -f "$SERVICE_FILE" "$SOCKET_FILE"
+ $SYSTEMCTL daemon-reload
+ basic_teardown
+}
+
+@test "podman system service - socket activation - no container" {
+ run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping
+ is "$output" "OK" "podman service responses normally"
+}
+
+@test "podman system service - socket activation - exist container " {
+ run_podman run $IMAGE sleep 90
+ run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping
+ is "$output" "OK" "podman service responses normally"
+}
+
+@test "podman system service - socket activation - kill rootless pause " {
+ if ! is_rootless; then
+ skip "root podman no need pause process"
+ fi
+ run_podman run $IMAGE sleep 90
+ local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid"
+ if [ -f $pause_pid ]; then
+ kill -9 $(cat $pause_pid) 2> /dev/null
+ fi
+ run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping
+ is "$output" "OK" "podman service responses normally"
+}
+
+# vim: filetype=sh
diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats
index 4a2c7b7a4..8a690fb48 100644
--- a/test/system/410-selinux.bats
+++ b/test/system/410-selinux.bats
@@ -191,5 +191,33 @@ function check_label() {
is "$output" "Error.*: \`/proc/thread-self/attr/exec\`: OCI runtime error: unable to assign security attribute" "useful diagnostic"
}
+@test "podman selinux: check relabel" {
+ skip_if_no_selinux
+
+ LABEL="system_u:object_r:tmp_t:s0"
+ tmpdir=$PODMAN_TMPDIR/vol
+ touch $tmpdir
+ chcon -vR ${LABEL} $tmpdir
+ ls -Z $tmpdir
+
+ run_podman run -v $tmpdir:/test $IMAGE cat /proc/self/attr/current
+ level=$(secon -l $output)
+ run ls -dZ ${tmpdir}
+ is "$output" ${LABEL} "No Relabel Correctly"
+
+ run_podman run -v $tmpdir:/test:Z --security-opt label=disable $IMAGE cat /proc/self/attr/current
+ level=$(secon -l $output)
+ run ls -dZ $tmpdir
+ is "$output" ${LABEL} "No Privileged Relabel Correctly"
+
+ run_podman run -v $tmpdir:/test:Z $IMAGE cat /proc/self/attr/current
+ level=$(secon -l $output)
+ run ls -dZ $tmpdir
+ is "$output" "system_u:object_r:container_file_t:$level" "Confined Relabel Correctly"
+
+ run_podman run -v $tmpdir:/test:z $IMAGE cat /proc/self/attr/current
+ run ls -dZ $tmpdir
+ is "$output" "system_u:object_r:container_file_t:s0" "Shared Relabel Correctly"
+}
# vim: filetype=sh
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index 4868ad6a0..804dd46b1 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -143,13 +143,6 @@ load helpers
run_podman network rm $mynetname
run_podman 1 network rm $mynetname
-
- # rootless CNI leaves behind an image pulled by SHA, hence with no tag.
- # Remove it if present; we can only remove it by ID.
- run_podman images --format '{{.Id}}' rootless-cni-infra
- if [ -n "$output" ]; then
- run_podman rmi $output
- fi
}
@test "podman network reload" {
diff --git a/vendor/github.com/go-task/slim-sprig/.editorconfig b/vendor/github.com/go-task/slim-sprig/.editorconfig
new file mode 100644
index 000000000..b0c95367e
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/.editorconfig
@@ -0,0 +1,14 @@
+# editorconfig.org
+
+root = true
+
+[*]
+insert_final_newline = true
+charset = utf-8
+trim_trailing_whitespace = true
+indent_style = tab
+indent_size = 8
+
+[*.{md,yml,yaml,json}]
+indent_style = space
+indent_size = 2
diff --git a/vendor/github.com/go-task/slim-sprig/.gitattributes b/vendor/github.com/go-task/slim-sprig/.gitattributes
new file mode 100644
index 000000000..176a458f9
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/.gitattributes
@@ -0,0 +1 @@
+* text=auto
diff --git a/vendor/github.com/go-task/slim-sprig/.gitignore b/vendor/github.com/go-task/slim-sprig/.gitignore
new file mode 100644
index 000000000..5e3002f88
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/.gitignore
@@ -0,0 +1,2 @@
+vendor/
+/.glide
diff --git a/vendor/github.com/go-task/slim-sprig/CHANGELOG.md b/vendor/github.com/go-task/slim-sprig/CHANGELOG.md
new file mode 100644
index 000000000..61d8ebffc
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/CHANGELOG.md
@@ -0,0 +1,364 @@
+# Changelog
+
+## Release 3.2.0 (2020-12-14)
+
+### Added
+
+- #211: Added randInt function (thanks @kochurovro)
+- #223: Added fromJson and mustFromJson functions (thanks @mholt)
+- #242: Added a bcrypt function (thanks @robbiet480)
+- #253: Added randBytes function (thanks @MikaelSmith)
+- #254: Added dig function for dicts (thanks @nyarly)
+- #257: Added regexQuoteMeta for quoting regex metadata (thanks @rheaton)
+- #261: Added filepath functions osBase, osDir, osExt, osClean, osIsAbs (thanks @zugl)
+- #268: Added and and all functions for testing conditions (thanks @phuslu)
+- #181: Added float64 arithmetic addf, add1f, subf, divf, mulf, maxf, and minf
+ (thanks @andrewmostello)
+- #265: Added chunk function to split array into smaller arrays (thanks @karelbilek)
+- #270: Extend certificate functions to handle non-RSA keys + add support for
+ ed25519 keys (thanks @misberner)
+
+### Changed
+
+- Removed testing and support for Go 1.12. ed25519 support requires Go 1.13 or newer
+- Using semver 3.1.1 and mergo 0.3.11
+
+### Fixed
+
+- #249: Fix htmlDateInZone example (thanks @spawnia)
+
+NOTE: The dependency github.com/imdario/mergo reverted the breaking change in
+0.3.9 via 0.3.10 release.
+
+## Release 3.1.0 (2020-04-16)
+
+NOTE: The dependency github.com/imdario/mergo made a behavior change in 0.3.9
+that impacts sprig functionality. Do not use sprig with a version newer than 0.3.8.
+
+### Added
+
+- #225: Added support for generating htpasswd hash (thanks @rustycl0ck)
+- #224: Added duration filter (thanks @frebib)
+- #205: Added `seq` function (thanks @thadc23)
+
+### Changed
+
+- #203: Unlambda functions with correct signature (thanks @muesli)
+- #236: Updated the license formatting for GitHub display purposes
+- #238: Updated package dependency versions. Note, mergo not updated to 0.3.9
+ as it causes a breaking change for sprig. That issue is tracked at
+ https://github.com/imdario/mergo/issues/139
+
+### Fixed
+
+- #229: Fix `seq` example in docs (thanks @kalmant)
+
+## Release 3.0.2 (2019-12-13)
+
+### Fixed
+
+- #220: Updating to semver v3.0.3 to fix issue with <= ranges
+- #218: fix typo elyptical->elliptic in ecdsa key description (thanks @laverya)
+
+## Release 3.0.1 (2019-12-08)
+
+### Fixed
+
+- #212: Updated semver fixing broken constraint checking with ^0.0
+
+## Release 3.0.0 (2019-10-02)
+
+### Added
+
+- #187: Added durationRound function (thanks @yjp20)
+- #189: Added numerous template functions that return errors rather than panic (thanks @nrvnrvn)
+- #193: Added toRawJson support (thanks @Dean-Coakley)
+- #197: Added get support to dicts (thanks @Dean-Coakley)
+
+### Changed
+
+- #186: Moving dependency management to Go modules
+- #186: Updated semver to v3. This has changes in the way ^ is handled
+- #194: Updated documentation on merging and how it copies. Added example using deepCopy
+- #196: trunc now supports negative values (thanks @Dean-Coakley)
+
+## Release 2.22.0 (2019-10-02)
+
+### Added
+
+- #173: Added getHostByName function to resolve dns names to ips (thanks @fcgravalos)
+- #195: Added deepCopy function for use with dicts
+
+### Changed
+
+- Updated merge and mergeOverwrite documentation to explain copying and how to
+ use deepCopy with it
+
+## Release 2.21.0 (2019-09-18)
+
+### Added
+
+- #122: Added encryptAES/decryptAES functions (thanks @n0madic)
+- #128: Added toDecimal support (thanks @Dean-Coakley)
+- #169: Added list contcat (thanks @astorath)
+- #174: Added deepEqual function (thanks @bonifaido)
+- #170: Added url parse and join functions (thanks @astorath)
+
+### Changed
+
+- #171: Updated glide config for Google UUID to v1 and to add ranges to semver and testify
+
+### Fixed
+
+- #172: Fix semver wildcard example (thanks @piepmatz)
+- #175: Fix dateInZone doc example (thanks @s3than)
+
+## Release 2.20.0 (2019-06-18)
+
+### Added
+
+- #164: Adding function to get unix epoch for a time (@mattfarina)
+- #166: Adding tests for date_in_zone (@mattfarina)
+
+### Changed
+
+- #144: Fix function comments based on best practices from Effective Go (@CodeLingoTeam)
+- #150: Handles pointer type for time.Time in "htmlDate" (@mapreal19)
+- #161, #157, #160, #153, #158, #156, #155, #159, #152 documentation updates (@badeadan)
+
+### Fixed
+
+## Release 2.19.0 (2019-03-02)
+
+IMPORTANT: This release reverts a change from 2.18.0
+
+In the previous release (2.18), we prematurely merged a partial change to the crypto functions that led to creating two sets of crypto functions (I blame @technosophos -- since that's me). This release rolls back that change, and does what was originally intended: It alters the existing crypto functions to use secure random.
+
+We debated whether this classifies as a change worthy of major revision, but given the proximity to the last release, we have decided that treating 2.18 as a faulty release is the correct course of action. We apologize for any inconvenience.
+
+### Changed
+
+- Fix substr panic 35fb796 (Alexey igrychev)
+- Remove extra period 1eb7729 (Matthew Lorimor)
+- Make random string functions use crypto by default 6ceff26 (Matthew Lorimor)
+- README edits/fixes/suggestions 08fe136 (Lauri Apple)
+
+
+## Release 2.18.0 (2019-02-12)
+
+### Added
+
+- Added mergeOverwrite function
+- cryptographic functions that use secure random (see fe1de12)
+
+### Changed
+
+- Improve documentation of regexMatch function, resolves #139 90b89ce (Jan Tagscherer)
+- Handle has for nil list 9c10885 (Daniel Cohen)
+- Document behaviour of mergeOverwrite fe0dbe9 (Lukas Rieder)
+- doc: adds missing documentation. 4b871e6 (Fernandez Ludovic)
+- Replace outdated goutils imports 01893d2 (Matthew Lorimor)
+- Surface crypto secure random strings from goutils fe1de12 (Matthew Lorimor)
+- Handle untyped nil values as paramters to string functions 2b2ec8f (Morten Torkildsen)
+
+### Fixed
+
+- Fix dict merge issue and provide mergeOverwrite .dst .src1 to overwrite from src -> dst 4c59c12 (Lukas Rieder)
+- Fix substr var names and comments d581f80 (Dean Coakley)
+- Fix substr documentation 2737203 (Dean Coakley)
+
+## Release 2.17.1 (2019-01-03)
+
+### Fixed
+
+The 2.17.0 release did not have a version pinned for xstrings, which caused compilation failures when xstrings < 1.2 was used. This adds the correct version string to glide.yaml.
+
+## Release 2.17.0 (2019-01-03)
+
+### Added
+
+- adds alder32sum function and test 6908fc2 (marshallford)
+- Added kebabcase function ca331a1 (Ilyes512)
+
+### Changed
+
+- Update goutils to 1.1.0 4e1125d (Matt Butcher)
+
+### Fixed
+
+- Fix 'has' documentation e3f2a85 (dean-coakley)
+- docs(dict): fix typo in pick example dc424f9 (Dustin Specker)
+- fixes spelling errors... not sure how that happened 4cf188a (marshallford)
+
+## Release 2.16.0 (2018-08-13)
+
+### Added
+
+- add splitn function fccb0b0 (Helgi Þorbjörnsson)
+- Add slice func df28ca7 (gongdo)
+- Generate serial number a3bdffd (Cody Coons)
+- Extract values of dict with values function df39312 (Lawrence Jones)
+
+### Changed
+
+- Modify panic message for list.slice ae38335 (gongdo)
+- Minor improvement in code quality - Removed an unreachable piece of code at defaults.go#L26:6 - Resolve formatting issues. 5834241 (Abhishek Kashyap)
+- Remove duplicated documentation 1d97af1 (Matthew Fisher)
+- Test on go 1.11 49df809 (Helgi Þormar Þorbjörnsson)
+
+### Fixed
+
+- Fix file permissions c5f40b5 (gongdo)
+- Fix example for buildCustomCert 7779e0d (Tin Lam)
+
+## Release 2.15.0 (2018-04-02)
+
+### Added
+
+- #68 and #69: Add json helpers to docs (thanks @arunvelsriram)
+- #66: Add ternary function (thanks @binoculars)
+- #67: Allow keys function to take multiple dicts (thanks @binoculars)
+- #89: Added sha1sum to crypto function (thanks @benkeil)
+- #81: Allow customizing Root CA that used by genSignedCert (thanks @chenzhiwei)
+- #92: Add travis testing for go 1.10
+- #93: Adding appveyor config for windows testing
+
+### Changed
+
+- #90: Updating to more recent dependencies
+- #73: replace satori/go.uuid with google/uuid (thanks @petterw)
+
+### Fixed
+
+- #76: Fixed documentation typos (thanks @Thiht)
+- Fixed rounding issue on the `ago` function. Note, the removes support for Go 1.8 and older
+
+## Release 2.14.1 (2017-12-01)
+
+### Fixed
+
+- #60: Fix typo in function name documentation (thanks @neil-ca-moore)
+- #61: Removing line with {{ due to blocking github pages genertion
+- #64: Update the list functions to handle int, string, and other slices for compatibility
+
+## Release 2.14.0 (2017-10-06)
+
+This new version of Sprig adds a set of functions for generating and working with SSL certificates.
+
+- `genCA` generates an SSL Certificate Authority
+- `genSelfSignedCert` generates an SSL self-signed certificate
+- `genSignedCert` generates an SSL certificate and key based on a given CA
+
+## Release 2.13.0 (2017-09-18)
+
+This release adds new functions, including:
+
+- `regexMatch`, `regexFindAll`, `regexFind`, `regexReplaceAll`, `regexReplaceAllLiteral`, and `regexSplit` to work with regular expressions
+- `floor`, `ceil`, and `round` math functions
+- `toDate` converts a string to a date
+- `nindent` is just like `indent` but also prepends a new line
+- `ago` returns the time from `time.Now`
+
+### Added
+
+- #40: Added basic regex functionality (thanks @alanquillin)
+- #41: Added ceil floor and round functions (thanks @alanquillin)
+- #48: Added toDate function (thanks @andreynering)
+- #50: Added nindent function (thanks @binoculars)
+- #46: Added ago function (thanks @slayer)
+
+### Changed
+
+- #51: Updated godocs to include new string functions (thanks @curtisallen)
+- #49: Added ability to merge multiple dicts (thanks @binoculars)
+
+## Release 2.12.0 (2017-05-17)
+
+- `snakecase`, `camelcase`, and `shuffle` are three new string functions
+- `fail` allows you to bail out of a template render when conditions are not met
+
+## Release 2.11.0 (2017-05-02)
+
+- Added `toJson` and `toPrettyJson`
+- Added `merge`
+- Refactored documentation
+
+## Release 2.10.0 (2017-03-15)
+
+- Added `semver` and `semverCompare` for Semantic Versions
+- `list` replaces `tuple`
+- Fixed issue with `join`
+- Added `first`, `last`, `intial`, `rest`, `prepend`, `append`, `toString`, `toStrings`, `sortAlpha`, `reverse`, `coalesce`, `pluck`, `pick`, `compact`, `keys`, `omit`, `uniq`, `has`, `without`
+
+## Release 2.9.0 (2017-02-23)
+
+- Added `splitList` to split a list
+- Added crypto functions of `genPrivateKey` and `derivePassword`
+
+## Release 2.8.0 (2016-12-21)
+
+- Added access to several path functions (`base`, `dir`, `clean`, `ext`, and `abs`)
+- Added functions for _mutating_ dictionaries (`set`, `unset`, `hasKey`)
+
+## Release 2.7.0 (2016-12-01)
+
+- Added `sha256sum` to generate a hash of an input
+- Added functions to convert a numeric or string to `int`, `int64`, `float64`
+
+## Release 2.6.0 (2016-10-03)
+
+- Added a `uuidv4` template function for generating UUIDs inside of a template.
+
+## Release 2.5.0 (2016-08-19)
+
+- New `trimSuffix`, `trimPrefix`, `hasSuffix`, and `hasPrefix` functions
+- New aliases have been added for a few functions that didn't follow the naming conventions (`trimAll` and `abbrevBoth`)
+- `trimall` and `abbrevboth` (notice the case) are deprecated and will be removed in 3.0.0
+
+## Release 2.4.0 (2016-08-16)
+
+- Adds two functions: `until` and `untilStep`
+
+## Release 2.3.0 (2016-06-21)
+
+- cat: Concatenate strings with whitespace separators.
+- replace: Replace parts of a string: `replace " " "-" "Me First"` renders "Me-First"
+- plural: Format plurals: `len "foo" | plural "one foo" "many foos"` renders "many foos"
+- indent: Indent blocks of text in a way that is sensitive to "\n" characters.
+
+## Release 2.2.0 (2016-04-21)
+
+- Added a `genPrivateKey` function (Thanks @bacongobbler)
+
+## Release 2.1.0 (2016-03-30)
+
+- `default` now prints the default value when it does not receive a value down the pipeline. It is much safer now to do `{{.Foo | default "bar"}}`.
+- Added accessors for "hermetic" functions. These return only functions that, when given the same input, produce the same output.
+
+## Release 2.0.0 (2016-03-29)
+
+Because we switched from `int` to `int64` as the return value for all integer math functions, the library's major version number has been incremented.
+
+- `min` complements `max` (formerly `biggest`)
+- `empty` indicates that a value is the empty value for its type
+- `tuple` creates a tuple inside of a template: `{{$t := tuple "a", "b" "c"}}`
+- `dict` creates a dictionary inside of a template `{{$d := dict "key1" "val1" "key2" "val2"}}`
+- Date formatters have been added for HTML dates (as used in `date` input fields)
+- Integer math functions can convert from a number of types, including `string` (via `strconv.ParseInt`).
+
+## Release 1.2.0 (2016-02-01)
+
+- Added quote and squote
+- Added b32enc and b32dec
+- add now takes varargs
+- biggest now takes varargs
+
+## Release 1.1.0 (2015-12-29)
+
+- Added #4: Added contains function. strings.Contains, but with the arguments
+ switched to simplify common pipelines. (thanks krancour)
+- Added Travis-CI testing support
+
+## Release 1.0.0 (2015-12-23)
+
+- Initial release
diff --git a/vendor/github.com/go-task/slim-sprig/LICENSE.txt b/vendor/github.com/go-task/slim-sprig/LICENSE.txt
new file mode 100644
index 000000000..f311b1eaa
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (C) 2013-2020 Masterminds
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/go-task/slim-sprig/README.md b/vendor/github.com/go-task/slim-sprig/README.md
new file mode 100644
index 000000000..72579471f
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/README.md
@@ -0,0 +1,73 @@
+# Slim-Sprig: Template functions for Go templates [![GoDoc](https://godoc.org/github.com/go-task/slim-sprig?status.svg)](https://godoc.org/github.com/go-task/slim-sprig) [![Go Report Card](https://goreportcard.com/badge/github.com/go-task/slim-sprig)](https://goreportcard.com/report/github.com/go-task/slim-sprig)
+
+Slim-Sprig is a fork of [Sprig](https://github.com/Masterminds/sprig), but with
+all functions that depend on external (non standard library) or crypto packages
+removed.
+The reason for this is to make this library more lightweight. Most of these
+functions (specially crypto ones) are not needed on most apps, but costs a lot
+in terms of binary size and compilation time.
+
+## Usage
+
+**Template developers**: Please use Slim-Sprig's [function documentation](https://go-task.github.io/slim-sprig/) for
+detailed instructions and code snippets for the >100 template functions available.
+
+**Go developers**: If you'd like to include Slim-Sprig as a library in your program,
+our API documentation is available [at GoDoc.org](http://godoc.org/github.com/go-task/slim-sprig).
+
+For standard usage, read on.
+
+### Load the Slim-Sprig library
+
+To load the Slim-Sprig `FuncMap`:
+
+```go
+
+import (
+ "html/template"
+
+ "github.com/go-task/slim-sprig"
+)
+
+// This example illustrates that the FuncMap *must* be set before the
+// templates themselves are loaded.
+tpl := template.Must(
+ template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html")
+)
+```
+
+### Calling the functions inside of templates
+
+By convention, all functions are lowercase. This seems to follow the Go
+idiom for template functions (as opposed to template methods, which are
+TitleCase). For example, this:
+
+```
+{{ "hello!" | upper | repeat 5 }}
+```
+
+produces this:
+
+```
+HELLO!HELLO!HELLO!HELLO!HELLO!
+```
+
+## Principles Driving Our Function Selection
+
+We followed these principles to decide which functions to add and how to implement them:
+
+- Use template functions to build layout. The following
+ types of operations are within the domain of template functions:
+ - Formatting
+ - Layout
+ - Simple type conversions
+ - Utilities that assist in handling common formatting and layout needs (e.g. arithmetic)
+- Template functions should not return errors unless there is no way to print
+ a sensible value. For example, converting a string to an integer should not
+ produce an error if conversion fails. Instead, it should display a default
+ value.
+- Simple math is necessary for grid layouts, pagers, and so on. Complex math
+ (anything other than arithmetic) should be done outside of templates.
+- Template functions only deal with the data passed into them. They never retrieve
+ data from a source.
+- Finally, do not override core Go template functions.
diff --git a/vendor/github.com/go-task/slim-sprig/Taskfile.yml b/vendor/github.com/go-task/slim-sprig/Taskfile.yml
new file mode 100644
index 000000000..cdcfd223b
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/Taskfile.yml
@@ -0,0 +1,12 @@
+# https://taskfile.dev
+
+version: '2'
+
+tasks:
+ default:
+ cmds:
+ - task: test
+
+ test:
+ cmds:
+ - go test -v .
diff --git a/vendor/github.com/go-task/slim-sprig/crypto.go b/vendor/github.com/go-task/slim-sprig/crypto.go
new file mode 100644
index 000000000..d06e516d4
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/crypto.go
@@ -0,0 +1,24 @@
+package sprig
+
+import (
+ "crypto/sha1"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "hash/adler32"
+)
+
+func sha256sum(input string) string {
+ hash := sha256.Sum256([]byte(input))
+ return hex.EncodeToString(hash[:])
+}
+
+func sha1sum(input string) string {
+ hash := sha1.Sum([]byte(input))
+ return hex.EncodeToString(hash[:])
+}
+
+func adler32sum(input string) string {
+ hash := adler32.Checksum([]byte(input))
+ return fmt.Sprintf("%d", hash)
+}
diff --git a/vendor/github.com/go-task/slim-sprig/date.go b/vendor/github.com/go-task/slim-sprig/date.go
new file mode 100644
index 000000000..ed022ddac
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/date.go
@@ -0,0 +1,152 @@
+package sprig
+
+import (
+ "strconv"
+ "time"
+)
+
+// Given a format and a date, format the date string.
+//
+// Date can be a `time.Time` or an `int, int32, int64`.
+// In the later case, it is treated as seconds since UNIX
+// epoch.
+func date(fmt string, date interface{}) string {
+ return dateInZone(fmt, date, "Local")
+}
+
+func htmlDate(date interface{}) string {
+ return dateInZone("2006-01-02", date, "Local")
+}
+
+func htmlDateInZone(date interface{}, zone string) string {
+ return dateInZone("2006-01-02", date, zone)
+}
+
+func dateInZone(fmt string, date interface{}, zone string) string {
+ var t time.Time
+ switch date := date.(type) {
+ default:
+ t = time.Now()
+ case time.Time:
+ t = date
+ case *time.Time:
+ t = *date
+ case int64:
+ t = time.Unix(date, 0)
+ case int:
+ t = time.Unix(int64(date), 0)
+ case int32:
+ t = time.Unix(int64(date), 0)
+ }
+
+ loc, err := time.LoadLocation(zone)
+ if err != nil {
+ loc, _ = time.LoadLocation("UTC")
+ }
+
+ return t.In(loc).Format(fmt)
+}
+
+func dateModify(fmt string, date time.Time) time.Time {
+ d, err := time.ParseDuration(fmt)
+ if err != nil {
+ return date
+ }
+ return date.Add(d)
+}
+
+func mustDateModify(fmt string, date time.Time) (time.Time, error) {
+ d, err := time.ParseDuration(fmt)
+ if err != nil {
+ return time.Time{}, err
+ }
+ return date.Add(d), nil
+}
+
+func dateAgo(date interface{}) string {
+ var t time.Time
+
+ switch date := date.(type) {
+ default:
+ t = time.Now()
+ case time.Time:
+ t = date
+ case int64:
+ t = time.Unix(date, 0)
+ case int:
+ t = time.Unix(int64(date), 0)
+ }
+ // Drop resolution to seconds
+ duration := time.Since(t).Round(time.Second)
+ return duration.String()
+}
+
+func duration(sec interface{}) string {
+ var n int64
+ switch value := sec.(type) {
+ default:
+ n = 0
+ case string:
+ n, _ = strconv.ParseInt(value, 10, 64)
+ case int64:
+ n = value
+ }
+ return (time.Duration(n) * time.Second).String()
+}
+
+func durationRound(duration interface{}) string {
+ var d time.Duration
+ switch duration := duration.(type) {
+ default:
+ d = 0
+ case string:
+ d, _ = time.ParseDuration(duration)
+ case int64:
+ d = time.Duration(duration)
+ case time.Time:
+ d = time.Since(duration)
+ }
+
+ u := uint64(d)
+ neg := d < 0
+ if neg {
+ u = -u
+ }
+
+ var (
+ year = uint64(time.Hour) * 24 * 365
+ month = uint64(time.Hour) * 24 * 30
+ day = uint64(time.Hour) * 24
+ hour = uint64(time.Hour)
+ minute = uint64(time.Minute)
+ second = uint64(time.Second)
+ )
+ switch {
+ case u > year:
+ return strconv.FormatUint(u/year, 10) + "y"
+ case u > month:
+ return strconv.FormatUint(u/month, 10) + "mo"
+ case u > day:
+ return strconv.FormatUint(u/day, 10) + "d"
+ case u > hour:
+ return strconv.FormatUint(u/hour, 10) + "h"
+ case u > minute:
+ return strconv.FormatUint(u/minute, 10) + "m"
+ case u > second:
+ return strconv.FormatUint(u/second, 10) + "s"
+ }
+ return "0s"
+}
+
+func toDate(fmt, str string) time.Time {
+ t, _ := time.ParseInLocation(fmt, str, time.Local)
+ return t
+}
+
+func mustToDate(fmt, str string) (time.Time, error) {
+ return time.ParseInLocation(fmt, str, time.Local)
+}
+
+func unixEpoch(date time.Time) string {
+ return strconv.FormatInt(date.Unix(), 10)
+}
diff --git a/vendor/github.com/go-task/slim-sprig/defaults.go b/vendor/github.com/go-task/slim-sprig/defaults.go
new file mode 100644
index 000000000..b9f979666
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/defaults.go
@@ -0,0 +1,163 @@
+package sprig
+
+import (
+ "bytes"
+ "encoding/json"
+ "math/rand"
+ "reflect"
+ "strings"
+ "time"
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+}
+
+// dfault checks whether `given` is set, and returns default if not set.
+//
+// This returns `d` if `given` appears not to be set, and `given` otherwise.
+//
+// For numeric types 0 is unset.
+// For strings, maps, arrays, and slices, len() = 0 is considered unset.
+// For bool, false is unset.
+// Structs are never considered unset.
+//
+// For everything else, including pointers, a nil value is unset.
+func dfault(d interface{}, given ...interface{}) interface{} {
+
+ if empty(given) || empty(given[0]) {
+ return d
+ }
+ return given[0]
+}
+
+// empty returns true if the given value has the zero value for its type.
+func empty(given interface{}) bool {
+ g := reflect.ValueOf(given)
+ if !g.IsValid() {
+ return true
+ }
+
+ // Basically adapted from text/template.isTrue
+ switch g.Kind() {
+ default:
+ return g.IsNil()
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return g.Len() == 0
+ case reflect.Bool:
+ return !g.Bool()
+ case reflect.Complex64, reflect.Complex128:
+ return g.Complex() == 0
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return g.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return g.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return g.Float() == 0
+ case reflect.Struct:
+ return false
+ }
+}
+
+// coalesce returns the first non-empty value.
+func coalesce(v ...interface{}) interface{} {
+ for _, val := range v {
+ if !empty(val) {
+ return val
+ }
+ }
+ return nil
+}
+
+// all returns true if empty(x) is false for all values x in the list.
+// If the list is empty, return true.
+func all(v ...interface{}) bool {
+ for _, val := range v {
+ if empty(val) {
+ return false
+ }
+ }
+ return true
+}
+
+// any returns true if empty(x) is false for any x in the list.
+// If the list is empty, return false.
+func any(v ...interface{}) bool {
+ for _, val := range v {
+ if !empty(val) {
+ return true
+ }
+ }
+ return false
+}
+
+// fromJson decodes JSON into a structured value, ignoring errors.
+func fromJson(v string) interface{} {
+ output, _ := mustFromJson(v)
+ return output
+}
+
+// mustFromJson decodes JSON into a structured value, returning errors.
+func mustFromJson(v string) (interface{}, error) {
+ var output interface{}
+ err := json.Unmarshal([]byte(v), &output)
+ return output, err
+}
+
+// toJson encodes an item into a JSON string
+func toJson(v interface{}) string {
+ output, _ := json.Marshal(v)
+ return string(output)
+}
+
+func mustToJson(v interface{}) (string, error) {
+ output, err := json.Marshal(v)
+ if err != nil {
+ return "", err
+ }
+ return string(output), nil
+}
+
+// toPrettyJson encodes an item into a pretty (indented) JSON string
+func toPrettyJson(v interface{}) string {
+ output, _ := json.MarshalIndent(v, "", " ")
+ return string(output)
+}
+
+func mustToPrettyJson(v interface{}) (string, error) {
+ output, err := json.MarshalIndent(v, "", " ")
+ if err != nil {
+ return "", err
+ }
+ return string(output), nil
+}
+
+// toRawJson encodes an item into a JSON string with no escaping of HTML characters.
+func toRawJson(v interface{}) string {
+ output, err := mustToRawJson(v)
+ if err != nil {
+ panic(err)
+ }
+ return string(output)
+}
+
+// mustToRawJson encodes an item into a JSON string with no escaping of HTML characters.
+func mustToRawJson(v interface{}) (string, error) {
+ buf := new(bytes.Buffer)
+ enc := json.NewEncoder(buf)
+ enc.SetEscapeHTML(false)
+ err := enc.Encode(&v)
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSuffix(buf.String(), "\n"), nil
+}
+
+// ternary returns the first value if the last value is true, otherwise returns the second value.
+func ternary(vt interface{}, vf interface{}, v bool) interface{} {
+ if v {
+ return vt
+ }
+
+ return vf
+}
diff --git a/vendor/github.com/go-task/slim-sprig/dict.go b/vendor/github.com/go-task/slim-sprig/dict.go
new file mode 100644
index 000000000..77ebc61b1
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/dict.go
@@ -0,0 +1,118 @@
+package sprig
+
+func get(d map[string]interface{}, key string) interface{} {
+ if val, ok := d[key]; ok {
+ return val
+ }
+ return ""
+}
+
+func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
+ d[key] = value
+ return d
+}
+
+func unset(d map[string]interface{}, key string) map[string]interface{} {
+ delete(d, key)
+ return d
+}
+
+func hasKey(d map[string]interface{}, key string) bool {
+ _, ok := d[key]
+ return ok
+}
+
+func pluck(key string, d ...map[string]interface{}) []interface{} {
+ res := []interface{}{}
+ for _, dict := range d {
+ if val, ok := dict[key]; ok {
+ res = append(res, val)
+ }
+ }
+ return res
+}
+
+func keys(dicts ...map[string]interface{}) []string {
+ k := []string{}
+ for _, dict := range dicts {
+ for key := range dict {
+ k = append(k, key)
+ }
+ }
+ return k
+}
+
+func pick(dict map[string]interface{}, keys ...string) map[string]interface{} {
+ res := map[string]interface{}{}
+ for _, k := range keys {
+ if v, ok := dict[k]; ok {
+ res[k] = v
+ }
+ }
+ return res
+}
+
+func omit(dict map[string]interface{}, keys ...string) map[string]interface{} {
+ res := map[string]interface{}{}
+
+ omit := make(map[string]bool, len(keys))
+ for _, k := range keys {
+ omit[k] = true
+ }
+
+ for k, v := range dict {
+ if _, ok := omit[k]; !ok {
+ res[k] = v
+ }
+ }
+ return res
+}
+
+func dict(v ...interface{}) map[string]interface{} {
+ dict := map[string]interface{}{}
+ lenv := len(v)
+ for i := 0; i < lenv; i += 2 {
+ key := strval(v[i])
+ if i+1 >= lenv {
+ dict[key] = ""
+ continue
+ }
+ dict[key] = v[i+1]
+ }
+ return dict
+}
+
+func values(dict map[string]interface{}) []interface{} {
+ values := []interface{}{}
+ for _, value := range dict {
+ values = append(values, value)
+ }
+
+ return values
+}
+
+func dig(ps ...interface{}) (interface{}, error) {
+ if len(ps) < 3 {
+ panic("dig needs at least three arguments")
+ }
+ dict := ps[len(ps)-1].(map[string]interface{})
+ def := ps[len(ps)-2]
+ ks := make([]string, len(ps)-2)
+ for i := 0; i < len(ks); i++ {
+ ks[i] = ps[i].(string)
+ }
+
+ return digFromDict(dict, def, ks)
+}
+
+func digFromDict(dict map[string]interface{}, d interface{}, ks []string) (interface{}, error) {
+ k, ns := ks[0], ks[1:len(ks)]
+ step, has := dict[k]
+ if !has {
+ return d, nil
+ }
+ if len(ns) == 0 {
+ return step, nil
+ }
+ return digFromDict(step.(map[string]interface{}), d, ns)
+}
diff --git a/vendor/github.com/go-task/slim-sprig/doc.go b/vendor/github.com/go-task/slim-sprig/doc.go
new file mode 100644
index 000000000..aabb9d448
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/doc.go
@@ -0,0 +1,19 @@
+/*
+Package sprig provides template functions for Go.
+
+This package contains a number of utility functions for working with data
+inside of Go `html/template` and `text/template` files.
+
+To add these functions, use the `template.Funcs()` method:
+
+ t := templates.New("foo").Funcs(sprig.FuncMap())
+
+Note that you should add the function map before you parse any template files.
+
+ In several cases, Sprig reverses the order of arguments from the way they
+ appear in the standard library. This is to make it easier to pipe
+ arguments into functions.
+
+See http://masterminds.github.io/sprig/ for more detailed documentation on each of the available functions.
+*/
+package sprig
diff --git a/vendor/github.com/go-task/slim-sprig/functions.go b/vendor/github.com/go-task/slim-sprig/functions.go
new file mode 100644
index 000000000..5ea74f899
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/functions.go
@@ -0,0 +1,317 @@
+package sprig
+
+import (
+ "errors"
+ "html/template"
+ "math/rand"
+ "os"
+ "path"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "strings"
+ ttemplate "text/template"
+ "time"
+)
+
+// FuncMap produces the function map.
+//
+// Use this to pass the functions into the template engine:
+//
+// tpl := template.New("foo").Funcs(sprig.FuncMap()))
+//
+func FuncMap() template.FuncMap {
+ return HtmlFuncMap()
+}
+
+// HermeticTxtFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
+func HermeticTxtFuncMap() ttemplate.FuncMap {
+ r := TxtFuncMap()
+ for _, name := range nonhermeticFunctions {
+ delete(r, name)
+ }
+ return r
+}
+
+// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
+func HermeticHtmlFuncMap() template.FuncMap {
+ r := HtmlFuncMap()
+ for _, name := range nonhermeticFunctions {
+ delete(r, name)
+ }
+ return r
+}
+
+// TxtFuncMap returns a 'text/template'.FuncMap
+func TxtFuncMap() ttemplate.FuncMap {
+ return ttemplate.FuncMap(GenericFuncMap())
+}
+
+// HtmlFuncMap returns an 'html/template'.Funcmap
+func HtmlFuncMap() template.FuncMap {
+ return template.FuncMap(GenericFuncMap())
+}
+
+// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
+func GenericFuncMap() map[string]interface{} {
+ gfm := make(map[string]interface{}, len(genericMap))
+ for k, v := range genericMap {
+ gfm[k] = v
+ }
+ return gfm
+}
+
+// These functions are not guaranteed to evaluate to the same result for given input, because they
+// refer to the environment or global state.
+var nonhermeticFunctions = []string{
+ // Date functions
+ "date",
+ "date_in_zone",
+ "date_modify",
+ "now",
+ "htmlDate",
+ "htmlDateInZone",
+ "dateInZone",
+ "dateModify",
+
+ // Strings
+ "randAlphaNum",
+ "randAlpha",
+ "randAscii",
+ "randNumeric",
+ "randBytes",
+ "uuidv4",
+
+ // OS
+ "env",
+ "expandenv",
+
+ // Network
+ "getHostByName",
+}
+
+var genericMap = map[string]interface{}{
+ "hello": func() string { return "Hello!" },
+
+ // Date functions
+ "ago": dateAgo,
+ "date": date,
+ "date_in_zone": dateInZone,
+ "date_modify": dateModify,
+ "dateInZone": dateInZone,
+ "dateModify": dateModify,
+ "duration": duration,
+ "durationRound": durationRound,
+ "htmlDate": htmlDate,
+ "htmlDateInZone": htmlDateInZone,
+ "must_date_modify": mustDateModify,
+ "mustDateModify": mustDateModify,
+ "mustToDate": mustToDate,
+ "now": time.Now,
+ "toDate": toDate,
+ "unixEpoch": unixEpoch,
+
+ // Strings
+ "trunc": trunc,
+ "trim": strings.TrimSpace,
+ "upper": strings.ToUpper,
+ "lower": strings.ToLower,
+ "title": strings.Title,
+ "substr": substring,
+ // Switch order so that "foo" | repeat 5
+ "repeat": func(count int, str string) string { return strings.Repeat(str, count) },
+ // Deprecated: Use trimAll.
+ "trimall": func(a, b string) string { return strings.Trim(b, a) },
+ // Switch order so that "$foo" | trimall "$"
+ "trimAll": func(a, b string) string { return strings.Trim(b, a) },
+ "trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
+ "trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
+ // Switch order so that "foobar" | contains "foo"
+ "contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
+ "hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
+ "hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
+ "quote": quote,
+ "squote": squote,
+ "cat": cat,
+ "indent": indent,
+ "nindent": nindent,
+ "replace": replace,
+ "plural": plural,
+ "sha1sum": sha1sum,
+ "sha256sum": sha256sum,
+ "adler32sum": adler32sum,
+ "toString": strval,
+
+ // Wrap Atoi to stop errors.
+ "atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
+ "int64": toInt64,
+ "int": toInt,
+ "float64": toFloat64,
+ "seq": seq,
+ "toDecimal": toDecimal,
+
+ //"gt": func(a, b int) bool {return a > b},
+ //"gte": func(a, b int) bool {return a >= b},
+ //"lt": func(a, b int) bool {return a < b},
+ //"lte": func(a, b int) bool {return a <= b},
+
+ // split "/" foo/bar returns map[int]string{0: foo, 1: bar}
+ "split": split,
+ "splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
+ // splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu}
+ "splitn": splitn,
+ "toStrings": strslice,
+
+ "until": until,
+ "untilStep": untilStep,
+
+ // VERY basic arithmetic.
+ "add1": func(i interface{}) int64 { return toInt64(i) + 1 },
+ "add": func(i ...interface{}) int64 {
+ var a int64 = 0
+ for _, b := range i {
+ a += toInt64(b)
+ }
+ return a
+ },
+ "sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
+ "div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
+ "mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
+ "mul": func(a interface{}, v ...interface{}) int64 {
+ val := toInt64(a)
+ for _, b := range v {
+ val = val * toInt64(b)
+ }
+ return val
+ },
+ "randInt": func(min, max int) int { return rand.Intn(max-min) + min },
+ "biggest": max,
+ "max": max,
+ "min": min,
+ "maxf": maxf,
+ "minf": minf,
+ "ceil": ceil,
+ "floor": floor,
+ "round": round,
+
+ // string slices. Note that we reverse the order b/c that's better
+ // for template processing.
+ "join": join,
+ "sortAlpha": sortAlpha,
+
+ // Defaults
+ "default": dfault,
+ "empty": empty,
+ "coalesce": coalesce,
+ "all": all,
+ "any": any,
+ "compact": compact,
+ "mustCompact": mustCompact,
+ "fromJson": fromJson,
+ "toJson": toJson,
+ "toPrettyJson": toPrettyJson,
+ "toRawJson": toRawJson,
+ "mustFromJson": mustFromJson,
+ "mustToJson": mustToJson,
+ "mustToPrettyJson": mustToPrettyJson,
+ "mustToRawJson": mustToRawJson,
+ "ternary": ternary,
+
+ // Reflection
+ "typeOf": typeOf,
+ "typeIs": typeIs,
+ "typeIsLike": typeIsLike,
+ "kindOf": kindOf,
+ "kindIs": kindIs,
+ "deepEqual": reflect.DeepEqual,
+
+ // OS:
+ "env": os.Getenv,
+ "expandenv": os.ExpandEnv,
+
+ // Network:
+ "getHostByName": getHostByName,
+
+ // Paths:
+ "base": path.Base,
+ "dir": path.Dir,
+ "clean": path.Clean,
+ "ext": path.Ext,
+ "isAbs": path.IsAbs,
+
+ // Filepaths:
+ "osBase": filepath.Base,
+ "osClean": filepath.Clean,
+ "osDir": filepath.Dir,
+ "osExt": filepath.Ext,
+ "osIsAbs": filepath.IsAbs,
+
+ // Encoding:
+ "b64enc": base64encode,
+ "b64dec": base64decode,
+ "b32enc": base32encode,
+ "b32dec": base32decode,
+
+ // Data Structures:
+ "tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
+ "list": list,
+ "dict": dict,
+ "get": get,
+ "set": set,
+ "unset": unset,
+ "hasKey": hasKey,
+ "pluck": pluck,
+ "keys": keys,
+ "pick": pick,
+ "omit": omit,
+ "values": values,
+
+ "append": push, "push": push,
+ "mustAppend": mustPush, "mustPush": mustPush,
+ "prepend": prepend,
+ "mustPrepend": mustPrepend,
+ "first": first,
+ "mustFirst": mustFirst,
+ "rest": rest,
+ "mustRest": mustRest,
+ "last": last,
+ "mustLast": mustLast,
+ "initial": initial,
+ "mustInitial": mustInitial,
+ "reverse": reverse,
+ "mustReverse": mustReverse,
+ "uniq": uniq,
+ "mustUniq": mustUniq,
+ "without": without,
+ "mustWithout": mustWithout,
+ "has": has,
+ "mustHas": mustHas,
+ "slice": slice,
+ "mustSlice": mustSlice,
+ "concat": concat,
+ "dig": dig,
+ "chunk": chunk,
+ "mustChunk": mustChunk,
+
+ // Flow Control:
+ "fail": func(msg string) (string, error) { return "", errors.New(msg) },
+
+ // Regex
+ "regexMatch": regexMatch,
+ "mustRegexMatch": mustRegexMatch,
+ "regexFindAll": regexFindAll,
+ "mustRegexFindAll": mustRegexFindAll,
+ "regexFind": regexFind,
+ "mustRegexFind": mustRegexFind,
+ "regexReplaceAll": regexReplaceAll,
+ "mustRegexReplaceAll": mustRegexReplaceAll,
+ "regexReplaceAllLiteral": regexReplaceAllLiteral,
+ "mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral,
+ "regexSplit": regexSplit,
+ "mustRegexSplit": mustRegexSplit,
+ "regexQuoteMeta": regexQuoteMeta,
+
+ // URLs:
+ "urlParse": urlParse,
+ "urlJoin": urlJoin,
+}
diff --git a/vendor/github.com/go-task/slim-sprig/go.mod b/vendor/github.com/go-task/slim-sprig/go.mod
new file mode 100644
index 000000000..d90a221be
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/go.mod
@@ -0,0 +1,8 @@
+module github.com/go-task/slim-sprig
+
+go 1.13
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/stretchr/testify v1.5.1
+)
diff --git a/vendor/github.com/go-task/slim-sprig/go.sum b/vendor/github.com/go-task/slim-sprig/go.sum
new file mode 100644
index 000000000..256ef8493
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/go.sum
@@ -0,0 +1,22 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/go-task/slim-sprig/list.go b/vendor/github.com/go-task/slim-sprig/list.go
new file mode 100644
index 000000000..ca0fbb789
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/list.go
@@ -0,0 +1,464 @@
+package sprig
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "sort"
+)
+
+// Reflection is used in these functions so that slices and arrays of strings,
+// ints, and other types not implementing []interface{} can be worked with.
+// For example, this is useful if you need to work on the output of regexs.
+
+func list(v ...interface{}) []interface{} {
+ return v
+}
+
+func push(list interface{}, v interface{}) []interface{} {
+ l, err := mustPush(list, v)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustPush(list interface{}, v interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ nl := make([]interface{}, l)
+ for i := 0; i < l; i++ {
+ nl[i] = l2.Index(i).Interface()
+ }
+
+ return append(nl, v), nil
+
+ default:
+ return nil, fmt.Errorf("Cannot push on type %s", tp)
+ }
+}
+
+func prepend(list interface{}, v interface{}) []interface{} {
+ l, err := mustPrepend(list, v)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustPrepend(list interface{}, v interface{}) ([]interface{}, error) {
+ //return append([]interface{}{v}, list...)
+
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ nl := make([]interface{}, l)
+ for i := 0; i < l; i++ {
+ nl[i] = l2.Index(i).Interface()
+ }
+
+ return append([]interface{}{v}, nl...), nil
+
+ default:
+ return nil, fmt.Errorf("Cannot prepend on type %s", tp)
+ }
+}
+
+func chunk(size int, list interface{}) [][]interface{} {
+ l, err := mustChunk(size, list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustChunk(size int, list interface{}) ([][]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+
+ cs := int(math.Floor(float64(l-1)/float64(size)) + 1)
+ nl := make([][]interface{}, cs)
+
+ for i := 0; i < cs; i++ {
+ clen := size
+ if i == cs-1 {
+ clen = int(math.Floor(math.Mod(float64(l), float64(size))))
+ if clen == 0 {
+ clen = size
+ }
+ }
+
+ nl[i] = make([]interface{}, clen)
+
+ for j := 0; j < clen; j++ {
+ ix := i*size + j
+ nl[i][j] = l2.Index(ix).Interface()
+ }
+ }
+
+ return nl, nil
+
+ default:
+ return nil, fmt.Errorf("Cannot chunk type %s", tp)
+ }
+}
+
+func last(list interface{}) interface{} {
+ l, err := mustLast(list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustLast(list interface{}) (interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ if l == 0 {
+ return nil, nil
+ }
+
+ return l2.Index(l - 1).Interface(), nil
+ default:
+ return nil, fmt.Errorf("Cannot find last on type %s", tp)
+ }
+}
+
+func first(list interface{}) interface{} {
+ l, err := mustFirst(list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustFirst(list interface{}) (interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ if l == 0 {
+ return nil, nil
+ }
+
+ return l2.Index(0).Interface(), nil
+ default:
+ return nil, fmt.Errorf("Cannot find first on type %s", tp)
+ }
+}
+
+func rest(list interface{}) []interface{} {
+ l, err := mustRest(list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustRest(list interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ if l == 0 {
+ return nil, nil
+ }
+
+ nl := make([]interface{}, l-1)
+ for i := 1; i < l; i++ {
+ nl[i-1] = l2.Index(i).Interface()
+ }
+
+ return nl, nil
+ default:
+ return nil, fmt.Errorf("Cannot find rest on type %s", tp)
+ }
+}
+
+func initial(list interface{}) []interface{} {
+ l, err := mustInitial(list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustInitial(list interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ if l == 0 {
+ return nil, nil
+ }
+
+ nl := make([]interface{}, l-1)
+ for i := 0; i < l-1; i++ {
+ nl[i] = l2.Index(i).Interface()
+ }
+
+ return nl, nil
+ default:
+ return nil, fmt.Errorf("Cannot find initial on type %s", tp)
+ }
+}
+
+func sortAlpha(list interface{}) []string {
+ k := reflect.Indirect(reflect.ValueOf(list)).Kind()
+ switch k {
+ case reflect.Slice, reflect.Array:
+ a := strslice(list)
+ s := sort.StringSlice(a)
+ s.Sort()
+ return s
+ }
+ return []string{strval(list)}
+}
+
+func reverse(v interface{}) []interface{} {
+ l, err := mustReverse(v)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustReverse(v interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(v).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(v)
+
+ l := l2.Len()
+ // We do not sort in place because the incoming array should not be altered.
+ nl := make([]interface{}, l)
+ for i := 0; i < l; i++ {
+ nl[l-i-1] = l2.Index(i).Interface()
+ }
+
+ return nl, nil
+ default:
+ return nil, fmt.Errorf("Cannot find reverse on type %s", tp)
+ }
+}
+
+func compact(list interface{}) []interface{} {
+ l, err := mustCompact(list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustCompact(list interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ nl := []interface{}{}
+ var item interface{}
+ for i := 0; i < l; i++ {
+ item = l2.Index(i).Interface()
+ if !empty(item) {
+ nl = append(nl, item)
+ }
+ }
+
+ return nl, nil
+ default:
+ return nil, fmt.Errorf("Cannot compact on type %s", tp)
+ }
+}
+
+func uniq(list interface{}) []interface{} {
+ l, err := mustUniq(list)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustUniq(list interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ dest := []interface{}{}
+ var item interface{}
+ for i := 0; i < l; i++ {
+ item = l2.Index(i).Interface()
+ if !inList(dest, item) {
+ dest = append(dest, item)
+ }
+ }
+
+ return dest, nil
+ default:
+ return nil, fmt.Errorf("Cannot find uniq on type %s", tp)
+ }
+}
+
+func inList(haystack []interface{}, needle interface{}) bool {
+ for _, h := range haystack {
+ if reflect.DeepEqual(needle, h) {
+ return true
+ }
+ }
+ return false
+}
+
+func without(list interface{}, omit ...interface{}) []interface{} {
+ l, err := mustWithout(list, omit...)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustWithout(list interface{}, omit ...interface{}) ([]interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ res := []interface{}{}
+ var item interface{}
+ for i := 0; i < l; i++ {
+ item = l2.Index(i).Interface()
+ if !inList(omit, item) {
+ res = append(res, item)
+ }
+ }
+
+ return res, nil
+ default:
+ return nil, fmt.Errorf("Cannot find without on type %s", tp)
+ }
+}
+
+func has(needle interface{}, haystack interface{}) bool {
+ l, err := mustHas(needle, haystack)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustHas(needle interface{}, haystack interface{}) (bool, error) {
+ if haystack == nil {
+ return false, nil
+ }
+ tp := reflect.TypeOf(haystack).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(haystack)
+ var item interface{}
+ l := l2.Len()
+ for i := 0; i < l; i++ {
+ item = l2.Index(i).Interface()
+ if reflect.DeepEqual(needle, item) {
+ return true, nil
+ }
+ }
+
+ return false, nil
+ default:
+ return false, fmt.Errorf("Cannot find has on type %s", tp)
+ }
+}
+
+// $list := [1, 2, 3, 4, 5]
+// slice $list -> list[0:5] = list[:]
+// slice $list 0 3 -> list[0:3] = list[:3]
+// slice $list 3 5 -> list[3:5]
+// slice $list 3 -> list[3:5] = list[3:]
+func slice(list interface{}, indices ...interface{}) interface{} {
+ l, err := mustSlice(list, indices...)
+ if err != nil {
+ panic(err)
+ }
+
+ return l
+}
+
+func mustSlice(list interface{}, indices ...interface{}) (interface{}, error) {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+
+ l := l2.Len()
+ if l == 0 {
+ return nil, nil
+ }
+
+ var start, end int
+ if len(indices) > 0 {
+ start = toInt(indices[0])
+ }
+ if len(indices) < 2 {
+ end = l
+ } else {
+ end = toInt(indices[1])
+ }
+
+ return l2.Slice(start, end).Interface(), nil
+ default:
+ return nil, fmt.Errorf("list should be type of slice or array but %s", tp)
+ }
+}
+
+func concat(lists ...interface{}) interface{} {
+ var res []interface{}
+ for _, list := range lists {
+ tp := reflect.TypeOf(list).Kind()
+ switch tp {
+ case reflect.Slice, reflect.Array:
+ l2 := reflect.ValueOf(list)
+ for i := 0; i < l2.Len(); i++ {
+ res = append(res, l2.Index(i).Interface())
+ }
+ default:
+ panic(fmt.Sprintf("Cannot concat type %s as list", tp))
+ }
+ }
+ return res
+}
diff --git a/vendor/github.com/go-task/slim-sprig/network.go b/vendor/github.com/go-task/slim-sprig/network.go
new file mode 100644
index 000000000..108d78a94
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/network.go
@@ -0,0 +1,12 @@
+package sprig
+
+import (
+ "math/rand"
+ "net"
+)
+
+func getHostByName(name string) string {
+ addrs, _ := net.LookupHost(name)
+ //TODO: add error handing when release v3 comes out
+ return addrs[rand.Intn(len(addrs))]
+}
diff --git a/vendor/github.com/go-task/slim-sprig/numeric.go b/vendor/github.com/go-task/slim-sprig/numeric.go
new file mode 100644
index 000000000..98cbb37a1
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/numeric.go
@@ -0,0 +1,228 @@
+package sprig
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// toFloat64 converts 64-bit floats
+func toFloat64(v interface{}) float64 {
+ if str, ok := v.(string); ok {
+ iv, err := strconv.ParseFloat(str, 64)
+ if err != nil {
+ return 0
+ }
+ return iv
+ }
+
+ val := reflect.Indirect(reflect.ValueOf(v))
+ switch val.Kind() {
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return float64(val.Int())
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ return float64(val.Uint())
+ case reflect.Uint, reflect.Uint64:
+ return float64(val.Uint())
+ case reflect.Float32, reflect.Float64:
+ return val.Float()
+ case reflect.Bool:
+ if val.Bool() {
+ return 1
+ }
+ return 0
+ default:
+ return 0
+ }
+}
+
+func toInt(v interface{}) int {
+ //It's not optimal. Bud I don't want duplicate toInt64 code.
+ return int(toInt64(v))
+}
+
+// toInt64 converts integer types to 64-bit integers
+func toInt64(v interface{}) int64 {
+ if str, ok := v.(string); ok {
+ iv, err := strconv.ParseInt(str, 10, 64)
+ if err != nil {
+ return 0
+ }
+ return iv
+ }
+
+ val := reflect.Indirect(reflect.ValueOf(v))
+ switch val.Kind() {
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return val.Int()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ return int64(val.Uint())
+ case reflect.Uint, reflect.Uint64:
+ tv := val.Uint()
+ if tv <= math.MaxInt64 {
+ return int64(tv)
+ }
+ // TODO: What is the sensible thing to do here?
+ return math.MaxInt64
+ case reflect.Float32, reflect.Float64:
+ return int64(val.Float())
+ case reflect.Bool:
+ if val.Bool() {
+ return 1
+ }
+ return 0
+ default:
+ return 0
+ }
+}
+
+func max(a interface{}, i ...interface{}) int64 {
+ aa := toInt64(a)
+ for _, b := range i {
+ bb := toInt64(b)
+ if bb > aa {
+ aa = bb
+ }
+ }
+ return aa
+}
+
+func maxf(a interface{}, i ...interface{}) float64 {
+ aa := toFloat64(a)
+ for _, b := range i {
+ bb := toFloat64(b)
+ aa = math.Max(aa, bb)
+ }
+ return aa
+}
+
+func min(a interface{}, i ...interface{}) int64 {
+ aa := toInt64(a)
+ for _, b := range i {
+ bb := toInt64(b)
+ if bb < aa {
+ aa = bb
+ }
+ }
+ return aa
+}
+
+func minf(a interface{}, i ...interface{}) float64 {
+ aa := toFloat64(a)
+ for _, b := range i {
+ bb := toFloat64(b)
+ aa = math.Min(aa, bb)
+ }
+ return aa
+}
+
+func until(count int) []int {
+ step := 1
+ if count < 0 {
+ step = -1
+ }
+ return untilStep(0, count, step)
+}
+
+func untilStep(start, stop, step int) []int {
+ v := []int{}
+
+ if stop < start {
+ if step >= 0 {
+ return v
+ }
+ for i := start; i > stop; i += step {
+ v = append(v, i)
+ }
+ return v
+ }
+
+ if step <= 0 {
+ return v
+ }
+ for i := start; i < stop; i += step {
+ v = append(v, i)
+ }
+ return v
+}
+
+func floor(a interface{}) float64 {
+ aa := toFloat64(a)
+ return math.Floor(aa)
+}
+
+func ceil(a interface{}) float64 {
+ aa := toFloat64(a)
+ return math.Ceil(aa)
+}
+
+func round(a interface{}, p int, rOpt ...float64) float64 {
+ roundOn := .5
+ if len(rOpt) > 0 {
+ roundOn = rOpt[0]
+ }
+ val := toFloat64(a)
+ places := toFloat64(p)
+
+ var round float64
+ pow := math.Pow(10, places)
+ digit := pow * val
+ _, div := math.Modf(digit)
+ if div >= roundOn {
+ round = math.Ceil(digit)
+ } else {
+ round = math.Floor(digit)
+ }
+ return round / pow
+}
+
+// converts unix octal to decimal
+func toDecimal(v interface{}) int64 {
+ result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64)
+ if err != nil {
+ return 0
+ }
+ return result
+}
+
+func seq(params ...int) string {
+ increment := 1
+ switch len(params) {
+ case 0:
+ return ""
+ case 1:
+ start := 1
+ end := params[0]
+ if end < start {
+ increment = -1
+ }
+ return intArrayToString(untilStep(start, end+increment, increment), " ")
+ case 3:
+ start := params[0]
+ end := params[2]
+ step := params[1]
+ if end < start {
+ increment = -1
+ if step > 0 {
+ return ""
+ }
+ }
+ return intArrayToString(untilStep(start, end+increment, step), " ")
+ case 2:
+ start := params[0]
+ end := params[1]
+ step := 1
+ if end < start {
+ step = -1
+ }
+ return intArrayToString(untilStep(start, end+step, step), " ")
+ default:
+ return ""
+ }
+}
+
+func intArrayToString(slice []int, delimeter string) string {
+ return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]")
+}
diff --git a/vendor/github.com/go-task/slim-sprig/reflect.go b/vendor/github.com/go-task/slim-sprig/reflect.go
new file mode 100644
index 000000000..8a65c132f
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/reflect.go
@@ -0,0 +1,28 @@
+package sprig
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// typeIs returns true if the src is the type named in target.
+func typeIs(target string, src interface{}) bool {
+ return target == typeOf(src)
+}
+
+func typeIsLike(target string, src interface{}) bool {
+ t := typeOf(src)
+ return target == t || "*"+target == t
+}
+
+func typeOf(src interface{}) string {
+ return fmt.Sprintf("%T", src)
+}
+
+func kindIs(target string, src interface{}) bool {
+ return target == kindOf(src)
+}
+
+func kindOf(src interface{}) string {
+ return reflect.ValueOf(src).Kind().String()
+}
diff --git a/vendor/github.com/go-task/slim-sprig/regex.go b/vendor/github.com/go-task/slim-sprig/regex.go
new file mode 100644
index 000000000..fab551018
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/regex.go
@@ -0,0 +1,83 @@
+package sprig
+
+import (
+ "regexp"
+)
+
+func regexMatch(regex string, s string) bool {
+ match, _ := regexp.MatchString(regex, s)
+ return match
+}
+
+func mustRegexMatch(regex string, s string) (bool, error) {
+ return regexp.MatchString(regex, s)
+}
+
+func regexFindAll(regex string, s string, n int) []string {
+ r := regexp.MustCompile(regex)
+ return r.FindAllString(s, n)
+}
+
+func mustRegexFindAll(regex string, s string, n int) ([]string, error) {
+ r, err := regexp.Compile(regex)
+ if err != nil {
+ return []string{}, err
+ }
+ return r.FindAllString(s, n), nil
+}
+
+func regexFind(regex string, s string) string {
+ r := regexp.MustCompile(regex)
+ return r.FindString(s)
+}
+
+func mustRegexFind(regex string, s string) (string, error) {
+ r, err := regexp.Compile(regex)
+ if err != nil {
+ return "", err
+ }
+ return r.FindString(s), nil
+}
+
+func regexReplaceAll(regex string, s string, repl string) string {
+ r := regexp.MustCompile(regex)
+ return r.ReplaceAllString(s, repl)
+}
+
+func mustRegexReplaceAll(regex string, s string, repl string) (string, error) {
+ r, err := regexp.Compile(regex)
+ if err != nil {
+ return "", err
+ }
+ return r.ReplaceAllString(s, repl), nil
+}
+
+func regexReplaceAllLiteral(regex string, s string, repl string) string {
+ r := regexp.MustCompile(regex)
+ return r.ReplaceAllLiteralString(s, repl)
+}
+
+func mustRegexReplaceAllLiteral(regex string, s string, repl string) (string, error) {
+ r, err := regexp.Compile(regex)
+ if err != nil {
+ return "", err
+ }
+ return r.ReplaceAllLiteralString(s, repl), nil
+}
+
+func regexSplit(regex string, s string, n int) []string {
+ r := regexp.MustCompile(regex)
+ return r.Split(s, n)
+}
+
+func mustRegexSplit(regex string, s string, n int) ([]string, error) {
+ r, err := regexp.Compile(regex)
+ if err != nil {
+ return []string{}, err
+ }
+ return r.Split(s, n), nil
+}
+
+func regexQuoteMeta(s string) string {
+ return regexp.QuoteMeta(s)
+}
diff --git a/vendor/github.com/go-task/slim-sprig/strings.go b/vendor/github.com/go-task/slim-sprig/strings.go
new file mode 100644
index 000000000..3c62d6b6f
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/strings.go
@@ -0,0 +1,189 @@
+package sprig
+
+import (
+ "encoding/base32"
+ "encoding/base64"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+func base64encode(v string) string {
+ return base64.StdEncoding.EncodeToString([]byte(v))
+}
+
+func base64decode(v string) string {
+ data, err := base64.StdEncoding.DecodeString(v)
+ if err != nil {
+ return err.Error()
+ }
+ return string(data)
+}
+
+func base32encode(v string) string {
+ return base32.StdEncoding.EncodeToString([]byte(v))
+}
+
+func base32decode(v string) string {
+ data, err := base32.StdEncoding.DecodeString(v)
+ if err != nil {
+ return err.Error()
+ }
+ return string(data)
+}
+
+func quote(str ...interface{}) string {
+ out := make([]string, 0, len(str))
+ for _, s := range str {
+ if s != nil {
+ out = append(out, fmt.Sprintf("%q", strval(s)))
+ }
+ }
+ return strings.Join(out, " ")
+}
+
+func squote(str ...interface{}) string {
+ out := make([]string, 0, len(str))
+ for _, s := range str {
+ if s != nil {
+ out = append(out, fmt.Sprintf("'%v'", s))
+ }
+ }
+ return strings.Join(out, " ")
+}
+
+func cat(v ...interface{}) string {
+ v = removeNilElements(v)
+ r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
+ return fmt.Sprintf(r, v...)
+}
+
+func indent(spaces int, v string) string {
+ pad := strings.Repeat(" ", spaces)
+ return pad + strings.Replace(v, "\n", "\n"+pad, -1)
+}
+
+func nindent(spaces int, v string) string {
+ return "\n" + indent(spaces, v)
+}
+
+func replace(old, new, src string) string {
+ return strings.Replace(src, old, new, -1)
+}
+
+func plural(one, many string, count int) string {
+ if count == 1 {
+ return one
+ }
+ return many
+}
+
+func strslice(v interface{}) []string {
+ switch v := v.(type) {
+ case []string:
+ return v
+ case []interface{}:
+ b := make([]string, 0, len(v))
+ for _, s := range v {
+ if s != nil {
+ b = append(b, strval(s))
+ }
+ }
+ return b
+ default:
+ val := reflect.ValueOf(v)
+ switch val.Kind() {
+ case reflect.Array, reflect.Slice:
+ l := val.Len()
+ b := make([]string, 0, l)
+ for i := 0; i < l; i++ {
+ value := val.Index(i).Interface()
+ if value != nil {
+ b = append(b, strval(value))
+ }
+ }
+ return b
+ default:
+ if v == nil {
+ return []string{}
+ }
+
+ return []string{strval(v)}
+ }
+ }
+}
+
+func removeNilElements(v []interface{}) []interface{} {
+ newSlice := make([]interface{}, 0, len(v))
+ for _, i := range v {
+ if i != nil {
+ newSlice = append(newSlice, i)
+ }
+ }
+ return newSlice
+}
+
+func strval(v interface{}) string {
+ switch v := v.(type) {
+ case string:
+ return v
+ case []byte:
+ return string(v)
+ case error:
+ return v.Error()
+ case fmt.Stringer:
+ return v.String()
+ default:
+ return fmt.Sprintf("%v", v)
+ }
+}
+
+func trunc(c int, s string) string {
+ if c < 0 && len(s)+c > 0 {
+ return s[len(s)+c:]
+ }
+ if c >= 0 && len(s) > c {
+ return s[:c]
+ }
+ return s
+}
+
+func join(sep string, v interface{}) string {
+ return strings.Join(strslice(v), sep)
+}
+
+func split(sep, orig string) map[string]string {
+ parts := strings.Split(orig, sep)
+ res := make(map[string]string, len(parts))
+ for i, v := range parts {
+ res["_"+strconv.Itoa(i)] = v
+ }
+ return res
+}
+
+func splitn(sep string, n int, orig string) map[string]string {
+ parts := strings.SplitN(orig, sep, n)
+ res := make(map[string]string, len(parts))
+ for i, v := range parts {
+ res["_"+strconv.Itoa(i)] = v
+ }
+ return res
+}
+
+// substring creates a substring of the given string.
+//
+// If start is < 0, this calls string[:end].
+//
+// If start is >= 0 and end < 0 or end bigger than s length, this calls string[start:]
+//
+// Otherwise, this calls string[start, end].
+func substring(start, end int, s string) string {
+ if start < 0 {
+ return s[:end]
+ }
+ if end < 0 || end > len(s) {
+ return s[start:]
+ }
+ return s[start:end]
+}
diff --git a/vendor/github.com/go-task/slim-sprig/url.go b/vendor/github.com/go-task/slim-sprig/url.go
new file mode 100644
index 000000000..b8e120e19
--- /dev/null
+++ b/vendor/github.com/go-task/slim-sprig/url.go
@@ -0,0 +1,66 @@
+package sprig
+
+import (
+ "fmt"
+ "net/url"
+ "reflect"
+)
+
+func dictGetOrEmpty(dict map[string]interface{}, key string) string {
+ value, ok := dict[key]
+ if !ok {
+ return ""
+ }
+ tp := reflect.TypeOf(value).Kind()
+ if tp != reflect.String {
+ panic(fmt.Sprintf("unable to parse %s key, must be of type string, but %s found", key, tp.String()))
+ }
+ return reflect.ValueOf(value).String()
+}
+
+// parses given URL to return dict object
+func urlParse(v string) map[string]interface{} {
+ dict := map[string]interface{}{}
+ parsedURL, err := url.Parse(v)
+ if err != nil {
+ panic(fmt.Sprintf("unable to parse url: %s", err))
+ }
+ dict["scheme"] = parsedURL.Scheme
+ dict["host"] = parsedURL.Host
+ dict["hostname"] = parsedURL.Hostname()
+ dict["path"] = parsedURL.Path
+ dict["query"] = parsedURL.RawQuery
+ dict["opaque"] = parsedURL.Opaque
+ dict["fragment"] = parsedURL.Fragment
+ if parsedURL.User != nil {
+ dict["userinfo"] = parsedURL.User.String()
+ } else {
+ dict["userinfo"] = ""
+ }
+
+ return dict
+}
+
+// join given dict to URL string
+func urlJoin(d map[string]interface{}) string {
+ resURL := url.URL{
+ Scheme: dictGetOrEmpty(d, "scheme"),
+ Host: dictGetOrEmpty(d, "host"),
+ Path: dictGetOrEmpty(d, "path"),
+ RawQuery: dictGetOrEmpty(d, "query"),
+ Opaque: dictGetOrEmpty(d, "opaque"),
+ Fragment: dictGetOrEmpty(d, "fragment"),
+ }
+ userinfo := dictGetOrEmpty(d, "userinfo")
+ var user *url.Userinfo
+ if userinfo != "" {
+ tempURL, err := url.Parse(fmt.Sprintf("proto://%s@host", userinfo))
+ if err != nil {
+ panic(fmt.Sprintf("unable to parse userinfo in dict: %s", err))
+ }
+ user = tempURL.User
+ }
+
+ resURL.User = user
+ return resURL.String()
+}
diff --git a/vendor/github.com/onsi/ginkgo/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/CHANGELOG.md
index 623b1ad37..f99f89480 100644
--- a/vendor/github.com/onsi/ginkgo/CHANGELOG.md
+++ b/vendor/github.com/onsi/ginkgo/CHANGELOG.md
@@ -1,3 +1,16 @@
+## 1.16.0
+
+### Features
+- Advertise Ginkgo 2.0. Introduce deprecations. [9ef1913]
+ - Update README.md to advertise that Ginkgo 2.0 is coming.
+ - Backport the 2.0 DeprecationTracker and start alerting users
+ about upcoming deprecations.
+
+- Add slim-sprig template functions to bootstrap/generate (#775) [9162b86]
+
+### Fixes
+- Fix accidental reference to 1488 (#784) [9fb7fe4]
+
## 1.15.2
### Fixes
diff --git a/vendor/github.com/onsi/ginkgo/README.md b/vendor/github.com/onsi/ginkgo/README.md
index 008cbcb75..05321e6ea 100644
--- a/vendor/github.com/onsi/ginkgo/README.md
+++ b/vendor/github.com/onsi/ginkgo/README.md
@@ -7,6 +7,18 @@ Jump to the [docs](https://onsi.github.io/ginkgo/) | [中文文档](https://ke-c
If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW).
+# Ginkgo 2.0 is coming soon!
+
+An effort is underway to develop and deliver Ginkgo 2.0. The work is happening in the [v2](https://github.com/onsi/ginkgo/tree/v2) branch and a changelog and migration guide is being maintained on that branch [here](https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md). Issue [#711](https://github.com/onsi/ginkgo/issues/711) is the central place for discussion and links to the original [proposal doc](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#).
+
+As described in the [changelog](https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md) and [proposal](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#), Ginkgo 2.0 will clean up the Ginkgo codebase, deprecate and remove some v1 functionality, and add several new much-requested features. To help users get ready for the migration, Ginkgo v1 has started emitting deprecation warnings for features that will no longer be supported with links to documentation for how to migrate away from these features. If you have concerns or comments please chime in on [#711](https://github.com/onsi/ginkgo/issues/711).
+
+The current timeline for completion of 2.0 looks like:
+
+- Early April 2021: first public release of 2.0, deprecation warnings land in v1.
+- May 2021: first beta/rc of 2.0 with most new functionality in place.
+- June/July 2021: 2.0 ships and fully replaces the 1.x codebase on master.
+
## TLDR
Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests.
It is typically (and optionally) paired with the [Gomega](https://github.com/onsi/gomega) matcher library.
diff --git a/vendor/github.com/onsi/ginkgo/config/config.go b/vendor/github.com/onsi/ginkgo/config/config.go
index d2857a7ef..25f8758a6 100644
--- a/vendor/github.com/onsi/ginkgo/config/config.go
+++ b/vendor/github.com/onsi/ginkgo/config/config.go
@@ -20,7 +20,7 @@ import (
"fmt"
)
-const VERSION = "1.15.2"
+const VERSION = "1.16.0"
type GinkgoConfigType struct {
RandomSeed int64
diff --git a/vendor/github.com/onsi/ginkgo/formatter/formatter.go b/vendor/github.com/onsi/ginkgo/formatter/formatter.go
new file mode 100644
index 000000000..30d7cbe12
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/formatter/formatter.go
@@ -0,0 +1,190 @@
+package formatter
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+)
+
+const COLS = 80
+
+type ColorMode uint8
+
+const (
+ ColorModeNone ColorMode = iota
+ ColorModeTerminal
+ ColorModePassthrough
+)
+
+var SingletonFormatter = New(ColorModeTerminal)
+
+func F(format string, args ...interface{}) string {
+ return SingletonFormatter.F(format, args...)
+}
+
+func Fi(indentation uint, format string, args ...interface{}) string {
+ return SingletonFormatter.Fi(indentation, format, args...)
+}
+
+func Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string {
+ return SingletonFormatter.Fiw(indentation, maxWidth, format, args...)
+}
+
+type Formatter struct {
+ ColorMode ColorMode
+ colors map[string]string
+ styleRe *regexp.Regexp
+ preserveColorStylingTags bool
+}
+
+func NewWithNoColorBool(noColor bool) Formatter {
+ if noColor {
+ return New(ColorModeNone)
+ }
+ return New(ColorModeTerminal)
+}
+
+func New(colorMode ColorMode) Formatter {
+ f := Formatter{
+ ColorMode: colorMode,
+ colors: map[string]string{
+ "/": "\x1b[0m",
+ "bold": "\x1b[1m",
+ "underline": "\x1b[4m",
+
+ "red": "\x1b[38;5;9m",
+ "orange": "\x1b[38;5;214m",
+ "coral": "\x1b[38;5;204m",
+ "magenta": "\x1b[38;5;13m",
+ "green": "\x1b[38;5;10m",
+ "dark-green": "\x1b[38;5;28m",
+ "yellow": "\x1b[38;5;11m",
+ "light-yellow": "\x1b[38;5;228m",
+ "cyan": "\x1b[38;5;14m",
+ "gray": "\x1b[38;5;243m",
+ "light-gray": "\x1b[38;5;246m",
+ "blue": "\x1b[38;5;12m",
+ },
+ }
+ colors := []string{}
+ for color := range f.colors {
+ colors = append(colors, color)
+ }
+ f.styleRe = regexp.MustCompile("{{(" + strings.Join(colors, "|") + ")}}")
+ return f
+}
+
+func (f Formatter) F(format string, args ...interface{}) string {
+ return f.Fi(0, format, args...)
+}
+
+func (f Formatter) Fi(indentation uint, format string, args ...interface{}) string {
+ return f.Fiw(indentation, 0, format, args...)
+}
+
+func (f Formatter) Fiw(indentation uint, maxWidth uint, format string, args ...interface{}) string {
+ out := fmt.Sprintf(f.style(format), args...)
+
+ if indentation == 0 && maxWidth == 0 {
+ return out
+ }
+
+ lines := strings.Split(out, "\n")
+
+ if maxWidth != 0 {
+ outLines := []string{}
+
+ maxWidth = maxWidth - indentation*2
+ for _, line := range lines {
+ if f.length(line) <= maxWidth {
+ outLines = append(outLines, line)
+ continue
+ }
+ outWords := []string{}
+ length := uint(0)
+ words := strings.Split(line, " ")
+ for _, word := range words {
+ wordLength := f.length(word)
+ if length+wordLength <= maxWidth {
+ length += wordLength
+ outWords = append(outWords, word)
+ continue
+ }
+ outLines = append(outLines, strings.Join(outWords, " "))
+ outWords = []string{word}
+ length = wordLength
+ }
+ if len(outWords) > 0 {
+ outLines = append(outLines, strings.Join(outWords, " "))
+ }
+ }
+
+ lines = outLines
+ }
+
+ if indentation == 0 {
+ return strings.Join(lines, "\n")
+ }
+
+ padding := strings.Repeat(" ", int(indentation))
+ for i := range lines {
+ if lines[i] != "" {
+ lines[i] = padding + lines[i]
+ }
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+func (f Formatter) length(styled string) uint {
+ n := uint(0)
+ inStyle := false
+ for _, b := range styled {
+ if inStyle {
+ if b == 'm' {
+ inStyle = false
+ }
+ continue
+ }
+ if b == '\x1b' {
+ inStyle = true
+ continue
+ }
+ n += 1
+ }
+ return n
+}
+
+func (f Formatter) CycleJoin(elements []string, joiner string, cycle []string) string {
+ if len(elements) == 0 {
+ return ""
+ }
+ n := len(cycle)
+ out := ""
+ for i, text := range elements {
+ out += cycle[i%n] + text
+ if i < len(elements)-1 {
+ out += joiner
+ }
+ }
+ out += "{{/}}"
+ return f.style(out)
+}
+
+func (f Formatter) style(s string) string {
+ switch f.ColorMode {
+ case ColorModeNone:
+ return f.styleRe.ReplaceAllString(s, "")
+ case ColorModePassthrough:
+ return s
+ case ColorModeTerminal:
+ return f.styleRe.ReplaceAllStringFunc(s, func(match string) string {
+ if out, ok := f.colors[strings.Trim(match, "{}")]; ok {
+ return out
+ }
+ return match
+ })
+ }
+
+ return ""
+}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
index 93150d1a4..6f5af3913 100644
--- a/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
@@ -12,6 +12,7 @@ import (
"go/build"
+ sprig "github.com/go-task/slim-sprig"
"github.com/onsi/ginkgo/ginkgo/nodot"
)
@@ -176,7 +177,7 @@ func generateBootstrap(agouti, noDot, internal bool, customBootstrapFile string)
templateText = bootstrapText
}
- bootstrapTemplate, err := template.New("bootstrap").Parse(templateText)
+ bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Parse(templateText)
if err != nil {
panic(err.Error())
}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
index 5944ed85c..8e99f56a2 100644
--- a/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
@@ -6,6 +6,8 @@ import (
"os"
"github.com/onsi/ginkgo/ginkgo/convert"
+ colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
+ "github.com/onsi/ginkgo/types"
)
func BuildConvertCommand() *Command {
@@ -21,6 +23,10 @@ func BuildConvertCommand() *Command {
}
func convertPackage(args []string, additionalArgs []string) {
+ deprecationTracker := types.NewDeprecationTracker()
+ deprecationTracker.TrackDeprecation(types.Deprecations.Convert())
+ fmt.Fprintln(colorable.NewColorableStderr(), deprecationTracker.DeprecationsReport())
+
if len(args) != 1 {
println(fmt.Sprintf("usage: ginkgo convert /path/to/your/package"))
os.Exit(1)
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
index 288df7797..27758beba 100644
--- a/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
@@ -10,6 +10,8 @@ import (
"strconv"
"strings"
"text/template"
+
+ sprig "github.com/go-task/slim-sprig"
)
func BuildGenerateCommand() *Command {
@@ -157,7 +159,7 @@ func generateSpecForSubject(subject string, agouti, noDot, internal bool, custom
templateText = specText
}
- specTemplate, err := template.New("spec").Parse(templateText)
+ specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Parse(templateText)
if err != nil {
return err
}
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go b/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
index f225d272f..dd87e1f89 100644
--- a/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
+++ b/vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
@@ -15,6 +15,7 @@ import (
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
"github.com/onsi/ginkgo/ginkgo/testrunner"
+ colorable "github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable"
"github.com/onsi/ginkgo/types"
)
@@ -53,6 +54,26 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
r.commandFlags.computeNodes()
r.notifier.VerifyNotificationsAreAvailable()
+ deprecationTracker := types.NewDeprecationTracker()
+
+ if r.commandFlags.ParallelStream {
+ deprecationTracker.TrackDeprecation(types.Deprecation{
+ Message: "--stream is deprecated and will be removed in Ginkgo 2.0",
+ DocLink: "removed--stream",
+ })
+ }
+
+ if r.commandFlags.Notify {
+ deprecationTracker.TrackDeprecation(types.Deprecation{
+ Message: "--notify is deprecated and will be removed in Ginkgo 2.0",
+ DocLink: "removed--notify",
+ })
+ }
+
+ if deprecationTracker.DidTrackDeprecations() {
+ fmt.Fprintln(colorable.NewColorableStderr(), deprecationTracker.DeprecationsReport())
+ }
+
suites, skippedPackages := findSuites(args, r.commandFlags.Recurse, r.commandFlags.SkipPackage, true)
if len(skippedPackages) > 0 {
fmt.Println("Will skip:")
diff --git a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go
index 7e8a48708..998c2c2ca 100644
--- a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go
+++ b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go
@@ -17,6 +17,7 @@ import (
"io"
"net/http"
"os"
+ "reflect"
"strings"
"time"
@@ -32,6 +33,8 @@ import (
"github.com/onsi/ginkgo/types"
)
+var deprecationTracker = types.NewDeprecationTracker()
+
const GINKGO_VERSION = config.VERSION
const GINKGO_PANIC = `
Your test failed.
@@ -205,21 +208,27 @@ func RunSpecs(t GinkgoTestingT, description string) bool {
if config.DefaultReporterConfig.ReportFile != "" {
reportFile := config.DefaultReporterConfig.ReportFile
specReporters[0] = reporters.NewJUnitReporter(reportFile)
- return RunSpecsWithDefaultAndCustomReporters(t, description, specReporters)
+ specReporters = append(specReporters, buildDefaultReporter())
}
- return RunSpecsWithCustomReporters(t, description, specReporters)
+ return runSpecsWithCustomReporters(t, description, specReporters)
}
//To run your tests with Ginkgo's default reporter and your custom reporter(s), replace
//RunSpecs() with this method.
func RunSpecsWithDefaultAndCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
+ deprecationTracker.TrackDeprecation(types.Deprecations.CustomReporter())
specReporters = append(specReporters, buildDefaultReporter())
- return RunSpecsWithCustomReporters(t, description, specReporters)
+ return runSpecsWithCustomReporters(t, description, specReporters)
}
//To run your tests with your custom reporter(s) (and *not* Ginkgo's default reporter), replace
//RunSpecs() with this method. Note that parallel tests will not work correctly without the default reporter
func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
+ deprecationTracker.TrackDeprecation(types.Deprecations.CustomReporter())
+ return runSpecsWithCustomReporters(t, description, specReporters)
+}
+
+func runSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
writer := GinkgoWriter.(*writer.Writer)
writer.SetStream(config.DefaultReporterConfig.Verbose)
reporters := make([]reporters.Reporter, len(specReporters))
@@ -227,6 +236,11 @@ func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor
reporters[i] = reporter
}
passed, hasFocusedTests := global.Suite.Run(t, description, reporters, writer, config.GinkgoConfig)
+
+ if deprecationTracker.DidTrackDeprecations() {
+ fmt.Fprintln(colorable.NewColorableStderr(), deprecationTracker.DeprecationsReport())
+ }
+
if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
fmt.Println("PASS | FOCUSED")
os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
@@ -380,12 +394,14 @@ func XWhen(text string, body func()) bool {
//Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a
//function that accepts a Done channel. When you do this, you can also provide an optional timeout.
func It(text string, body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
return true
}
//You can focus individual Its using FIt
func FIt(text string, body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -406,12 +422,14 @@ func XIt(text string, _ ...interface{}) bool {
//which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks
//which apply to It blocks.
func Specify(text string, body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
return true
}
//You can focus individual Specifys using FSpecify
func FSpecify(text string, body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -484,6 +502,7 @@ func XMeasure(text string, _ ...interface{}) bool {
//
//You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level.
func BeforeSuite(body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -497,6 +516,7 @@ func BeforeSuite(body interface{}, timeout ...float64) bool {
//
//You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level.
func AfterSuite(body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -584,6 +604,7 @@ func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, tim
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel
func BeforeEach(body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -594,6 +615,7 @@ func BeforeEach(body interface{}, timeout ...float64) bool {
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel
func JustBeforeEach(body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -604,6 +626,7 @@ func JustBeforeEach(body interface{}, timeout ...float64) bool {
//Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel
func JustAfterEach(body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
@@ -614,10 +637,30 @@ func JustAfterEach(body interface{}, timeout ...float64) bool {
//Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel
func AfterEach(body interface{}, timeout ...float64) bool {
+ validateBodyFunc(body, codelocation.New(1))
global.Suite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true
}
+func validateBodyFunc(body interface{}, cl types.CodeLocation) {
+ t := reflect.TypeOf(body)
+ if t.Kind() != reflect.Func {
+ return
+ }
+
+ if t.NumOut() > 0 {
+ return
+ }
+
+ if t.NumIn() == 0 {
+ return
+ }
+
+ if t.In(0) == reflect.TypeOf(make(Done)) {
+ deprecationTracker.TrackDeprecation(types.Deprecations.Async(), cl)
+ }
+}
+
func parseTimeout(timeout ...float64) time.Duration {
if len(timeout) == 0 {
return global.DefaultTimeout
diff --git a/vendor/github.com/onsi/ginkgo/go.mod b/vendor/github.com/onsi/ginkgo/go.mod
index 738a2f107..664372fb6 100644
--- a/vendor/github.com/onsi/ginkgo/go.mod
+++ b/vendor/github.com/onsi/ginkgo/go.mod
@@ -3,6 +3,7 @@ module github.com/onsi/ginkgo
go 1.15
require (
+ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
github.com/nxadm/tail v1.4.8
github.com/onsi/gomega v1.10.1
golang.org/x/sys v0.0.0-20210112080510-489259a85091
diff --git a/vendor/github.com/onsi/ginkgo/go.sum b/vendor/github.com/onsi/ginkgo/go.sum
index 8fdaac400..5c5c3c502 100644
--- a/vendor/github.com/onsi/ginkgo/go.sum
+++ b/vendor/github.com/onsi/ginkgo/go.sum
@@ -1,6 +1,11 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -22,6 +27,11 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -70,6 +80,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/onsi/ginkgo/types/deprecation_support.go b/vendor/github.com/onsi/ginkgo/types/deprecation_support.go
new file mode 100644
index 000000000..c7bbfbf41
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/types/deprecation_support.go
@@ -0,0 +1,96 @@
+package types
+
+import (
+ "github.com/onsi/ginkgo/formatter"
+)
+
+type Deprecation struct {
+ Message string
+ DocLink string
+}
+
+type deprecations struct{}
+
+var Deprecations = deprecations{}
+
+func (d deprecations) CustomReporter() Deprecation {
+ return Deprecation{
+ Message: "You are using a custom reporter. Support for custom reporters will likely be removed in V2. Most users were using them to generate junit or teamcity reports and this functionality will be merged into the core reporter. In addition, Ginkgo 2.0 will support emitting a JSON-formatted report that users can then manipulate to generate custom reports.\n\n{{red}}{{bold}}If this change will be impactful to you please leave a comment on {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}",
+ DocLink: "removed-custom-reporters",
+ }
+}
+
+func (d deprecations) V1Reporter() Deprecation {
+ return Deprecation{
+ Message: "You are using a V1 Ginkgo Reporter. Please update your custom reporter to the new V2 Reporter interface.",
+ DocLink: "changed-reporter-interface",
+ }
+}
+
+func (d deprecations) Async() Deprecation {
+ return Deprecation{
+ Message: "You are passing a Done channel to a test node to test asynchronous behavior. This is deprecated in Ginkgo V2. Your test will run synchronously and the timeout will be ignored.",
+ DocLink: "removed-async-testing",
+ }
+}
+
+func (d deprecations) Measure() Deprecation {
+ return Deprecation{
+ Message: "Measure is deprecated in Ginkgo V2",
+ DocLink: "removed-measure",
+ }
+}
+
+func (d deprecations) Convert() Deprecation {
+ return Deprecation{
+ Message: "The convert command is deprecated in Ginkgo V2",
+ DocLink: "removed-ginkgo-convert",
+ }
+}
+
+func (d deprecations) Blur() Deprecation {
+ return Deprecation{
+ Message: "The blur command is deprecated in Ginkgo V2. Use 'ginkgo unfocus' instead.",
+ }
+}
+
+type DeprecationTracker struct {
+ deprecations map[Deprecation][]CodeLocation
+}
+
+func NewDeprecationTracker() *DeprecationTracker {
+ return &DeprecationTracker{
+ deprecations: map[Deprecation][]CodeLocation{},
+ }
+}
+
+func (d *DeprecationTracker) TrackDeprecation(deprecation Deprecation, cl ...CodeLocation) {
+ if len(cl) == 1 {
+ d.deprecations[deprecation] = append(d.deprecations[deprecation], cl[0])
+ } else {
+ d.deprecations[deprecation] = []CodeLocation{}
+ }
+}
+
+func (d *DeprecationTracker) DidTrackDeprecations() bool {
+ return len(d.deprecations) > 0
+}
+
+func (d *DeprecationTracker) DeprecationsReport() string {
+ out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n")
+ out += formatter.F("{{light-yellow}}============================================={{/}}\n")
+ out += formatter.F("Ginkgo 2.0 is under active development and will introduce (a small number of) breaking changes.\n")
+ out += formatter.F("To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md{{/}}\n")
+ out += formatter.F("To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n")
+
+ for deprecation, locations := range d.deprecations {
+ out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n")
+ if deprecation.DocLink != "" {
+ out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink)
+ }
+ for _, location := range locations {
+ out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location)
+ }
+ }
+ return out
+}
diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/api/api.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/api/api.go
index b6779bf70..5d74cae49 100644
--- a/vendor/github.com/rootless-containers/rootlesskit/pkg/api/api.go
+++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/api/api.go
@@ -5,7 +5,7 @@ import "net"
const (
// Version of the REST API, not implementation version.
// See openapi.yaml for the definition.
- Version = "1.1.0"
+ Version = "1.1.1"
)
// ErrorJSON is returned with "application/json" content type and non-2XX status code
@@ -25,12 +25,15 @@ type Info struct {
// NetworkDriverInfo in Info
type NetworkDriverInfo struct {
- Driver string `json:"driver"`
- DNS []net.IP `json:"dns,omitempty"`
+ Driver string `json:"driver"`
+ DNS []net.IP `json:"dns,omitempty"`
+ ChildIP net.IP `json:"childIP,omitempty"` // since API v1.1.1 (RootlessKit v0.14.1)
+ DynamicChildIP bool `json:"dynamicChildIP,omitempty"` // since API v1.1.1
}
// PortDriverInfo in Info
type PortDriverInfo struct {
- Driver string `json:"driver"`
- Protos []string `json:"protos"`
+ Driver string `json:"driver"`
+ Protos []string `json:"protos"`
+ DisallowLoopbackChildIP bool `json:"disallowLoopbackChildIP,omitempty"` // since API v1.1.1
}
diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/api/openapi.yaml b/vendor/github.com/rootless-containers/rootlesskit/pkg/api/openapi.yaml
index 6a6550c33..dffc49680 100644
--- a/vendor/github.com/rootless-containers/rootlesskit/pkg/api/openapi.yaml
+++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/api/openapi.yaml
@@ -1,7 +1,7 @@
# When you made a change to this YAML, please validate with https://editor.swagger.io
openapi: 3.0.3
info:
- version: 1.1.0
+ version: 1.1.1
title: RootlessKit API
servers:
- url: 'http://rootlesskit/v1'
@@ -144,6 +144,13 @@ components:
items:
type: string
example: ["10.0.2.3"]
+ childIP:
+ type: string
+ description: "Child IP (v4)"
+ example: "10.0.2.100"
+ dynamicChildIP:
+ type: boolean
+ description: "Child IP may change"
PortDriverInfo:
required:
- driver
@@ -159,3 +166,6 @@ components:
example: ["tcp","udp"]
items:
$ref: '#/components/schemas/Proto'
+ disallowLoopbackChildIP:
+ type: boolean
+ description: "If this field is set to true, loopback IP such as 127.0.0.1 cannot be specified as a child IP"
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
index e7ce641e1..2895a8f07 100644
--- 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
@@ -59,8 +59,9 @@ type driver struct {
func (d *driver) Info(ctx context.Context) (*api.PortDriverInfo, error) {
info := &api.PortDriverInfo{
- Driver: "builtin",
- Protos: []string{"tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"},
+ Driver: "builtin",
+ Protos: []string{"tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"},
+ DisallowLoopbackChildIP: false,
}
return info, nil
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 2b9908ca4..52ed2101e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -327,6 +327,8 @@ github.com/fsouza/go-dockerclient
github.com/ghodss/yaml
# github.com/go-logr/logr v0.2.0
github.com/go-logr/logr
+# github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
+github.com/go-task/slim-sprig
# github.com/godbus/dbus/v5 v5.0.4
github.com/godbus/dbus/v5
# github.com/gogo/protobuf v1.3.2
@@ -425,10 +427,11 @@ github.com/nxadm/tail/ratelimiter
github.com/nxadm/tail/util
github.com/nxadm/tail/watch
github.com/nxadm/tail/winfile
-# github.com/onsi/ginkgo v1.15.2
+# github.com/onsi/ginkgo v1.16.0
github.com/onsi/ginkgo
github.com/onsi/ginkgo/config
github.com/onsi/ginkgo/extensions/table
+github.com/onsi/ginkgo/formatter
github.com/onsi/ginkgo/ginkgo
github.com/onsi/ginkgo/ginkgo/convert
github.com/onsi/ginkgo/ginkgo/interrupthandler
@@ -530,7 +533,7 @@ github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
# github.com/rivo/uniseg v0.2.0
github.com/rivo/uniseg
-# github.com/rootless-containers/rootlesskit v0.14.0
+# github.com/rootless-containers/rootlesskit v0.14.1
github.com/rootless-containers/rootlesskit/pkg/api
github.com/rootless-containers/rootlesskit/pkg/msgutil
github.com/rootless-containers/rootlesskit/pkg/port