diff options
-rw-r--r-- | contrib/podmanimage/stable/Dockerfile | 2 | ||||
-rw-r--r-- | contrib/podmanimage/testing/Dockerfile | 2 | ||||
-rw-r--r-- | contrib/podmanimage/upstream/Dockerfile | 2 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images.go | 35 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/kube.go | 95 | ||||
-rw-r--r-- | test/apiv2/10-images.at | 5 | ||||
-rwxr-xr-x | test/apiv2/test-apiv2 | 4 | ||||
-rw-r--r-- | test/e2e/play_kube_test.go | 100 | ||||
-rw-r--r-- | test/system/250-systemd.bats | 39 | ||||
-rw-r--r-- | test/system/255-auto-update.bats | 11 | ||||
-rw-r--r-- | test/system/270-socket-activation.bats | 17 | ||||
-rw-r--r-- | test/system/500-networking.bats | 2 | ||||
-rw-r--r-- | test/system/helpers.bash | 18 | ||||
-rw-r--r-- | test/system/helpers.systemd.bash | 30 | ||||
-rw-r--r-- | vendor/github.com/opencontainers/runc/libcontainer/user/user.go | 92 | ||||
-rw-r--r-- | vendor/modules.txt | 2 |
18 files changed, 349 insertions, 113 deletions
diff --git a/contrib/podmanimage/stable/Dockerfile b/contrib/podmanimage/stable/Dockerfile index 2f86dd4ae..a5d485e7f 100644 --- a/contrib/podmanimage/stable/Dockerfile +++ b/contrib/podmanimage/stable/Dockerfile @@ -11,7 +11,7 @@ FROM registry.fedoraproject.org/fedora:latest # Don't include container-selinux and remove # directories used by yum that are just taking # up space. -RUN dnf -y update; yum -y reinstall shadow-utils; \ +RUN dnf -y update; rpm --restore --quiet shadow-utils; \ yum -y install podman fuse-overlayfs --exclude container-selinux; \ rm -rf /var/cache /var/log/dnf* /var/log/yum.* diff --git a/contrib/podmanimage/testing/Dockerfile b/contrib/podmanimage/testing/Dockerfile index 63b31252f..1090fed00 100644 --- a/contrib/podmanimage/testing/Dockerfile +++ b/contrib/podmanimage/testing/Dockerfile @@ -13,7 +13,7 @@ FROM registry.fedoraproject.org/fedora:latest # Don't include container-selinux and remove # directories used by yum that are just taking # up space. -RUN yum -y update; yum -y reinstall shadow-utils; yum -y install podman fuse-overlayfs --exclude container-selinux --enablerepo updates-testing; rm -rf /var/cache /var/log/dnf* /var/log/yum.* +RUN yum -y update; rpm --restore --quiet shadow-utils; yum -y install podman fuse-overlayfs --exclude container-selinux --enablerepo updates-testing; rm -rf /var/cache /var/log/dnf* /var/log/yum.* RUN useradd podman; \ echo podman:10000:5000 > /etc/subuid; \ diff --git a/contrib/podmanimage/upstream/Dockerfile b/contrib/podmanimage/upstream/Dockerfile index 922eee748..01666e453 100644 --- a/contrib/podmanimage/upstream/Dockerfile +++ b/contrib/podmanimage/upstream/Dockerfile @@ -17,7 +17,7 @@ ENV GOPATH=/root/podman # to the container. # Finally remove the podman directory and a few other packages # that are needed for building but not running Podman -RUN yum -y update; yum -y reinstall shadow-utils; yum -y install --exclude container-selinux \ +RUN yum -y update; rpm --restore --quiet shadow-utils; yum -y install --exclude container-selinux \ --enablerepo=updates-testing \ btrfs-progs-devel \ containernetworking-cni \ @@ -46,7 +46,7 @@ require ( github.com/onsi/gomega v1.14.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 - github.com/opencontainers/runc v1.0.0 + github.com/opencontainers/runc v1.0.1 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.0 github.com/opencontainers/selinux v1.8.2 @@ -146,6 +146,7 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.1/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -729,8 +730,9 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm github.com/opencontainers/runc v1.0.0-rc91/go.mod h1:3Sm6Dt7OT8z88EbdQqqcRN2oCT54jbi72tT/HqgflT8= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= -github.com/opencontainers/runc v1.0.0 h1:QOhAQAYUlKeofuyeKdR6ITvOnXLPbEAjPMjz9wCUXcU= github.com/opencontainers/runc v1.0.0/go.mod h1:MU2S3KEB2ZExnhnAQYbwjdYV6HwKtDlNbA2Z2OeNDeA= +github.com/opencontainers/runc v1.0.1 h1:G18PGckGdAm3yVQRWDVQ1rLSLntiniKJ0cNRT2Tm5gs= +github.com/opencontainers/runc v1.0.1/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 7baa1145a..6f8fb21f0 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -266,41 +266,26 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { } defer auth.RemoveAuthfile(authfile) - platformSpecs := strings.Split(query.Platform, "/") // split query into its parts - - addOS := true // default assume true due to structure of if/else below - addArch := false - addVariant := false - - if len(platformSpecs) > 1 { // if we have two arguments then we have os and arch - addArch = true - if len(platformSpecs) > 2 { // if we have 3 arguments then we have os arch and variant - addVariant = true - } - } else if len(platformSpecs) == 0 { - addOS = false - } - pullOptions := &libimage.PullOptions{} pullOptions.AuthFilePath = authfile if authConf != nil { pullOptions.Username = authConf.Username pullOptions.Password = authConf.Password pullOptions.IdentityToken = authConf.IdentityToken - if addOS { // if the len is not 0 - pullOptions.OS = platformSpecs[0] - if addArch { - pullOptions.Architecture = platformSpecs[1] - } - if addVariant { - pullOptions.Variant = platformSpecs[2] - } - } } pullOptions.Writer = os.Stderr // allows for debugging on the server - progress := make(chan types.ProgressProperties) + // Handle the platform. + platformSpecs := strings.Split(query.Platform, "/") + pullOptions.OS = platformSpecs[0] // may be empty + if len(platformSpecs) > 1 { + pullOptions.Architecture = platformSpecs[1] + if len(platformSpecs) > 2 { + pullOptions.Variant = platformSpecs[2] + } + } + progress := make(chan types.ProgressProperties) pullOptions.Progress = progress pullResChan := make(chan pullResult) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index fb563f935..37cacdaa3 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -6,10 +6,12 @@ import ( "fmt" "net" "strings" + "time" "github.com/containers/common/libimage" "github.com/containers/common/pkg/parse" "github.com/containers/common/pkg/secrets" + "github.com/containers/image/v5/manifest" ann "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" @@ -129,6 +131,10 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } setupSecurityContext(s, opts.Container) + err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) + if err != nil { + return nil, errors.Wrap(err, "Failed to configure livenessProbe") + } // Since we prefix the container name with pod name to work-around the uniqueness requirement, // the seccomp profile should reference the actual container name from the YAML @@ -332,6 +338,95 @@ func parseMountPath(mountPath string, readOnly bool) (string, []string, error) { return dest, options, nil } +func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, restartPolicy string) error { + var err error + if containerYAML.LivenessProbe == nil { + return nil + } + emptyHandler := v1.Handler{} + if containerYAML.LivenessProbe.Handler != emptyHandler { + var commandString string + failureCmd := "exit 1" + probe := containerYAML.LivenessProbe + probeHandler := probe.Handler + + // append `exit 1` to `cmd` so healthcheck can be marked as `unhealthy`. + // append `kill 1` to `cmd` if appropriate restart policy is configured. + if restartPolicy == "always" || restartPolicy == "onfailure" { + // container will be restarted so we can kill init. + failureCmd = "kill 1" + } + + // configure healthcheck on the basis of Handler Actions. + if probeHandler.Exec != nil { + execString := strings.Join(probeHandler.Exec.Command, " ") + commandString = fmt.Sprintf("%s || %s", execString, failureCmd) + } else if probeHandler.HTTPGet != nil { + commandString = fmt.Sprintf("curl %s://%s:%d/%s || %s", probeHandler.HTTPGet.Scheme, probeHandler.HTTPGet.Host, probeHandler.HTTPGet.Port.IntValue(), probeHandler.HTTPGet.Path, failureCmd) + } else if probeHandler.TCPSocket != nil { + commandString = fmt.Sprintf("nc -z -v %s %d || %s", probeHandler.TCPSocket.Host, probeHandler.TCPSocket.Port.IntValue(), failureCmd) + } + s.HealthConfig, err = makeHealthCheck(commandString, probe.PeriodSeconds, probe.FailureThreshold, probe.TimeoutSeconds, probe.InitialDelaySeconds) + if err != nil { + return err + } + return nil + } + return nil +} + +func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32, startPeriod int32) (*manifest.Schema2HealthConfig, error) { + // Every healthcheck requires a command + if len(inCmd) == 0 { + return nil, errors.New("Must define a healthcheck command for all healthchecks") + } + + // first try to parse option value as JSON array of strings... + cmd := []string{} + + if inCmd == "none" { + cmd = []string{"NONE"} + } else { + err := json.Unmarshal([]byte(inCmd), &cmd) + if err != nil { + // ...otherwise pass it to "/bin/sh -c" inside the container + cmd = []string{"CMD-SHELL"} + cmd = append(cmd, strings.Split(inCmd, " ")...) + } + } + hc := manifest.Schema2HealthConfig{ + Test: cmd, + } + + if interval < 1 { + //kubernetes interval defaults to 10 sec and cannot be less than 1 + interval = 10 + } + hc.Interval = (time.Duration(interval) * time.Second) + if retries < 1 { + //kubernetes retries defaults to 3 + retries = 3 + } + hc.Retries = int(retries) + if timeout < 1 { + //kubernetes timeout defaults to 1 + timeout = 1 + } + timeoutDuration := (time.Duration(timeout) * time.Second) + if timeoutDuration < time.Duration(1) { + return nil, errors.New("healthcheck-timeout must be at least 1 second") + } + hc.Timeout = timeoutDuration + + startPeriodDuration := (time.Duration(startPeriod) * time.Second) + if startPeriodDuration < time.Duration(0) { + return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") + } + hc.StartPeriod = startPeriodDuration + + return &hc, nil +} + func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container) { if containerYAML.SecurityContext == nil { return diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index 9e464dbc7..195b11ff0 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -45,6 +45,11 @@ t POST "images/create?fromImage=alpine" 200 .error~null .status~".*Download comp t POST "images/create?fromImage=alpine&tag=latest" 200 +# 10977 - handle platform parameter correctly +t POST "images/create?fromImage=alpine&platform=linux/arm64" 200 +t GET "images/alpine/json" 200 \ + .Architecture=arm64 + # Make sure that new images are pulled old_iid=$(podman image inspect --format "{{.ID}}" docker.io/library/alpine:latest) podman rmi -f docker.io/library/alpine:latest diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index 9f6bf257f..26619ae03 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -442,10 +442,10 @@ function random_string() { function wait_for_port() { local host=$1 # Probably "localhost" local port=$2 # Numeric port - local timeout=${3:-5} # Optional; default to 5 seconds + local _timeout=${3:-5} # Optional; default to 5 seconds # Wait - while [ $timeout -gt 0 ]; do + while [ $_timeout -gt 0 ]; do { exec 3<> /dev/tcp/$host/$port; } &>/dev/null && return sleep 1 _timeout=$(( $_timeout - 1 )) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 42bb0cb64..5e303bf2f 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "text/template" + "time" "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" @@ -67,6 +68,75 @@ spec: shareProcessNamespace: true status: {} ` +var livenessProbePodYaml = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: liveness-probe + labels: + app: alpine +spec: + replicas: 1 + selector: + matchLabels: + app: alpine + template: + metadata: + labels: + app: alpine + spec: + containers: + - command: + - top + - -d + - "1.5" + name: alpine + image: quay.io/libpod/alpine:latest + ports: + - containerPort: 80 + livenessProbe: + exec: + command: + - echo + - hello + initialDelaySeconds: 5 + periodSeconds: 5 +` +var livenessProbeUnhealthyPodYaml = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: liveness-unhealthy-probe + labels: + app: alpine +spec: + replicas: 1 + selector: + matchLabels: + app: alpine + template: + metadata: + labels: + app: alpine + spec: + restartPolicy: Never + containers: + - command: + - top + - -d + - "1.5" + name: alpine + image: quay.io/libpod/alpine:latest + ports: + - containerPort: 80 + livenessProbe: + exec: + command: + - cat + - /randomfile + initialDelaySeconds: 0 + periodSeconds: 1 +` var selinuxLabelPodYaml = ` apiVersion: v1 @@ -1061,6 +1131,36 @@ var _ = Describe("Podman play kube", func() { Expect(sharednamespaces).To(ContainSubstring("pid")) }) + It("podman play kube support container liveness probe", func() { + err := writeYaml(livenessProbePodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", "liveness-probe-pod-0-alpine", "--format", "'{{ .Config.Healthcheck }}'"}) + inspect.WaitWithDefaultTimeout() + healthcheckcmd := inspect.OutputToString() + // check if CMD-SHELL based equivalent health check is added to container + Expect(healthcheckcmd).To(ContainSubstring("CMD-SHELL")) + }) + + It("podman play kube liveness probe should fail", func() { + err := writeYaml(livenessProbeUnhealthyPodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + time.Sleep(2 * time.Second) + hc := podmanTest.Podman([]string{"healthcheck", "run", "liveness-unhealthy-probe-pod-0-alpine"}) + hc.WaitWithDefaultTimeout() + hcoutput := hc.OutputToString() + Expect(hcoutput).To(ContainSubstring("unhealthy")) + }) + It("podman play kube fail with nonexistent authfile", func() { err := generateKubeYaml("pod", getPod(), kubeYaml) Expect(err).To(BeNil()) diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index aafe385c8..ee951ff21 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -4,17 +4,10 @@ # load helpers +load helpers.systemd SERVICE_NAME="podman_test_$(random_string)" -SYSTEMCTL="systemctl" -UNIT_DIR="/usr/lib/systemd/system" -if is_rootless; then - UNIT_DIR="$HOME/.config/systemd/user" - mkdir -p $UNIT_DIR - - SYSTEMCTL="$SYSTEMCTL --user" -fi UNIT_FILE="$UNIT_DIR/$SERVICE_NAME.service" function setup() { @@ -24,38 +17,28 @@ function setup() { } function teardown() { - run '?' $SYSTEMCTL stop "$SERVICE_NAME" + run '?' systemctl stop "$SERVICE_NAME" rm -f "$UNIT_FILE" - $SYSTEMCTL daemon-reload + systemctl daemon-reload run_podman rmi -a basic_teardown } -# Helper to setup xdg runtime for rootless -function xdg_rootless() { - # podman initializes this if unset, but systemctl doesn't - if is_rootless; then - if [ -z "$XDG_RUNTIME_DIR" ]; then - export XDG_RUNTIME_DIR=/run/user/$(id -u) - fi - fi -} - # Helper to start a systemd service running a container function service_setup() { run_podman generate systemd --new $cname echo "$output" > "$UNIT_FILE" run_podman rm $cname - $SYSTEMCTL daemon-reload + systemctl daemon-reload - run $SYSTEMCTL start "$SERVICE_NAME" + run systemctl start "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Error starting systemd unit $SERVICE_NAME, output: $output" fi - run $SYSTEMCTL status "$SERVICE_NAME" + run systemctl status "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Non-zero status of systemd unit $SERVICE_NAME, output: $output" fi @@ -63,20 +46,18 @@ function service_setup() { # Helper to stop a systemd service running a container function service_cleanup() { - run $SYSTEMCTL stop "$SERVICE_NAME" + run systemctl stop "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Error stopping systemd unit $SERVICE_NAME, output: $output" fi rm -f "$UNIT_FILE" - $SYSTEMCTL daemon-reload + systemctl daemon-reload } # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman generate - systemd - basic" { - xdg_rootless - cname=$(random_string) # See #7407 for --pull=always. run_podman create --pull=always --name $cname --label "io.containers.autoupdate=registry" $IMAGE top @@ -100,8 +81,6 @@ function service_cleanup() { } @test "podman autoupdate local" { - xdg_rootless - cname=$(random_string) run_podman create --name $cname --label "io.containers.autoupdate=local" $IMAGE top @@ -128,8 +107,6 @@ function service_cleanup() { # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman generate systemd - envar" { - xdg_rootless - cname=$(random_string) FOO=value BAR=%s run_podman create --name $cname --env FOO -e BAR --env MYVAR=myval \ $IMAGE sh -c 'printenv && sleep 100' diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index a73ed94e8..25eaba45b 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -4,14 +4,12 @@ # load helpers +load helpers.systemd -UNIT_DIR="/usr/lib/systemd/system" SNAME_FILE=$BATS_TMPDIR/services function setup() { skip_if_remote "systemd tests are meaningless over remote" - skip_if_rootless - basic_setup } @@ -29,7 +27,7 @@ function teardown() { rm -f $SNAME_FILE run_podman ? rmi quay.io/libpod/alpine:latest - run_podman ? rmi quay.io/libpod/alpine_nginx:latest + run_podman ? rmi quay.io/libpod/busybox:latest run_podman ? rmi quay.io/libpod/localtest:latest basic_teardown } @@ -58,8 +56,7 @@ function generate_service() { fi run_podman run -d --name $cname $label $target_img top -d 120 - run_podman generate systemd --new $cname - echo "$output" > "$UNIT_DIR/container-$cname.service" + (cd $UNIT_DIR; run_podman generate systemd --new --files --name $cname) echo "container-$cname" >> $SNAME_FILE run_podman rm -f $cname @@ -185,7 +182,7 @@ function _confirm_update() { do local img_base="alpine" if [[ $auto_update == "registry" ]]; then - img_base="alpine_nginx" + img_base="busybox" elif [[ $auto_update == "local" ]]; then img_base="localtest" fi diff --git a/test/system/270-socket-activation.bats b/test/system/270-socket-activation.bats index 25206c6a7..031ba161b 100644 --- a/test/system/270-socket-activation.bats +++ b/test/system/270-socket-activation.bats @@ -4,21 +4,12 @@ # load helpers +load helpers.systemd 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 @@ -66,13 +57,13 @@ EOF rm -f $pause_pid fi fi - $SYSTEMCTL start "$SERVICE_NAME.socket" + systemctl start "$SERVICE_NAME.socket" } function teardown() { - $SYSTEMCTL stop "$SERVICE_NAME.socket" + systemctl stop "$SERVICE_NAME.socket" rm -f "$SERVICE_FILE" "$SOCKET_FILE" - $SYSTEMCTL daemon-reload + systemctl daemon-reload basic_teardown } diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 4feb57807..419d325b0 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -139,6 +139,8 @@ load helpers $IMAGE nc -l -n -v -p $myport cid="$output" + wait_for_port 127.0.0.1 $myport + # emit random string, and check it teststring=$(random_string 30) echo "$teststring" | nc 127.0.0.1 $myport diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 1859a2168..02fd7252c 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -278,6 +278,24 @@ function wait_for_ready { wait_for_output 'READY' "$@" } +################### +# wait_for_port # Returns once port is available on host +################### +function wait_for_port() { + local host=$1 # Probably "localhost" + local port=$2 # Numeric port + local _timeout=${3:-5} # Optional; default to 5 seconds + + # Wait + while [ $_timeout -gt 0 ]; do + { exec 3<> /dev/tcp/$host/$port; } &>/dev/null && return + sleep 1 + _timeout=$(( $_timeout - 1 )) + done + + die "Timed out waiting for $host:$port" +} + # END podman helpers ############################################################################### # BEGIN miscellaneous tools diff --git a/test/system/helpers.systemd.bash b/test/system/helpers.systemd.bash new file mode 100644 index 000000000..4bde912a4 --- /dev/null +++ b/test/system/helpers.systemd.bash @@ -0,0 +1,30 @@ +# -*- bash -*- +# +# BATS helpers for systemd-related functionality +# + +# podman initializes this if unset, but systemctl doesn't +if [ -z "$XDG_RUNTIME_DIR" ]; then + if is_rootless; then + export XDG_RUNTIME_DIR=/run/user/$(id -u) + fi +fi + +# For tests which write systemd unit files +UNIT_DIR="/run/systemd/system" +_DASHUSER= +if is_rootless; then + UNIT_DIR="${XDG_RUNTIME_DIR}/systemd/user" + # Why isn't systemd smart enough to figure this out on its own? + _DASHUSER="--user" +fi + +mkdir -p $UNIT_DIR + +systemctl() { + command systemctl $_DASHUSER "$@" +} + +journalctl() { + command journalctl $_DASHUSER "$@" +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go index d2c16f7fd..cc7a106be 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go @@ -2,6 +2,7 @@ package user import ( "bufio" + "bytes" "errors" "fmt" "io" @@ -55,11 +56,11 @@ type IDMap struct { Count int64 } -func parseLine(line string, v ...interface{}) { - parseParts(strings.Split(line, ":"), v...) +func parseLine(line []byte, v ...interface{}) { + parseParts(bytes.Split(line, []byte(":")), v...) } -func parseParts(parts []string, v ...interface{}) { +func parseParts(parts [][]byte, v ...interface{}) { if len(parts) == 0 { return } @@ -75,16 +76,16 @@ func parseParts(parts []string, v ...interface{}) { // This is legit. switch e := v[i].(type) { case *string: - *e = p + *e = string(p) case *int: // "numbers", with conversion errors ignored because of some misbehaving configuration files. - *e, _ = strconv.Atoi(p) + *e, _ = strconv.Atoi(string(p)) case *int64: - *e, _ = strconv.ParseInt(p, 10, 64) + *e, _ = strconv.ParseInt(string(p), 10, 64) case *[]string: // Comma-separated lists. - if p != "" { - *e = strings.Split(p, ",") + if len(p) != 0 { + *e = strings.Split(string(p), ",") } else { *e = []string{} } @@ -128,8 +129,8 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { ) for s.Scan() { - line := strings.TrimSpace(s.Text()) - if line == "" { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { continue } @@ -179,15 +180,53 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { if r == nil { return nil, fmt.Errorf("nil source for group-formatted data") } + rd := bufio.NewReader(r) + out := []Group{} - var ( - s = bufio.NewScanner(r) - out = []Group{} - ) + // Read the file line-by-line. + for { + var ( + isPrefix bool + wholeLine []byte + err error + ) - for s.Scan() { - text := s.Text() - if text == "" { + // Read the next line. We do so in chunks (as much as reader's + // buffer is able to keep), check if we read enough columns + // already on each step and store final result in wholeLine. + for { + var line []byte + line, isPrefix, err = rd.ReadLine() + + if err != nil { + // We should return no error if EOF is reached + // without a match. + if err == io.EOF { //nolint:errorlint // comparison with io.EOF is legit, https://github.com/polyfloyd/go-errorlint/pull/12 + err = nil + } + return out, err + } + + // Simple common case: line is short enough to fit in a + // single reader's buffer. + if !isPrefix && len(wholeLine) == 0 { + wholeLine = line + break + } + + wholeLine = append(wholeLine, line...) + + // Check if we read the whole line already. + if !isPrefix { + break + } + } + + // There's no spec for /etc/passwd or /etc/group, but we try to follow + // the same rules as the glibc parser, which allows comments and blank + // space at the beginning of a line. + wholeLine = bytes.TrimSpace(wholeLine) + if len(wholeLine) == 0 || wholeLine[0] == '#' { continue } @@ -197,17 +236,12 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { // root:x:0:root // adm:x:4:root,adm,daemon p := Group{} - parseLine(text, &p.Name, &p.Pass, &p.Gid, &p.List) + parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List) if filter == nil || filter(p) { out = append(out, p) } } - if err := s.Err(); err != nil { - return nil, err - } - - return out, nil } type ExecUser struct { @@ -278,7 +312,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) ( // Allow for userArg to have either "user" syntax, or optionally "user:group" syntax var userArg, groupArg string - parseLine(userSpec, &userArg, &groupArg) + parseLine([]byte(userSpec), &userArg, &groupArg) // Convert userArg and groupArg to be numeric, so we don't have to execute // Atoi *twice* for each iteration over lines. @@ -496,8 +530,8 @@ func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { ) for s.Scan() { - line := strings.TrimSpace(s.Text()) - if line == "" { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { continue } @@ -549,14 +583,14 @@ func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { ) for s.Scan() { - line := strings.TrimSpace(s.Text()) - if line == "" { + line := bytes.TrimSpace(s.Bytes()) + if len(line) == 0 { continue } // see: man 7 user_namespaces p := IDMap{} - parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count) + parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count) if filter == nil || filter(p) { out = append(out, p) diff --git a/vendor/modules.txt b/vendor/modules.txt index cdda0641f..337d85442 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -503,7 +503,7 @@ github.com/opencontainers/go-digest # github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 -# github.com/opencontainers/runc v1.0.0 +# github.com/opencontainers/runc v1.0.1 github.com/opencontainers/runc/libcontainer/apparmor github.com/opencontainers/runc/libcontainer/cgroups github.com/opencontainers/runc/libcontainer/configs |