summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml55
-rw-r--r--.github/workflows/check_cirrus_cron.yml3
-rw-r--r--.github/workflows/issue-labeler.yml6
-rw-r--r--.github/workflows/multi-arch-build.yaml212
-rw-r--r--.github/workflows/pr-title.yml5
-rw-r--r--cmd/podman/common/create.go3
-rw-r--r--cmd/podman/common/create_opts.go2
-rw-r--r--cmd/podman/containers/create.go7
-rw-r--r--cmd/podman/containers/logs.go3
-rw-r--r--cmd/podman/main.go64
-rw-r--r--cmd/podman/pods/logs.go2
-rwxr-xr-xcontrib/cirrus/cirrus_yaml_test.py3
-rw-r--r--contrib/hello/Containerfile (renamed from contrib/helloimage/Containerfile)0
-rw-r--r--contrib/hello/README.md (renamed from contrib/helloimage/README.md)0
-rw-r--r--contrib/hello/podman_hello_world.c (renamed from contrib/helloimage/podman_hello_world.c)0
-rw-r--r--contrib/systemd/system/podman.service.in1
-rw-r--r--docs/source/markdown/podman-logs.1.md4
-rw-r--r--docs/source/markdown/podman-pod-logs.1.md4
-rwxr-xr-xhack/make-and-check-size2
-rw-r--r--libpod/container_config.go23
-rw-r--r--libpod/container_inspect.go6
-rw-r--r--libpod/container_internal.go4
-rw-r--r--libpod/container_log.go14
-rw-r--r--libpod/container_log_linux.go3
-rw-r--r--libpod/container_log_unsupported.go2
-rw-r--r--libpod/define/version.go2
-rw-r--r--libpod/kube.go2
-rw-r--r--libpod/logs/log.go33
-rw-r--r--libpod/networking_linux.go11
-rw-r--r--libpod/networking_slirp4netns.go107
-rw-r--r--libpod/oci_conmon_linux.go2
-rw-r--r--libpod/pod_api.go4
-rw-r--r--pkg/bindings/images/build.go9
-rw-r--r--pkg/bindings/system/system.go1
-rw-r--r--pkg/domain/entities/containers.go2
-rw-r--r--pkg/domain/entities/pods.go5
-rw-r--r--pkg/domain/infra/abi/containers.go3
-rw-r--r--pkg/domain/infra/abi/play.go11
-rw-r--r--pkg/machine/qemu/config.go19
-rw-r--r--pkg/machine/qemu/machine.go269
-rw-r--r--pkg/machine/qemu/options_darwin_arm64.go6
-rw-r--r--pkg/machine/wsl/machine.go30
-rw-r--r--pkg/specgen/container_validate.go4
-rw-r--r--pkg/specgen/generate/container.go43
-rw-r--r--pkg/specgen/generate/container_create.go33
-rw-r--r--pkg/specgen/generate/oci.go4
-rw-r--r--pkg/specgenutil/specgen.go4
-rw-r--r--pkg/specgenutil/volumes.go12
-rw-r--r--test/e2e/build_test.go66
-rw-r--r--test/e2e/common_test.go15
-rw-r--r--test/e2e/libpod_suite_remote_test.go73
-rw-r--r--test/e2e/logs_test.go23
-rw-r--r--test/e2e/play_kube_test.go26
-rw-r--r--test/e2e/pod_create_test.go4
-rw-r--r--test/e2e/run_test.go12
-rw-r--r--test/e2e/run_volume_test.go7
-rw-r--r--test/e2e/unshare_test.go14
-rw-r--r--test/e2e/version_test.go8
-rw-r--r--test/system/030-run.bats6
-rw-r--r--test/system/500-networking.bats21
-rw-r--r--test/upgrade/test-upgrade.bats51
-rw-r--r--test/utils/utils.go4
62 files changed, 828 insertions, 546 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index ae5463427..772843dd7 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -38,7 +38,7 @@ env:
UBUNTU_NAME: "ubuntu-2110"
# Google-cloud VM Images
- IMAGE_SUFFIX: "c5814666029957120"
+ IMAGE_SUFFIX: "c6464310661611520"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
@@ -722,13 +722,11 @@ upgrade_test_task:
- local_system_test
matrix:
- env:
- PODMAN_UPGRADE_FROM: v1.9.0
- - env:
- PODMAN_UPGRADE_FROM: v2.0.6
- - env:
PODMAN_UPGRADE_FROM: v2.1.1
- env:
PODMAN_UPGRADE_FROM: v3.1.2
+ - env:
+ PODMAN_UPGRADE_FROM: v3.4.4
gce_instance: *standardvm
env:
TEST_FLAVOR: upgrade_test
@@ -743,6 +741,51 @@ upgrade_test_task:
always: *logs_artifacts
+image_build_task: &image-build
+ name: "Build multi-arch $CTXDIR"
+ alias: image_build
+ # Some of these container images take > 1h to build, limit
+ # this task to a specific Cirrus-Cron entry with this name.
+ only_if: $CIRRUS_CRON == 'multiarch'
+ depends_on:
+ - ext_svc_check
+ timeout_in: 120m # emulation is sssllllooooowwww
+ gce_instance:
+ <<: *standardvm
+ image_name: build-push-${IMAGE_SUFFIX}
+ # More muscle required for parallel multi-arch build
+ type: "n2-standard-4"
+ env:
+ PODMAN_USERNAME: ENCRYPTED[b9f0f2550029dd2196e086d9dd6c2d1fec7e328630b15990d9bb610f9fcccb5baab8b64a8c3e72b0c1d0f5917cf65aa1]
+ PODMAN_PASSWORD: ENCRYPTED[e3444f6072853f0c8db7f964ead5e2204116af485469fa0de367f26b9316b460fd842a9882f552b9e9a83bbaf650d8b4]
+ CONTAINERS_USERNAME: ENCRYPTED[54a372d5f22f424173c114c6fb25c3214956cad323d5b285c7393a71041884ce96471d0ff733774e5dab9fa5a3c8795c]
+ CONTAINERS_PASSWORD: ENCRYPTED[4ecc3fb534935095a99fb1f2e320ac6bc87f3e7e186746e41cbcc4b5f5379a014b9fc8cc90e1f3d5abdbaf31580a4ab9]
+ matrix:
+ - env:
+ CTXDIR: contrib/podmanimage/upstream
+ - env:
+ CTXDIR: contrib/podmanimage/testing
+ - env:
+ CTXDIR: contrib/podmanimage/stable
+ - env:
+ CTXDIR: contrib/hello
+ script:
+ - set -a; source /etc/automation_environment; set +a
+ - main.sh $CIRRUS_REPO_CLONE_URL $CTXDIR
+
+
+test_image_build_task:
+ <<: *image-build
+ # Allow this to run inside a PR
+ only_if: $CI == $CI
+ # This takes a LONG time, only run when requested. N/B: Any task
+ # made to depend on this one will block FOREVER unless triggered.
+ trigger_type: manual
+ # Overwrite all 'env', don't push anything, just do the build.
+ env:
+ DRYRUN: 1
+
+
# This task is critical. It updates the "last-used by" timestamp stored
# in metadata for all VM images. This mechanism functions in tandem with
# an out-of-band pruning operation to remove disused VM images.
@@ -759,6 +802,7 @@ meta_task:
${FEDORA_CACHE_IMAGE_NAME}
${PRIOR_FEDORA_CACHE_IMAGE_NAME}
${UBUNTU_CACHE_IMAGE_NAME}
+ build-push-${IMAGE_SUFFIX}
BUILDID: "${CIRRUS_BUILD_ID}"
REPOREF: "${CIRRUS_REPO_NAME}"
GCPJSON: ENCRYPTED[3a198350077849c8df14b723c0f4c9fece9ebe6408d35982e7adf2105a33f8e0e166ed3ed614875a0887e1af2b8775f4]
@@ -801,6 +845,7 @@ success_task:
- rootless_gitlab_test
- upgrade_test
- buildah_bud_test
+ - image_build
- meta
container: *smallcontainer
env:
diff --git a/.github/workflows/check_cirrus_cron.yml b/.github/workflows/check_cirrus_cron.yml
index 5704b0b9d..bca77e4d9 100644
--- a/.github/workflows/check_cirrus_cron.yml
+++ b/.github/workflows/check_cirrus_cron.yml
@@ -25,6 +25,9 @@ env:
# (must be in $GITHUB_WORKSPACE/artifacts/)
NAME_ID_FILEPATH: './artifacts/name_id.txt'
+permissions:
+ contents: read
+
jobs:
cron_failures:
runs-on: ubuntu-latest
diff --git a/.github/workflows/issue-labeler.yml b/.github/workflows/issue-labeler.yml
index 18fff7f7c..afebc7fca 100644
--- a/.github/workflows/issue-labeler.yml
+++ b/.github/workflows/issue-labeler.yml
@@ -3,8 +3,14 @@ on:
issues:
types: [opened, edited]
+permissions:
+ contents: read
+
jobs:
triage:
+ permissions:
+ contents: read # for github/issue-labeler to get repo contents
+ issues: write # for github/issue-labeler to create or remove labels
runs-on: ubuntu-latest
steps:
- uses: github/issue-labeler@3ae0e4623c1fda729347ae0d8f1c2e52302ef4c6 # v2.0
diff --git a/.github/workflows/multi-arch-build.yaml b/.github/workflows/multi-arch-build.yaml
deleted file mode 100644
index 1dc485d71..000000000
--- a/.github/workflows/multi-arch-build.yaml
+++ /dev/null
@@ -1,212 +0,0 @@
----
-
-# Please see contrib/<reponame>image/README.md for details on the intentions
-# of this workflow.
-#
-# BIG FAT WARNING: This workflow is duplicated across containers/skopeo,
-# containers/buildah, and containers/podman. ANY AND
-# ALL CHANGES MADE HERE MUST BE MANUALLY DUPLICATED
-# TO THE OTHER REPOS.
-
-name: build multi-arch images
-
-on:
- # Upstream tends to be very active, with many merges per day.
- # Only run this daily via cron schedule, or manually, not by branch push.
- schedule:
- - cron: '0 8 * * *'
- # allows to run this workflow manually from the Actions tab
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- multi:
- name: multi-arch image build
- env:
- REPONAME: podman # No easy way to parse this out of $GITHUB_REPOSITORY
- # Server/namespace value used to format FQIN
- REPONAME_QUAY_REGISTRY: quay.io/podman
- CONTAINERS_QUAY_REGISTRY: quay.io/containers
- # list of architectures for build
- PLATFORMS: linux/amd64,linux/s390x,linux/ppc64le,linux/arm64
- # Command to execute in container to obtain project version number
- VERSION_CMD: "podman --version"
-
- # build several images (upstream, testing, stable) in parallel
- strategy:
- # By default, failure of one matrix item cancels all others
- fail-fast: false
- matrix:
- # Builds are located under contrib/<reponame>image/<source> directory
- source:
- - upstream
- - testing
- - stable
- runs-on: ubuntu-latest
- # internal registry caches build for inspection before push
- services:
- registry:
- image: quay.io/libpod/registry:2
- ports:
- - 5000:5000
- steps:
- - name: Checkout
- uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5 # v2
-
- - name: Set up QEMU
- uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # v1
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # v1
- with:
- driver-opts: network=host
- install: true
-
- - name: Build and locally push image
- uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a # v2
- with:
- context: contrib/${{ env.REPONAME }}image/${{ matrix.source }}
- file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile
- platforms: ${{ env.PLATFORMS }}
- push: true
- tags: localhost:5000/${{ env.REPONAME }}/${{ matrix.source }}
-
- # Simple verification that stable images work, and
- # also grab version number use in forming the FQIN.
- - name: amd64 container sniff test
- if: matrix.source == 'stable'
- id: sniff_test
- run: |
- podman pull --tls-verify=false \
- localhost:5000/$REPONAME/${{ matrix.source }}
- VERSION_OUTPUT=$(podman run \
- localhost:5000/$REPONAME/${{ matrix.source }} \
- $VERSION_CMD)
- echo "$VERSION_OUTPUT"
- VERSION=$(awk -r -e "/^${REPONAME} version /"'{print $3}' <<<"$VERSION_OUTPUT")
- test -n "$VERSION"
- echo "::set-output name=version::$VERSION"
-
- - name: Generate image FQIN(s) to push
- id: reponame_reg
- run: |
- if [[ "${{ matrix.source }}" == 'stable' ]]; then
- # The command version in image just built
- VERSION='v${{ steps.sniff_test.outputs.version }}'
- # workaround vim syntax-highlight bug: '
- # Push both new|updated version-tag and latest-tag FQINs
- FQIN="$REPONAME_QUAY_REGISTRY/stable:$VERSION,$REPONAME_QUAY_REGISTRY/stable:latest"
- elif [[ "${{ matrix.source }}" == 'testing' ]]; then
- # Assume some contents changed, always push latest testing.
- FQIN="$REPONAME_QUAY_REGISTRY/testing:latest"
- elif [[ "${{ matrix.source }}" == 'upstream' ]]; then
- # Assume some contents changed, always push latest upstream.
- FQIN="$REPONAME_QUAY_REGISTRY/upstream:latest"
- else
- echo "::error::Unknown matrix item '${{ matrix.source }}'"
- exit 1
- fi
- echo "::warning::Pushing $FQIN"
- echo "::set-output name=fqin::${FQIN}"
- echo '::set-output name=push::true'
-
- # This is substantially similar to the above logic,
- # but only handles $CONTAINERS_QUAY_REGISTRY for
- # the stable "latest" and named-version tagged images.
- - name: Generate containers reg. image FQIN(s)
- if: matrix.source == 'stable'
- id: containers_reg
- run: |
- VERSION='v${{ steps.sniff_test.outputs.version }}'
- # workaround vim syntax-highlight bug: '
- # Push both new|updated version-tag and latest-tag FQINs
- FQIN="$CONTAINERS_QUAY_REGISTRY/$REPONAME:$VERSION,$CONTAINERS_QUAY_REGISTRY/$REPONAME:latest"
- echo "::warning::Pushing $FQIN"
- echo "::set-output name=fqin::${FQIN}"
- echo '::set-output name=push::true'
-
- - name: Define LABELS multi-line env. var. value
- run: |
- # This is a really hacky/strange workflow idiom, required
- # for setting multi-line $LABELS value for consumption in
- # a future step. There is literally no cleaner way to do this :<
- # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#multiline-strings
- function set_labels() {
- echo 'LABELS<<DELIMITER' >> "$GITHUB_ENV"
- for line; do
- echo "$line" | tee -a "$GITHUB_ENV"
- done
- echo "DELIMITER" >> "$GITHUB_ENV"
- }
-
- declare -a lines
- lines=(\
- "org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}.git"
- "org.opencontainers.image.revision=${GITHUB_SHA}"
- "org.opencontainers.image.created=$(date -u --iso-8601=seconds)"
- )
-
- # Only the 'stable' matrix source obtains $VERSION
- if [[ "${{ matrix.source }}" == "stable" ]]; then
- lines+=(\
- "org.opencontainers.image.version=${{ steps.sniff_test.outputs.version }}"
- )
- fi
-
- set_labels "${lines[@]}"
-
- # Separate steps to login and push for $REPONAME_QUAY_REGISTRY and
- # $CONTAINERS_QUAY_REGISTRY are required, because 2 sets of credentials
- # are used and namespaced within the registry. At the same time, reuse
- # of non-shell steps is not supported by Github Actions nor are YAML
- # anchors/aliases, nor composite actions.
-
- # Push to $REPONAME_QUAY_REGISTRY for stable, testing. and upstream
- - name: Login to ${{ env.REPONAME_QUAY_REGISTRY }}
- uses: docker/login-action@dd4fa0671be5250ee6f50aedf4cb05514abda2c7 # v1
- if: steps.reponame_reg.outputs.push == 'true'
- with:
- registry: ${{ env.REPONAME_QUAY_REGISTRY }}
- # N/B: Secrets are not passed to workflows that are triggered
- # by a pull request from a fork
- username: ${{ secrets.REPONAME_QUAY_USERNAME }}
- password: ${{ secrets.REPONAME_QUAY_PASSWORD }}
-
- - name: Push images to ${{ steps.reponame_reg.outputs.fqin }}
- uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a # v2
- if: steps.reponame_reg.outputs.push == 'true'
- with:
- cache-from: type=registry,ref=localhost:5000/${{ env.REPONAME }}/${{ matrix.source }}
- cache-to: type=inline
- context: contrib/${{ env.REPONAME }}image/${{ matrix.source }}
- file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile
- platforms: ${{ env.PLATFORMS }}
- push: true
- tags: ${{ steps.reponame_reg.outputs.fqin }}
- labels: |
- ${{ env.LABELS }}
-
- # Push to $CONTAINERS_QUAY_REGISTRY only stable
- - name: Login to ${{ env.CONTAINERS_QUAY_REGISTRY }}
- if: steps.containers_reg.outputs.push == 'true'
- uses: docker/login-action@dd4fa0671be5250ee6f50aedf4cb05514abda2c7 # v1
- with:
- registry: ${{ env.CONTAINERS_QUAY_REGISTRY}}
- username: ${{ secrets.CONTAINERS_QUAY_USERNAME }}
- password: ${{ secrets.CONTAINERS_QUAY_PASSWORD }}
-
- - name: Push images to ${{ steps.containers_reg.outputs.fqin }}
- if: steps.containers_reg.outputs.push == 'true'
- uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a # v2
- with:
- cache-from: type=registry,ref=localhost:5000/${{ env.REPONAME }}/${{ matrix.source }}
- cache-to: type=inline
- context: contrib/${{ env.REPONAME }}image/${{ matrix.source }}
- file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile
- platforms: ${{ env.PLATFORMS }}
- push: true
- tags: ${{ steps.containers_reg.outputs.fqin }}
- labels: |
- ${{ env.LABELS }}
diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml
index 2b57392ce..66599a86d 100644
--- a/.github/workflows/pr-title.yml
+++ b/.github/workflows/pr-title.yml
@@ -7,8 +7,13 @@ on:
branches:
- "!master" # causes errors; reason unknown
+permissions:
+ contents: read
+
jobs:
update_pr:
+ permissions:
+ pull-requests: write # for tzkhan/pr-update-action to update PRs
runs-on: ubuntu-latest
steps:
- uses: tzkhan/pr-update-action@bbd4c9395df8a9c4ef075b8b7fe29f2ca76cdca9 # v2
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index afaa1942e..8a3f02036 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -348,8 +348,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
oomScoreAdjFlagName := "oom-score-adj"
- createFlags.IntVar(
- &cf.OOMScoreAdj,
+ createFlags.Int(
oomScoreAdjFlagName, 0,
"Tune the host's OOM preferences (-1000 to 1000)",
)
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index e5a2a0da3..ad6b3870a 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -286,7 +286,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
LogDriver: cc.HostConfig.LogConfig.Type,
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
Name: cc.Name,
- OOMScoreAdj: cc.HostConfig.OomScoreAdj,
+ OOMScoreAdj: &cc.HostConfig.OomScoreAdj,
Arch: "",
OS: "",
Variant: "",
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index de1a41e25..bbc449a1e 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -238,6 +238,13 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
vals.GroupAdd = groups
}
+ if c.Flags().Changed("oom-score-adj") {
+ val, err := c.Flags().GetInt("oom-score-adj")
+ if err != nil {
+ return vals, err
+ }
+ vals.OOMScoreAdj = &val
+ }
if c.Flags().Changed("pids-limit") {
val := c.Flag("pids-limit").Value.String()
// Convert -1 to 0, so that -1 maps to unlimited pids limit
diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 8b08a6313..374bf6b1c 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -64,6 +64,7 @@ var (
ValidArgsFunction: logsCommand.ValidArgsFunction,
Example: `podman container logs ctrID
podman container logs --names ctrID1 ctrID2
+ podman container logs --color --names ctrID1 ctrID2
podman container logs --tail 2 mywebserver
podman container logs --follow=true --since 10m ctrID
podman container logs mywebserver mydbserver`,
@@ -112,7 +113,9 @@ func logsFlags(cmd *cobra.Command) {
_ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone)
flags.BoolVarP(&logsOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log")
+ flags.BoolVarP(&logsOptions.Colors, "color", "", false, "Output the containers with different colors in the log.")
flags.BoolVarP(&logsOptions.Names, "names", "n", false, "Output the container name in the log")
+
flags.SetInterspersed(false)
_ = flags.MarkHidden("details")
}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 4f8131653..8f580601e 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -3,7 +3,6 @@ package main
import (
"fmt"
"os"
- "strings"
_ "github.com/containers/podman/v4/cmd/podman/completion"
_ "github.com/containers/podman/v4/cmd/podman/containers"
@@ -20,6 +19,7 @@ import (
_ "github.com/containers/podman/v4/cmd/podman/system"
_ "github.com/containers/podman/v4/cmd/podman/system/connection"
_ "github.com/containers/podman/v4/cmd/podman/volumes"
+ "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/terminal"
"github.com/containers/storage/pkg/reexec"
@@ -44,7 +44,29 @@ func parseCommands() *cobra.Command {
cfg := registry.PodmanConfig()
for _, c := range registry.Commands {
if supported, found := c.Command.Annotations[registry.EngineMode]; found {
- if !strings.Contains(cfg.EngineMode.String(), supported) {
+ if cfg.EngineMode.String() != supported {
+ var client string
+ switch cfg.EngineMode {
+ case entities.TunnelMode:
+ client = "remote"
+ case entities.ABIMode:
+ client = "local"
+ }
+
+ // add error message to the command so the user knows that this command is not supported with local/remote
+ c.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return fmt.Errorf("cannot use command %q with the %s podman client", cmd.CommandPath(), client)
+ }
+ // turn of flag parsing to make we do not get flag errors
+ c.Command.DisableFlagParsing = true
+
+ // mark command as hidden so it is not shown in --help
+ c.Command.Hidden = true
+
+ // overwrite persistent pre/post function to skip setup
+ c.Command.PersistentPostRunE = noop
+ c.Command.PersistentPreRunE = noop
+ addCommand(c)
continue
}
}
@@ -65,22 +87,9 @@ func parseCommands() *cobra.Command {
}
}
}
-
- parent := rootCmd
- if c.Parent != nil {
- parent = c.Parent
- }
- parent.AddCommand(c.Command)
-
- c.Command.SetFlagErrorFunc(flagErrorFuncfunc)
-
- // - templates need to be set here, as PersistentPreRunE() is
- // not called when --help is used.
- // - rootCmd uses cobra default template not ours
- c.Command.SetHelpTemplate(helpTemplate)
- c.Command.SetUsageTemplate(usageTemplate)
- c.Command.DisableFlagsInUseLine = true
+ addCommand(c)
}
+
if err := terminal.SetConsole(); err != nil {
logrus.Error(err)
os.Exit(1)
@@ -94,3 +103,24 @@ func flagErrorFuncfunc(c *cobra.Command, e error) error {
e = fmt.Errorf("%w\nSee '%s --help'", e, c.CommandPath())
return e
}
+
+func addCommand(c registry.CliCommand) {
+ parent := rootCmd
+ if c.Parent != nil {
+ parent = c.Parent
+ }
+ parent.AddCommand(c.Command)
+
+ c.Command.SetFlagErrorFunc(flagErrorFuncfunc)
+
+ // - templates need to be set here, as PersistentPreRunE() is
+ // not called when --help is used.
+ // - rootCmd uses cobra default template not ours
+ c.Command.SetHelpTemplate(helpTemplate)
+ c.Command.SetUsageTemplate(usageTemplate)
+ c.Command.DisableFlagsInUseLine = true
+}
+
+func noop(cmd *cobra.Command, args []string) error {
+ return nil
+}
diff --git a/cmd/podman/pods/logs.go b/cmd/podman/pods/logs.go
index e35b48bed..28e7b7a43 100644
--- a/cmd/podman/pods/logs.go
+++ b/cmd/podman/pods/logs.go
@@ -89,6 +89,8 @@ func logsFlags(cmd *cobra.Command) {
flags.BoolVarP(&logsPodOptions.Names, "names", "n", false, "Output container names instead of container IDs in the log")
flags.BoolVarP(&logsPodOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log")
+ flags.BoolVarP(&logsPodOptions.Colors, "color", "", false, "Output the containers within a pod with different colors in the log")
+
flags.SetInterspersed(false)
_ = flags.MarkHidden("details")
}
diff --git a/contrib/cirrus/cirrus_yaml_test.py b/contrib/cirrus/cirrus_yaml_test.py
index b424c3ee6..a7fff8d3f 100755
--- a/contrib/cirrus/cirrus_yaml_test.py
+++ b/contrib/cirrus/cirrus_yaml_test.py
@@ -26,7 +26,8 @@ class TestCaseBase(unittest.TestCase):
class TestDependsOn(TestCaseBase):
ALL_TASK_NAMES = None
- SUCCESS_DEPS_EXCLUDE = set(['success', 'artifacts', 'release', 'release_test'])
+ SUCCESS_DEPS_EXCLUDE = set(['success', 'artifacts',
+ 'test_image_build', 'release', 'release_test'])
def setUp(self):
super().setUp()
diff --git a/contrib/helloimage/Containerfile b/contrib/hello/Containerfile
index 0cbf6d9a0..0cbf6d9a0 100644
--- a/contrib/helloimage/Containerfile
+++ b/contrib/hello/Containerfile
diff --git a/contrib/helloimage/README.md b/contrib/hello/README.md
index 528466f7b..528466f7b 100644
--- a/contrib/helloimage/README.md
+++ b/contrib/hello/README.md
diff --git a/contrib/helloimage/podman_hello_world.c b/contrib/hello/podman_hello_world.c
index ee574130d..ee574130d 100644
--- a/contrib/helloimage/podman_hello_world.c
+++ b/contrib/hello/podman_hello_world.c
diff --git a/contrib/systemd/system/podman.service.in b/contrib/systemd/system/podman.service.in
index 9a7e04fd4..c1a5952b5 100644
--- a/contrib/systemd/system/podman.service.in
+++ b/contrib/systemd/system/podman.service.in
@@ -6,6 +6,7 @@ Documentation=man:podman-system-service(1)
StartLimitIntervalSec=0
[Service]
+Delegate=true
Type=exec
KillMode=process
Environment=LOGGING="--log-level=info"
diff --git a/docs/source/markdown/podman-logs.1.md b/docs/source/markdown/podman-logs.1.md
index e12042030..6ce6c3812 100644
--- a/docs/source/markdown/podman-logs.1.md
+++ b/docs/source/markdown/podman-logs.1.md
@@ -15,6 +15,10 @@ any logs at the time you execute podman logs).
## OPTIONS
+#### **--color**
+
+Output the containers with different colors in the log.
+
#### **--follow**, **-f**
Follow log output. Default is false.
diff --git a/docs/source/markdown/podman-pod-logs.1.md b/docs/source/markdown/podman-pod-logs.1.md
index 5ef667504..7fc1d4b90 100644
--- a/docs/source/markdown/podman-pod-logs.1.md
+++ b/docs/source/markdown/podman-pod-logs.1.md
@@ -13,6 +13,10 @@ Note: Long running command of `podman pod log` with a `-f` or `--follow` needs t
## OPTIONS
+#### **--color**
+
+Output the containers with different colors in the log.
+
#### **--container**, **-c**
By default `podman pod logs` retrieves logs for all the containers available within the pod differentiate by field `container`. However there are use-cases where user would want to limit the log stream only to a particular container of a pod for such cases `-c` can be used like `podman pod logs -c ctrNameorID podname`.
diff --git a/hack/make-and-check-size b/hack/make-and-check-size
index 71b382b44..f2345b815 100755
--- a/hack/make-and-check-size
+++ b/hack/make-and-check-size
@@ -109,6 +109,7 @@ for bin in bin/*;do
size_orig=$(< $saved_size_file)
delta_size=$(( size - size_orig ))
+ printf "%-20s size=%9d delta=%6d\n" $bin $size $delta_size
if [[ $delta_size -gt $MAX_BIN_GROWTH ]]; then
separator=$(printf "%.0s*" {1..75}) # row of stars, for highlight
echo "$separator"
@@ -129,5 +130,6 @@ for bin in bin/*;do
else
# First time through: preserve original file size
echo $size >$saved_size_file
+ printf "%-20s size=%9d\n" $bin $size
fi
done
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 0d9cd5723..ea644764c 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v4/pkg/namespaces"
+ "github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -405,13 +406,19 @@ type ContainerMiscConfig struct {
InitContainerType string `json:"init_container_type,omitempty"`
}
+// InfraInherit contains the compatible options inheritable from the infra container
type InfraInherit struct {
- InfraSecurity ContainerSecurityConfig
- InfraLabels []string `json:"labelopts,omitempty"`
- InfraVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
- InfraOverlay []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
- InfraImageVolumes []*ContainerImageVolume `json:"ctrImageVolumes,omitempty"`
- InfraUserVolumes []string `json:"userVolumes,omitempty"`
- InfraResources *spec.LinuxResources `json:"resources,omitempty"`
- InfraDevices []spec.LinuxDevice `json:"device_host_src,omitempty"`
+ ApparmorProfile string `json:"apparmor_profile,omitempty"`
+ CapAdd []string `json:"cap_add,omitempty"`
+ CapDrop []string `json:"cap_drop,omitempty"`
+ HostDeviceList []spec.LinuxDevice `json:"host_device_list,omitempty"`
+ ImageVolumes []*specgen.ImageVolume `json:"image_volumes,omitempty"`
+ InfraResources *spec.LinuxResources `json:"resource_limits,omitempty"`
+ Mounts []spec.Mount `json:"mounts,omitempty"`
+ NoNewPrivileges bool `json:"no_new_privileges,omitempty"`
+ OverlayVolumes []*specgen.OverlayVolume `json:"overlay_volumes,omitempty"`
+ SeccompPolicy string `json:"seccomp_policy,omitempty"`
+ SeccompProfilePath string `json:"seccomp_profile_path,omitempty"`
+ SelinuxOpts []string `json:"selinux_opts,omitempty"`
+ Volumes []*specgen.NamedVolume `json:"volumes,omitempty"`
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 5fb32bd90..f2a2c2d16 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -103,8 +103,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
}
}
- namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
- inspectMounts, err := c.GetInspectMounts(namedVolumes, c.config.ImageVolumes, mounts)
+ namedVolumes, mounts := c.SortUserVolumes(ctrSpec)
+ inspectMounts, err := c.GetMounts(namedVolumes, c.config.ImageVolumes, mounts)
if err != nil {
return nil, err
}
@@ -222,7 +222,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
// Get inspect-formatted mounts list.
// Only includes user-specified mounts. Only includes bind mounts and named
// volumes, not tmpfs volumes.
-func (c *Container) GetInspectMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) {
+func (c *Container) GetMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) {
inspectMounts := []define.InspectMount{}
// No mounts, return early
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 0db59f2fe..f1f467879 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -2235,9 +2235,9 @@ func (c *Container) prepareCheckpointExport() error {
return nil
}
-// sortUserVolumes sorts the volumes specified for a container
+// SortUserVolumes sorts the volumes specified for a container
// between named and normal volumes
-func (c *Container) sortUserVolumes(ctrSpec *spec.Spec) ([]*ContainerNamedVolume, []spec.Mount) {
+func (c *Container) SortUserVolumes(ctrSpec *spec.Spec) ([]*ContainerNamedVolume, []spec.Mount) {
namedUserVolumes := []*ContainerNamedVolume{}
userMounts := []spec.Mount{}
diff --git a/libpod/container_log.go b/libpod/container_log.go
index dd5c36e26..7a9eb2dbf 100644
--- a/libpod/container_log.go
+++ b/libpod/container_log.go
@@ -23,8 +23,8 @@ func init() {
// Log is a runtime function that can read one or more container logs.
func (r *Runtime) Log(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
- for _, ctr := range containers {
- if err := ctr.ReadLog(ctx, options, logChannel); err != nil {
+ for c, ctr := range containers {
+ if err := ctr.ReadLog(ctx, options, logChannel, int64(c)); err != nil {
return err
}
}
@@ -32,26 +32,26 @@ func (r *Runtime) Log(ctx context.Context, containers []*Container, options *log
}
// ReadLog reads a containers log based on the input options and returns log lines over a channel.
-func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
switch c.LogDriver() {
case define.PassthroughLogging:
return errors.Wrapf(define.ErrNoLogs, "this container is using the 'passthrough' log driver, cannot read logs")
case define.NoLogging:
return errors.Wrapf(define.ErrNoLogs, "this container is using the 'none' log driver, cannot read logs")
case define.JournaldLogging:
- return c.readFromJournal(ctx, options, logChannel)
+ return c.readFromJournal(ctx, options, logChannel, colorID)
case define.JSONLogging:
// TODO provide a separate implementation of this when Conmon
// has support.
fallthrough
case define.KubernetesLogging, "":
- return c.readFromLogFile(ctx, options, logChannel)
+ return c.readFromLogFile(ctx, options, logChannel, colorID)
default:
return errors.Wrapf(define.ErrInternal, "unrecognized log driver %q, cannot read logs", c.LogDriver())
}
}
-func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
t, tailLog, err := logs.GetLogFile(c.LogPath(), options)
if err != nil {
// If the log file does not exist, this is not fatal.
@@ -65,6 +65,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption
for _, nll := range tailLog {
nll.CID = c.ID()
nll.CName = c.Name()
+ nll.ColorID = colorID
if nll.Since(options.Since) && nll.Until(options.Until) {
logChannel <- nll
}
@@ -97,6 +98,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption
}
nll.CID = c.ID()
nll.CName = c.Name()
+ nll.ColorID = colorID
if nll.Since(options.Since) && nll.Until(options.Until) {
logChannel <- nll
}
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 8ae8ff2c0..d96647e51 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -45,7 +45,7 @@ func (c *Container) initializeJournal(ctx context.Context) error {
return journal.Send("", journal.PriInfo, m)
}
-func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
// We need the container's events in the same journal to guarantee
// consistency, see #10323.
if options.Follow && c.runtime.config.Engine.EventsLogger != "journald" {
@@ -231,6 +231,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
}
logLine, err := logs.NewJournaldLogLine(message, options.Multi)
+ logLine.ColorID = colorID
if err != nil {
logrus.Errorf("Failed parse log line: %v", err)
return
diff --git a/libpod/container_log_unsupported.go b/libpod/container_log_unsupported.go
index 4f50f9f4c..c84a578cc 100644
--- a/libpod/container_log_unsupported.go
+++ b/libpod/container_log_unsupported.go
@@ -11,7 +11,7 @@ import (
"github.com/pkg/errors"
)
-func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine) error {
+func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine, colorID int64) error {
return errors.Wrapf(define.ErrOSNotSupported, "Journald logging only enabled with systemd on linux")
}
diff --git a/libpod/define/version.go b/libpod/define/version.go
index 039b0ff27..2c17e6e92 100644
--- a/libpod/define/version.go
+++ b/libpod/define/version.go
@@ -27,6 +27,7 @@ type Version struct {
BuiltTime string
Built int64
OsArch string
+ Os string
}
// GetVersion returns a VersionOutput struct for API and podman
@@ -49,5 +50,6 @@ func GetVersion() (Version, error) {
BuiltTime: time.Unix(buildTime, 0).Format(time.ANSIC),
Built: buildTime,
OsArch: runtime.GOOS + "/" + runtime.GOARCH,
+ Os: runtime.GOOS,
}, nil
}
diff --git a/libpod/kube.go b/libpod/kube.go
index a193df2cb..22fbb5f9f 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -773,7 +773,7 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
// libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands
func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, map[string]string, error) {
- namedVolumes, mounts := c.sortUserVolumes(c.config.Spec)
+ namedVolumes, mounts := c.SortUserVolumes(c.config.Spec)
vms := make([]v1.VolumeMount, 0, len(mounts))
vos := make([]v1.Volume, 0, len(mounts))
annotations := make(map[string]string)
diff --git a/libpod/logs/log.go b/libpod/logs/log.go
index 9672f6ee1..0eb3bb922 100644
--- a/libpod/logs/log.go
+++ b/libpod/logs/log.go
@@ -27,6 +27,9 @@ const (
// FullLogType signifies a log line is full
FullLogType = "F"
+
+ //ANSIEscapeResetCode is a code that resets all colors and text effects
+ ANSIEscapeResetCode = "\033[0m"
)
// LogOptions is the options you can use for logs
@@ -37,6 +40,7 @@ type LogOptions struct {
Until time.Time
Tail int64
Timestamps bool
+ Colors bool
Multi bool
WaitGroup *sync.WaitGroup
UseName bool
@@ -50,6 +54,7 @@ type LogLine struct {
Msg string
CID string
CName string
+ ColorID int64
}
// GetLogFile returns an hp tail for a container given options
@@ -162,6 +167,24 @@ func getTailLog(path string, tail int) ([]*LogLine, error) {
return tailLog, nil
}
+//getColor returns a ANSI escape code for color based on the colorID
+func getColor(colorID int64) string {
+ colors := map[int64]string{
+ 0: "\033[37m", // Light Gray
+ 1: "\033[31m", // Red
+ 2: "\033[33m", // Yellow
+ 3: "\033[34m", // Blue
+ 4: "\033[35m", // Magenta
+ 5: "\033[36m", // Cyan
+ 6: "\033[32m", // Green
+ }
+ return colors[colorID%int64(len(colors))]
+}
+
+func (l *LogLine) colorize(prefix string) string {
+ return getColor(l.ColorID) + prefix + l.Msg + ANSIEscapeResetCode
+}
+
// String converts a log line to a string for output given whether a detail
// bool is specified.
func (l *LogLine) String(options *LogOptions) string {
@@ -177,10 +200,18 @@ func (l *LogLine) String(options *LogOptions) string {
out = fmt.Sprintf("%s ", cid)
}
}
+
if options.Timestamps {
out += fmt.Sprintf("%s ", l.Time.Format(LogTimeFormat))
}
- return out + l.Msg
+
+ if options.Colors {
+ out = l.colorize(out)
+ } else {
+ out += l.Msg
+ }
+
+ return out
}
// Since returns a bool as to whether a log line occurred after a given time
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index db36ac75d..71e29f18f 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -498,10 +498,13 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
return nil, err
}
- // move to systemd scope to prevent systemd from killing it
- err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid)
- if err != nil {
- logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err)
+ if utils.RunsOnSystemd() {
+ // move to systemd scope to prevent systemd from killing it
+ err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid)
+ if err != nil {
+ // only log this, it is not fatal but can lead to issues when running podman inside systemd units
+ logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err)
+ }
}
// build a new resolv.conf file which uses the slirp4netns dns server address
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index 3f6c4bef2..3f2842d4c 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -614,60 +614,73 @@ func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd
// 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.convertPortMappings() {
- 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)
+ for _, port := range ctr.convertPortMappings() {
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ hostIP := port.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ for i := uint16(0); i < port.Range; i++ {
+ if err := openSlirp4netnsPort(apiSocket, protocol, hostIP, port.HostPort+i, port.ContainerPort+i); err != nil {
+ return 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("from slirp4netns while setting up port redirection: %v", e)
}
}
logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready")
return nil
}
+// openSlirp4netnsPort sends the slirp4netns pai quey to the given socket
+func openSlirp4netnsPort(apiSocket, proto, hostip string, hostport, guestport uint16) error {
+ 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 slirp4netns connection: %q", err)
+ }
+ }()
+ apiCmd := slirp4netnsCmd{
+ Execute: "add_hostfwd",
+ Args: slirp4netnsCmdArg{
+ Proto: proto,
+ HostAddr: hostip,
+ HostPort: hostport,
+ GuestPort: guestport,
+ },
+ }
+ // 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("from slirp4netns while setting up port redirection: %v", e)
+ }
+ return nil
+}
+
func getRootlessPortChildIP(c *Container, netStatus map[string]types.StatusBlock) string {
if c.config.NetMode.IsSlirp4netns() {
slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet)
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 38bf85834..264236dc1 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -661,7 +661,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.
}
errChan <- err
}()
- if err := ctr.ReadLog(context.Background(), logOpts, logChan); err != nil {
+ if err := ctr.ReadLog(context.Background(), logOpts, logChan, 0); err != nil {
return err
}
go func() {
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index be726d8d1..48049798b 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -602,8 +602,8 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
infraConfig.PidNS = p.PidMode()
infraConfig.UserNS = p.UserNSMode()
- namedVolumes, mounts := infra.sortUserVolumes(infra.config.Spec)
- inspectMounts, err = infra.GetInspectMounts(namedVolumes, infra.config.ImageVolumes, mounts)
+ namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec)
+ inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions()
if err != nil {
return nil, err
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index f6739b7ca..ab562377f 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -367,20 +367,20 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
return nil, err
}
- // Check if Containerfile is in the context directory, if so truncate the contextdirectory off path
+ // Check if Containerfile is in the context directory, if so truncate the context directory off path
// Do NOT add to tarfile
if strings.HasPrefix(containerfile, contextDir+string(filepath.Separator)) {
containerfile = strings.TrimPrefix(containerfile, contextDir+string(filepath.Separator))
dontexcludes = append(dontexcludes, "!"+containerfile)
} else {
- // If Containerfile does not exists assume it is in context directory, do Not add to tarfile
+ // If Containerfile does not exist, assume it is in context directory and do Not add to tarfile
if _, err := os.Lstat(containerfile); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
containerfile = c
} else {
- // If Containerfile does exists but is not in context directory add it to the tarfile
+ // If Containerfile does exist and not in the context directory, add it to the tarfile
tarContent = append(tarContent, containerfile)
}
}
@@ -586,6 +586,9 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
return errors.Wrapf(err, "error checking if %q is excluded", name)
}
if excluded {
+ // Note: filepath.SkipDir is not possible to use given .dockerignore semantics.
+ // An exception to exclusions may include an excluded directory, therefore we
+ // are required to visit all files. :(
return nil
}
diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go
index 1eb90ab54..5ef78e444 100644
--- a/pkg/bindings/system/system.go
+++ b/pkg/bindings/system/system.go
@@ -120,6 +120,7 @@ func Version(ctx context.Context, options *VersionOptions) (*entities.SystemVers
BuiltTime: time.Unix(b.Unix(), 0).Format(time.ANSIC),
Built: b.Unix(),
OsArch: fmt.Sprintf("%s/%s", component.Os, component.Arch),
+ Os: component.Os,
}
for _, c := range component.Components {
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 79795a221..072514d0f 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -257,6 +257,8 @@ type ContainerLogsOptions struct {
Tail int64
// Show timestamps in the logs.
Timestamps bool
+ // Show different colors in the logs.
+ Colors bool
// Write the stdout to this Writer.
StdoutWriter io.Writer
// Write the stderr to this Writer.
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index da93d3f8b..f1d445c4b 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -149,6 +149,8 @@ type PodLogsOptions struct {
ContainerLogsOptions
// If specified will only fetch the logs of specified container
ContainerName string
+ // Show different colors in the logs.
+ Color bool
}
type ContainerCreateOptions struct {
@@ -210,7 +212,7 @@ type ContainerCreateOptions struct {
Name string `json:"container_name"`
NoHealthCheck bool
OOMKillDisable bool
- OOMScoreAdj int
+ OOMScoreAdj *int
Arch string
OS string
Variant string
@@ -482,6 +484,7 @@ func PodLogsOptionsToContainerLogsOptions(options PodLogsOptions) ContainerLogsO
Until: options.Until,
Tail: options.Tail,
Timestamps: options.Timestamps,
+ Colors: options.Colors,
StdoutWriter: options.StdoutWriter,
StderrWriter: options.StderrWriter,
}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index f45bdeba5..100842c69 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -1088,6 +1088,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
Until: options.Until,
Tail: options.Tail,
Timestamps: options.Timestamps,
+ Colors: options.Colors,
UseName: options.Names,
WaitGroup: &wg,
}
@@ -1491,7 +1492,7 @@ func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string,
func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts entities.ContainerCloneOptions) (*entities.ContainerCreateReport, error) {
spec := specgen.NewSpecGenerator(ctrCloneOpts.Image, ctrCloneOpts.CreateOpts.RootFS)
var c *libpod.Container
- c, err := generate.ConfigToSpec(ic.Libpod, spec, ctrCloneOpts.ID)
+ c, _, err := generate.ConfigToSpec(ic.Libpod, spec, ctrCloneOpts.ID)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 1423ab06e..c3f6bb17d 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -290,7 +290,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
if v.Type == kube.KubeVolumeTypeConfigMap && !v.Optional {
vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source))
if err != nil {
- return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source)
+ if errors.Is(err, define.ErrVolumeExists) {
+ // Volume for this configmap already exists do not
+ // error out instead reuse the current volume.
+ vol, err = ic.Libpod.GetVolume(v.Source)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot re-use local volume for volume from configmap %q", v.Source)
+ }
+ } else {
+ return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source)
+ }
}
mountPoint, err := vol.MountPoint()
if err != nil || mountPoint == "" {
diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go
index 05a1d74d3..3d1032fba 100644
--- a/pkg/machine/qemu/config.go
+++ b/pkg/machine/qemu/config.go
@@ -5,6 +5,7 @@ package qemu
import (
"errors"
+ "io/ioutil"
"os"
"time"
@@ -59,6 +60,8 @@ type MachineVMV1 struct {
}
type MachineVM struct {
+ // ConfigPath is the path to the configuration file
+ ConfigPath MachineFile
// The command line representation of the qemu command
CmdLine []string
// HostUser contains info about host user
@@ -83,11 +86,11 @@ type MachineVM struct {
// ImageConfig describes the bootable image for the VM
type ImageConfig struct {
- IgnitionFilePath string
+ IgnitionFilePath MachineFile
// ImageStream is the update stream for the image
ImageStream string
// ImagePath is the fq path to
- ImagePath string
+ ImagePath MachineFile
}
// HostUser describes the host user
@@ -171,11 +174,19 @@ func (m *MachineFile) GetPath() string {
// the actual path
func (m *MachineFile) Delete() error {
if m.Symlink != nil {
- if err := os.Remove(*m.Symlink); err != nil {
+ if err := os.Remove(*m.Symlink); err != nil && !errors.Is(err, os.ErrNotExist) {
logrus.Errorf("unable to remove symlink %q", *m.Symlink)
}
}
- return os.Remove(m.Path)
+ if err := os.Remove(m.Path); err != nil && !errors.Is(err, os.ErrNotExist) {
+ return err
+ }
+ return nil
+}
+
+// Read the contents of a given file and return in []bytes
+func (m *MachineFile) Read() ([]byte, error) {
+ return ioutil.ReadFile(m.GetPath())
}
// NewMachineFile is a constructor for MachineFile
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 07155bbcf..65980129d 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -71,10 +71,17 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
if len(opts.Name) > 0 {
vm.Name = opts.Name
}
- ignitionFile := filepath.Join(vmConfigDir, vm.Name+".ign")
- vm.IgnitionFilePath = ignitionFile
+ ignitionFile, err := NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil)
+ if err != nil {
+ return nil, err
+ }
+ vm.IgnitionFilePath = *ignitionFile
- vm.ImagePath = opts.ImagePath
+ imagePath, err := NewMachineFile(opts.ImagePath, nil)
+ if err != nil {
+ return nil, err
+ }
+ vm.ImagePath = *imagePath
vm.RemoteUsername = opts.Username
// Add a random port for ssh
@@ -104,7 +111,7 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
// Add cpus
cmd = append(cmd, []string{"-smp", strconv.Itoa(int(vm.CPUs))}...)
// Add ignition file
- cmd = append(cmd, []string{"-fw_cfg", "name=opt/com.coreos/config,file=" + vm.IgnitionFilePath}...)
+ cmd = append(cmd, []string{"-fw_cfg", "name=opt/com.coreos/config,file=" + vm.IgnitionFilePath.GetPath()}...)
// Add qmp socket
monitor, err := NewQMPMonitor("unix", vm.Name, defaultQMPTimeout)
if err != nil {
@@ -117,17 +124,19 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
// Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is
// why we can only run one vm at a time right now
cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...)
- socketPath, err := getRuntimeDir()
- if err != nil {
+ if err := vm.setReadySocket(); err != nil {
return nil, err
}
- virtualSocketPath := filepath.Join(socketPath, "podman", vm.Name+"_ready.sock")
+
// Add serial port for readiness
cmd = append(cmd, []string{
"-device", "virtio-serial",
- "-chardev", "socket,path=" + virtualSocketPath + ",server=on,wait=off,id=" + vm.Name + "_ready",
+ "-chardev", "socket,path=" + vm.getReadySocket() + ",server=on,wait=off,id=" + vm.Name + "_ready",
"-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...)
vm.CmdLine = cmd
+ if err := vm.setQEMUAndPIDSocket(); err != nil {
+ return nil, err
+ }
return vm, nil
}
@@ -165,12 +174,26 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error {
vm.ResourceConfig = ResourceConfig{}
vm.SSHConfig = SSHConfig{}
+ ignitionFilePath, err := NewMachineFile(old.IgnitionFilePath, nil)
+ if err != nil {
+ return err
+ }
+ imagePath, err := NewMachineFile(old.ImagePath, nil)
+ if err != nil {
+ return err
+ }
+
+ // setReadySocket will stick the entry into the new struct
+ if err := vm.setReadySocket(); err != nil {
+ return err
+ }
+
vm.CPUs = old.CPUs
vm.CmdLine = old.CmdLine
vm.DiskSize = old.DiskSize
vm.IdentityPath = old.IdentityPath
- vm.IgnitionFilePath = old.IgnitionFilePath
- vm.ImagePath = old.ImagePath
+ vm.IgnitionFilePath = *ignitionFilePath
+ vm.ImagePath = *imagePath
vm.ImageStream = old.ImageStream
vm.Memory = old.Memory
vm.Mounts = old.Mounts
@@ -200,31 +223,15 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error {
return os.Remove(configPath + ".orig")
}
-// LoadByName reads a json file that describes a known qemu vm
+// LoadVMByName reads a json file that describes a known qemu vm
// and returns a vm instance
func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
vm := &MachineVM{Name: name}
vm.HostUser = HostUser{UID: -1} // posix reserves -1, so use it to signify undefined
- vmConfigDir, err := machine.GetConfDir(vmtype)
- if err != nil {
+ if err := vm.update(); err != nil {
return nil, err
}
- path := filepath.Join(vmConfigDir, name+".json")
- b, err := ioutil.ReadFile(path)
- if os.IsNotExist(err) {
- return nil, errors.Wrap(machine.ErrNoSuchVM, name)
- }
- if err != nil {
- return nil, err
- }
- err = json.Unmarshal(b, vm)
- if err != nil {
- migrateErr := migrateVM(path, b, vm)
- if migrateErr != nil {
- return nil, migrateErr
- }
- err = migrateErr
- }
+
// It is here for providing the ability to propagate
// proxy settings (e.g. HTTP_PROXY and others) on a start
// and avoid a need of re-creating/re-initiating a VM
@@ -239,7 +246,7 @@ func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
}
logrus.Debug(vm.CmdLine)
- return vm, err
+ return vm, nil
}
// Init writes the json configuration file to the filesystem for
@@ -261,7 +268,11 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
if err != nil {
return false, err
}
- v.ImagePath = dd.Get().LocalUncompressedFile
+ uncompressedFile, err := NewMachineFile(dd.Get().LocalUncompressedFile, nil)
+ if err != nil {
+ return false, err
+ }
+ v.ImagePath = *uncompressedFile
if err := machine.DownloadImage(dd); err != nil {
return false, err
}
@@ -273,14 +284,17 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
if err != nil {
return false, err
}
- v.ImagePath = g.Get().LocalUncompressedFile
+ imagePath, err := NewMachineFile(g.Get().LocalUncompressedFile, nil)
+ if err != nil {
+ return false, err
+ }
+ v.ImagePath = *imagePath
if err := machine.DownloadImage(g); err != nil {
return false, err
}
}
// Add arch specific options including image location
v.CmdLine = append(v.CmdLine, v.addArchOptions()...)
-
var volumeType string
switch opts.VolumeDriver {
case "virtfs":
@@ -330,7 +344,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
v.UID = os.Getuid()
// Add location of bootable image
- v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath)
+ v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.getImageFile())
// This kind of stinks but no other way around this r/n
if len(opts.IgnitionPath) < 1 {
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID), strconv.Itoa(v.Port), v.RemoteUsername)
@@ -373,7 +387,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
return false, err
}
- originalDiskSize, err := getDiskSize(v.ImagePath)
+ originalDiskSize, err := getDiskSize(v.getImageFile())
if err != nil {
return false, err
}
@@ -390,7 +404,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
if err != nil {
return false, err
}
- resize := exec.Command(resizePath, []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...)
+ resize := exec.Command(resizePath, []string{"resize", v.getImageFile(), strconv.Itoa(int(opts.DiskSize)) + "G"}...)
resize.Stdout = os.Stdout
resize.Stderr = os.Stderr
if err := resize.Run(); err != nil {
@@ -404,7 +418,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
if err != nil {
return false, err
}
- return false, ioutil.WriteFile(v.IgnitionFilePath, inputIgnition, 0644)
+ return false, ioutil.WriteFile(v.getIgnitionFile(), inputIgnition, 0644)
}
// Write the ignition file
ign := machine.DynamicIgnition{
@@ -412,14 +426,14 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
Key: key,
VMName: v.Name,
TimeZone: opts.TimeZone,
- WritePath: v.IgnitionFilePath,
+ WritePath: v.getIgnitionFile(),
UID: v.UID,
}
err = machine.NewIgnitionFile(ign)
return err == nil, err
}
-func (v *MachineVM) Set(name string, opts machine.SetOptions) error {
+func (v *MachineVM) Set(_ string, opts machine.SetOptions) error {
if v.Rootful == opts.Rootful {
return nil
}
@@ -473,17 +487,16 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
return err
}
}
- qemuSocketPath, _, err := v.getSocketandPid()
- if err != nil {
+ if err := v.setQEMUAndPIDSocket(); err != nil {
return err
}
// If the qemusocketpath exists and the vm is off/down, we should rm
// it before the dial as to avoid a segv
- if err := os.Remove(qemuSocketPath); err != nil && !errors.Is(err, os.ErrNotExist) {
- logrus.Warn(err)
+ if err := v.QMPMonitor.Address.Delete(); err != nil {
+ return err
}
for i := 0; i < 6; i++ {
- qemuSocketConn, err = net.Dial("unix", qemuSocketPath)
+ qemuSocketConn, err = net.Dial("unix", v.QMPMonitor.Address.GetPath())
if err == nil {
break
}
@@ -650,7 +663,7 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.QemuMachine
}
// Stop uses the qmp monitor to call a system_powerdown
-func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
+func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error {
var disconnected bool
// check if the qmp socket is there. if not, qemu instance is gone
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) {
@@ -687,14 +700,10 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return err
}
- qemuSocketFile, pidFile, err := v.getSocketandPid()
- if err != nil {
- return err
- }
- if _, err := os.Stat(pidFile); os.IsNotExist(err) {
+ if _, err := os.Stat(v.getPidFile()); os.IsNotExist(err) {
return nil
}
- pidString, err := ioutil.ReadFile(pidFile)
+ pidString, err := v.PidFilePath.Read()
if err != nil {
return err
}
@@ -712,11 +721,11 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return err
}
// Remove the pidfile
- if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) {
- logrus.Warn(err)
+ if err := v.PidFilePath.Delete(); err != nil {
+ return err
}
// Remove socket
- if err := os.Remove(qemuSocketFile); err != nil {
+ if err := v.QMPMonitor.Address.Delete(); err != nil {
return err
}
@@ -774,7 +783,7 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error)
}
// Remove deletes all the files associated with a machine including ssh keys, the image itself
-func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, func() error, error) {
+func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func() error, error) {
var (
files []string
)
@@ -793,10 +802,10 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
files = append(files, v.IdentityPath, v.IdentityPath+".pub")
}
if !opts.SaveIgnition {
- files = append(files, v.IgnitionFilePath)
+ files = append(files, v.getIgnitionFile())
}
if !opts.SaveImage {
- files = append(files, v.ImagePath)
+ files = append(files, v.getImageFile())
}
socketPath, err := v.getForwardSocketPath()
if err != nil {
@@ -822,19 +831,15 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
confirmationMessage += msg + "\n"
}
- // Get path to socket and pidFile before we do any cleanups
- qemuSocketFile, pidFile, errSocketFile := v.getSocketandPid()
//silently try to delete socket and pid file
//remove socket and pid file if any: warn at low priority if things fail
- if errSocketFile == nil {
- // Remove the pidfile
- if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) {
- logrus.Debugf("Error while removing pidfile: %v", err)
- }
- // Remove socket
- if err := os.Remove(qemuSocketFile); err != nil && !errors.Is(err, os.ErrNotExist) {
- logrus.Debugf("Error while removing podman-machine-socket: %v", err)
- }
+ // Remove the pidfile
+ if err := v.PidFilePath.Delete(); err != nil {
+ logrus.Debugf("Error while removing pidfile: %v", err)
+ }
+ // Remove socket
+ if err := v.QMPMonitor.Address.Delete(); err != nil {
+ logrus.Debugf("Error while removing podman-machine-socket: %v", err)
}
confirmationMessage += "\n"
@@ -890,7 +895,7 @@ func (v *MachineVM) isListening() bool {
// SSH opens an interactive SSH session to the vm specified.
// Added ssh function to VM interface: pkg/machine/config/go : line 58
-func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
+func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error {
running, err := v.isRunning()
if err != nil {
return err
@@ -966,10 +971,10 @@ func getDiskSize(path string) (uint64, error) {
// List lists all vm's that use qemu virtualization
func (p *Provider) List(_ machine.ListOptions) ([]*machine.ListResponse, error) {
- return GetVMInfos()
+ return getVMInfos()
}
-func GetVMInfos() ([]*machine.ListResponse, error) {
+func getVMInfos() ([]*machine.ListResponse, error) {
vmConfigDir, err := machine.GetConfDir(vmtype)
if err != nil {
return nil, err
@@ -1011,7 +1016,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
}
listEntry.CreatedAt = fi.ModTime()
- fi, err = os.Stat(vm.ImagePath)
+ fi, err = os.Stat(vm.getImageFile())
if err != nil {
return err
}
@@ -1034,7 +1039,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
}
func (p *Provider) IsValidVMName(name string) (bool, error) {
- infos, err := GetVMInfos()
+ infos, err := getVMInfos()
if err != nil {
return false, err
}
@@ -1049,7 +1054,7 @@ func (p *Provider) IsValidVMName(name string) (bool, error) {
// CheckExclusiveActiveVM checks if there is a VM already running
// that does not allow other VMs to be running
func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) {
- vms, err := GetVMInfos()
+ vms, err := getVMInfos()
if err != nil {
return false, "", errors.Wrap(err, "error checking VM active")
}
@@ -1073,16 +1078,12 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {
return "", noForwarding, err
}
- qemuSocket, pidFile, err := v.getSocketandPid()
- if err != nil {
- return "", noForwarding, err
- }
attr := new(os.ProcAttr)
// Pass on stdin, stdout, stderr
files := []*os.File{os.Stdin, os.Stdout, os.Stderr}
attr.Files = files
cmd := []string{binary}
- cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...)
+ cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", v.getQMPMonitorSocket()), "-pid-file", v.getPidFile()}...)
// Add the ssh port
cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...)
@@ -1171,6 +1172,52 @@ func (v *MachineVM) getForwardSocketPath() (string, error) {
return filepath.Join(path, "podman.sock"), nil
}
+func (v *MachineVM) setConfigPath() error {
+ vmConfigDir, err := machine.GetConfDir(vmtype)
+ if err != nil {
+ return err
+ }
+
+ configPath, err := NewMachineFile(filepath.Join(vmConfigDir, v.Name)+".json", nil)
+ if err != nil {
+ return err
+ }
+ v.ConfigPath = *configPath
+ return nil
+}
+
+func (v *MachineVM) setReadySocket() error {
+ rtPath, err := getRuntimeDir()
+ if err != nil {
+ return err
+ }
+ virtualSocketPath, err := NewMachineFile(filepath.Join(rtPath, "podman", v.Name+"_ready.sock"), nil)
+ if err != nil {
+ return err
+ }
+ v.ReadySocket = *virtualSocketPath
+ return nil
+}
+
+func (v *MachineVM) setQEMUAndPIDSocket() error {
+ rtPath, err := getRuntimeDir()
+ if err != nil {
+ return err
+ }
+ if !rootless.IsRootless() {
+ rtPath = "/run"
+ }
+ socketDir := filepath.Join(rtPath, "podman")
+ pidFilePath, err := NewMachineFile(filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name)), nil)
+ if err != nil {
+ return err
+ }
+ v.PidFilePath = *pidFilePath
+ return nil
+}
+
+// Deprecated: getSocketandPid is being replace by setQEMUAndPIDSocket and
+// machinefiles.
func (v *MachineVM) getSocketandPid() (string, string, error) {
rtPath, err := getRuntimeDir()
if err != nil {
@@ -1287,23 +1334,73 @@ func (v *MachineVM) waitAPIAndPrintInfo(forwardState apiForwardingState, forward
}
}
-func (v *MachineVM) writeConfig() error {
- // GetConfDir creates the directory so no need to check for
- // its existence
- vmConfigDir, err := machine.GetConfDir(vmtype)
+// update returns the content of the VM's
+// configuration file in json
+func (v *MachineVM) update() error {
+ if err := v.setConfigPath(); err != nil {
+ return err
+ }
+ b, err := v.ConfigPath.Read()
if err != nil {
+ if os.IsNotExist(err) {
+ return errors.Wrap(machine.ErrNoSuchVM, v.Name)
+ }
return err
}
+ if err != nil {
+ return err
+ }
+ err = json.Unmarshal(b, v)
+ if err != nil {
+ err = migrateVM(v.ConfigPath.GetPath(), b, v)
+ if err != nil {
+ return err
+ }
+ }
+ return err
+}
- jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json"
+func (v *MachineVM) writeConfig() error {
+ // Set the path of the configfile before writing to make
+ // life easier down the line
+ if err := v.setConfigPath(); err != nil {
+ return err
+ }
// Write the JSON file
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
return err
}
- if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil {
+ if err := ioutil.WriteFile(v.ConfigPath.GetPath(), b, 0644); err != nil {
return err
}
-
return nil
}
+
+// getPidFile gets the file where the machine pid is stored
+func (v *MachineVM) getPidFile() string {
+ return v.PidFilePath.GetPath()
+}
+
+// getQMPMonitorSocket gets the socket used by qemu to interact
+// with the instance
+func (v *MachineVM) getQMPMonitorSocket() string {
+ return v.QMPMonitor.Address.GetPath()
+}
+
+// getReadySocket returns the socket used to communicate
+// with the machinevm and report when it is booted
+func (v *MachineVM) getReadySocket() string {
+ return v.ReadySocket.GetPath()
+}
+
+// getImageFile returns the path to the image used
+// to boot the VM
+func (v *MachineVM) getImageFile() string {
+ return v.ImagePath.GetPath()
+}
+
+// getIgnitionFile returns the path to the ignition file
+func (v *MachineVM) getIgnitionFile() string {
+ return v.IgnitionFilePath.GetPath()
+}
diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go
index 5b6cdc86d..4c954af00 100644
--- a/pkg/machine/qemu/options_darwin_arm64.go
+++ b/pkg/machine/qemu/options_darwin_arm64.go
@@ -11,7 +11,7 @@ var (
)
func (v *MachineVM) addArchOptions() []string {
- ovmfDir := getOvmfDir(v.ImagePath, v.Name)
+ ovmfDir := getOvmfDir(v.ImagePath.GetPath(), v.Name)
opts := []string{
"-accel", "hvf",
"-accel", "tcg",
@@ -23,13 +23,13 @@ func (v *MachineVM) addArchOptions() []string {
}
func (v *MachineVM) prepare() error {
- ovmfDir := getOvmfDir(v.ImagePath, v.Name)
+ ovmfDir := getOvmfDir(v.ImagePath.GetPath(), v.Name)
cmd := []string{"/bin/dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir}
return exec.Command(cmd[0], cmd[1:]...).Run()
}
func (v *MachineVM) archRemovalFiles() []string {
- ovmDir := getOvmfDir(v.ImagePath, v.Name)
+ ovmDir := getOvmfDir(v.ImagePath.GetPath(), v.Name)
return []string{ovmDir}
}
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 5128fa313..fdda45ca6 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -145,6 +145,8 @@ http://docs.microsoft.com/en-us/windows/wsl/install\
const (
winSShProxy = "win-sshproxy.exe"
winSshProxyTid = "win-sshproxy.tid"
+ pipePrefix = "npipe:////./pipe/"
+ globalPipe = "docker_engine"
)
type Provider struct{}
@@ -801,16 +803,15 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
}
func launchWinProxy(v *MachineVM) (bool, string, error) {
- globalName := true
- pipeName := "docker_engine"
- if !pipeAvailable(pipeName) {
- pipeName = toDist(v.Name)
- globalName = false
- if !pipeAvailable(pipeName) {
- return globalName, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", pipeName)
- }
+ machinePipe := toDist(v.Name)
+ if !pipeAvailable(machinePipe) {
+ return false, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe)
+ }
+
+ globalName := false
+ if pipeAvailable(globalPipe) {
+ globalName = true
}
- fullPipeName := "npipe:////./pipe/" + pipeName
exe, err := os.Executable()
if err != nil {
@@ -829,12 +830,19 @@ func launchWinProxy(v *MachineVM) (bool, string, error) {
}
dest := fmt.Sprintf("ssh://root@localhost:%d/run/podman/podman.sock", v.Port)
- cmd := exec.Command(command, v.Name, stateDir, fullPipeName, dest, v.IdentityPath)
+ args := []string{v.Name, stateDir, pipePrefix + machinePipe, dest, v.IdentityPath}
+ waitPipe := machinePipe
+ if globalName {
+ args = append(args, pipePrefix+globalPipe, dest, v.IdentityPath)
+ waitPipe = globalPipe
+ }
+
+ cmd := exec.Command(command, args...)
if err := cmd.Start(); err != nil {
return globalName, "", err
}
- return globalName, fullPipeName, waitPipeExists(pipeName, 30, func() error {
+ return globalName, pipePrefix + waitPipe, waitPipeExists(waitPipe, 30, func() error {
active, exitCode := getProcessState(cmd.Process.Pid)
if !active {
return errors.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode)
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go
index e71eafb75..42b70e334 100644
--- a/pkg/specgen/container_validate.go
+++ b/pkg/specgen/container_validate.go
@@ -83,10 +83,6 @@ func (s *SpecGenerator) Validate() error {
//
// ContainerSecurityConfig
//
- // capadd and privileged are exclusive
- if len(s.CapAdd) > 0 && s.Privileged {
- return exclusiveOptions("CapAdd", "privileged")
- }
// userns and idmappings conflict
if s.UserNS.IsPrivate() && s.IDMappings == nil {
return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace")
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 118d80e2c..b38b0e695 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -337,11 +337,11 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error {
return nil
}
-// ConfigToSpec takes a completed container config and converts it back into a specgenerator for purposes of cloning an existing container
-func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID string) (*libpod.Container, error) {
- c, err := rt.LookupContainer(containerID)
+// ConfigToSpec takes a completed container config and converts it back into a specgenerator for purposes of cloning an exisiting container
+func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID string) (*libpod.Container, *libpod.InfraInherit, error) {
+ c, err := rt.LookupContainer(contaierID)
if err != nil {
- return nil, err
+ return nil, nil, err
}
conf := c.Config()
@@ -351,17 +351,22 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID
conf.Systemd = nil
conf.Mounts = []string{}
+ if specg == nil {
+ specg = &specgen.SpecGenerator{}
+ }
+
specg.Pod = conf.Pod
matching, err := json.Marshal(conf)
if err != nil {
- return nil, err
+ return nil, nil, err
}
err = json.Unmarshal(matching, specg)
if err != nil {
- return nil, err
+ return nil, nil, err
}
+
conf.Systemd = tmpSystemd
conf.Mounts = tmpMounts
@@ -481,7 +486,29 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID
}
}
specg.OverlayVolumes = overlay
- specg.Mounts = conf.Spec.Mounts
+ _, mounts := c.SortUserVolumes(c.Spec())
+ specg.Mounts = mounts
specg.HostDeviceList = conf.DeviceHostSrc
- return c, nil
+ mapSecurityConfig(conf, specg)
+
+ if c.IsInfra() { // if we are creating this spec for a pod's infra ctr, map the compatible options
+ spec, err := json.Marshal(specg)
+ if err != nil {
+ return nil, nil, err
+ }
+ infraInherit := &libpod.InfraInherit{}
+ err = json.Unmarshal(spec, infraInherit)
+ return c, infraInherit, err
+ }
+ // else just return the container
+ return c, nil, nil
+}
+
+// mapSecurityConfig takes a libpod.ContainerSecurityConfig and converts it to a specgen.ContinerSecurityConfig
+func mapSecurityConfig(c *libpod.ContainerConfig, s *specgen.SpecGenerator) {
+ s.Privileged = c.Privileged
+ s.SelinuxOpts = append(s.SelinuxOpts, c.LabelOpts...)
+ s.User = c.User
+ s.Groups = c.Groups
+ s.HostUsers = c.HostUsers
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index a014f5047..6a611e854 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -49,7 +49,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
compatibleOptions := &libpod.InfraInherit{}
var infraSpec *spec.Spec
if infra != nil {
- options, infraSpec, compatibleOptions, err = Inherit(*infra)
+ options, infraSpec, compatibleOptions, err = Inherit(*infra, s, rt)
if err != nil {
return nil, nil, nil, err
}
@@ -152,8 +152,8 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, nil, nil, err
}
- infraVolumes := (len(compatibleOptions.InfraVolumes) > 0 || len(compatibleOptions.InfraUserVolumes) > 0 || len(compatibleOptions.InfraImageVolumes) > 0)
- opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVolumes, *compatibleOptions)
+ infraVol := (len(compatibleOptions.Mounts) > 0 || len(compatibleOptions.Volumes) > 0 || len(compatibleOptions.ImageVolumes) > 0 || len(compatibleOptions.OverlayVolumes) > 0)
+ opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions)
if err != nil {
return nil, nil, nil, err
}
@@ -446,7 +446,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if len(s.SelinuxOpts) > 0 {
options = append(options, libpod.WithSecLabels(s.SelinuxOpts))
} else {
- if pod != nil && len(compatibleOptions.InfraLabels) == 0 {
+ if pod != nil && len(compatibleOptions.SelinuxOpts) == 0 {
// duplicate the security options from the pod
processLabel, err := pod.ProcessLabel()
if err != nil {
@@ -544,32 +544,23 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
return options, nil
}
-func Inherit(infra libpod.Container) (opts []libpod.CtrCreateOption, infraS *spec.Spec, compat *libpod.InfraInherit, err error) {
+func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtime) (opts []libpod.CtrCreateOption, infraS *spec.Spec, compat *libpod.InfraInherit, err error) {
+ inheritSpec := &specgen.SpecGenerator{}
+ _, compatibleOptions, err := ConfigToSpec(rt, inheritSpec, infra.ID())
+ if err != nil {
+ return nil, nil, nil, err
+ }
options := []libpod.CtrCreateOption{}
- compatibleOptions := &libpod.InfraInherit{}
infraConf := infra.Config()
infraSpec := infraConf.Spec
- config, err := json.Marshal(infraConf)
+ compatByte, err := json.Marshal(compatibleOptions)
if err != nil {
return nil, nil, nil, err
}
- err = json.Unmarshal(config, compatibleOptions)
+ err = json.Unmarshal(compatByte, s)
if err != nil {
return nil, nil, nil, err
}
- if infraSpec.Linux != nil && infraSpec.Linux.Resources != nil {
- resources, err := json.Marshal(infraSpec.Linux.Resources)
- if err != nil {
- return nil, nil, nil, err
- }
- err = json.Unmarshal(resources, &compatibleOptions.InfraResources)
- if err != nil {
- return nil, nil, nil, err
- }
- }
- if compatibleOptions != nil {
- options = append(options, libpod.WithInfraConfig(*compatibleOptions))
- }
return options, infraSpec, compatibleOptions, nil
}
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index 1cc3a463f..961cea933 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -352,8 +352,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
return nil, err
}
}
- if len(compatibleOptions.InfraDevices) > 0 && len(s.Devices) == 0 {
- userDevices = compatibleOptions.InfraDevices
+ if len(compatibleOptions.HostDeviceList) > 0 && len(s.Devices) == 0 {
+ userDevices = compatibleOptions.HostDeviceList
} else {
userDevices = s.Devices
}
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 186d3862b..7d4fca846 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -753,8 +753,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.PreserveFDs = c.PreserveFDs
}
- if s.OOMScoreAdj == nil || c.OOMScoreAdj != 0 {
- s.OOMScoreAdj = &c.OOMScoreAdj
+ if s.OOMScoreAdj == nil || c.OOMScoreAdj != nil {
+ s.OOMScoreAdj = c.OOMScoreAdj
}
if c.Restart != "" {
splitRestart := strings.Split(c.Restart, ":")
diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go
index 2bd79b186..8a861077a 100644
--- a/pkg/specgenutil/volumes.go
+++ b/pkg/specgenutil/volumes.go
@@ -28,7 +28,7 @@ var (
// TODO: handle options parsing/processing via containers/storage/pkg/mount
func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) {
// Get mounts from the --mounts flag.
- unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag)
+ unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := Mounts(mountFlag)
if err != nil {
return nil, nil, nil, nil, err
}
@@ -167,12 +167,12 @@ func findMountType(input string) (mountType string, tokens []string, err error)
return
}
-// getMounts takes user-provided input from the --mount flag and creates OCI
+// Mounts takes user-provided input from the --mount flag and creates OCI
// spec mounts and Libpod named volumes.
// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
// podman run --mount type=tmpfs,target=/dev/shm ...
// podman run --mount type=volume,source=test-volume, ...
-func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) {
+func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) {
finalMounts := make(map[string]spec.Mount)
finalNamedVolumes := make(map[string]*specgen.NamedVolume)
finalImageVolumes := make(map[string]*specgen.ImageVolume)
@@ -518,7 +518,7 @@ func getDevptsMount(args []string) (spec.Mount, error) {
func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
newVolume := new(specgen.NamedVolume)
- var setSource, setDest, setRORW, setSuid, setDev, setExec, setOwnership bool
+ var setDest, setRORW, setSuid, setDev, setExec, setOwnership bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
@@ -554,7 +554,6 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
return nil, errors.Wrapf(optionArgError, kv[0])
}
newVolume.Name = kv[1]
- setSource = true
case "target", "dst", "destination":
if len(kv) == 1 {
return nil, errors.Wrapf(optionArgError, kv[0])
@@ -585,9 +584,6 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
}
}
- if !setSource {
- return nil, errors.Errorf("must set source volume")
- }
if !setDest {
return nil, noDestError
}
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index 096c98727..0c665687d 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -1,9 +1,11 @@
package integration
import (
+ "bytes"
"fmt"
"io/ioutil"
"os"
+ "os/exec"
"path/filepath"
"runtime"
"strings"
@@ -493,14 +495,64 @@ subdir**`
Expect(output).NotTo(ContainSubstring("/testfilter/subdir"))
})
+ // See https://github.com/containers/podman/issues/13535
+ It("Remote build .containerignore filtering embedded directory (#13535)", func() {
+ SkipIfNotRemote("Testing remote .containerignore file filtering")
+ podmanTest.RestartRemoteService()
+
+ // Switch to temp dir and restore it afterwards
+ cwd, err := os.Getwd()
+ Expect(err).ToNot(HaveOccurred())
+
+ podmanTest.AddImageToRWStore(ALPINE)
+
+ contents := bytes.Buffer{}
+ contents.WriteString("FROM " + ALPINE + "\n")
+ contents.WriteString("ADD . /testfilter/\n")
+ contents.WriteString("RUN find /testfilter/ -print\n")
+
+ containerfile := filepath.Join(tempdir, "Containerfile")
+ Expect(ioutil.WriteFile(containerfile, contents.Bytes(), 0644)).ToNot(HaveOccurred())
+
+ contextDir, err := CreateTempDirInTempDir()
+ Expect(err).ToNot(HaveOccurred())
+ defer os.RemoveAll(contextDir)
+
+ Expect(ioutil.WriteFile(filepath.Join(contextDir, "expected"), contents.Bytes(), 0644)).
+ ToNot(HaveOccurred())
+
+ subdirPath := filepath.Join(contextDir, "subdir")
+ Expect(os.MkdirAll(subdirPath, 0755)).ToNot(HaveOccurred())
+ Expect(ioutil.WriteFile(filepath.Join(subdirPath, "extra"), contents.Bytes(), 0644)).
+ ToNot(HaveOccurred())
+ randomFile := filepath.Join(subdirPath, "randomFile")
+ dd := exec.Command("dd", "if=/dev/random", "of="+randomFile, "bs=1G", "count=1")
+ ddSession, err := Start(dd, GinkgoWriter, GinkgoWriter)
+ Expect(err).ToNot(HaveOccurred())
+ Eventually(ddSession).Should(Exit(0))
+
+ // make cwd as context root path
+ Expect(os.Chdir(contextDir)).ToNot(HaveOccurred())
+ defer os.Chdir(cwd)
+
+ By("Test .containerignore filtering subdirectory")
+ err = ioutil.WriteFile(filepath.Join(contextDir, ".containerignore"), []byte(`subdir/`), 0644)
+ Expect(err).ToNot(HaveOccurred())
+
+ session := podmanTest.Podman([]string{"build", "-f", containerfile, contextDir})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(Exit(0))
+
+ output := session.OutputToString()
+ Expect(output).To(ContainSubstring("Containerfile"))
+ Expect(output).To(ContainSubstring("/testfilter/expected"))
+ Expect(output).NotTo(ContainSubstring("subdir"))
+ })
+
It("podman remote test context dir contains empty dirs and symlinks", func() {
- if IsRemote() {
- podmanTest.StopRemoteService()
- podmanTest.StartRemoteService()
- } else {
- Skip("Only valid at remote test")
- }
- // Given
+ SkipIfNotRemote("Testing remote contextDir empty")
+ podmanTest.RestartRemoteService()
+
// Switch to temp dir and restore it afterwards
cwd, err := os.Getwd()
Expect(err).To(BeNil())
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index cb6574f23..620494b34 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -20,6 +20,7 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/inspect"
"github.com/containers/podman/v4/pkg/rootless"
+ "github.com/containers/podman/v4/pkg/util"
. "github.com/containers/podman/v4/test/utils"
"github.com/containers/storage"
"github.com/containers/storage/pkg/reexec"
@@ -500,14 +501,12 @@ func (p *PodmanTestIntegration) BuildImageWithLabel(dockerfile, imageName string
// PodmanPID execs podman and returns its PID
func (p *PodmanTestIntegration) PodmanPID(args []string) (*PodmanSessionIntegration, int) {
podmanOptions := p.MakeOptions(args, false, false)
- if p.RemoteTest {
- podmanOptions = append([]string{"--remote", "--url", p.RemoteSocket}, podmanOptions...)
- }
fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
+
command := exec.Command(p.PodmanBinary, podmanOptions...)
session, err := Start(command, GinkgoWriter, GinkgoWriter)
if err != nil {
- Fail(fmt.Sprintf("unable to run podman command: %s", strings.Join(podmanOptions, " ")))
+ Fail("unable to run podman command: " + strings.Join(podmanOptions, " "))
}
podmanSession := &PodmanSession{Session: session}
return &PodmanSessionIntegration{podmanSession}, command.Process.Pid
@@ -843,11 +842,13 @@ func (p *PodmanTestIntegration) PodmanNoEvents(args []string) *PodmanSessionInte
// MakeOptions assembles all the podman main options
func (p *PodmanTestIntegration) makeOptions(args []string, noEvents, noCache bool) []string {
if p.RemoteTest {
+ if !util.StringInSlice("--remote", args) {
+ return append([]string{"--remote", "--url", p.RemoteSocket}, args...)
+ }
return args
}
- var (
- debug string
- )
+
+ var debug string
if _, ok := os.LookupEnv("DEBUG"); ok {
debug = "--log-level=debug --syslog=true "
}
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index b4a59c54d..dddcf5c14 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "bytes"
"errors"
"fmt"
"io/ioutil"
@@ -25,31 +24,28 @@ func IsRemote() bool {
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
- var remoteArgs = []string{"--remote", "--url", p.RemoteSocket}
- remoteArgs = append(remoteArgs, args...)
- podmanSession := p.PodmanBase(remoteArgs, false, false)
+ args = p.makeOptions(args, false, false)
+ podmanSession := p.PodmanBase(args, false, false)
return &PodmanSessionIntegration{podmanSession}
}
// PodmanSystemdScope runs the podman command in a new systemd scope
func (p *PodmanTestIntegration) PodmanSystemdScope(args []string) *PodmanSessionIntegration {
- var remoteArgs = []string{"--remote", "--url", p.RemoteSocket}
- remoteArgs = append(remoteArgs, args...)
+ args = p.makeOptions(args, false, false)
wrapper := []string{"systemd-run", "--scope"}
if rootless.IsRootless() {
wrapper = []string{"systemd-run", "--scope", "--user"}
}
- podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, wrapper, nil)
+ podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, wrapper, nil)
return &PodmanSessionIntegration{podmanSession}
}
// PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files
func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration {
- var remoteArgs = []string{"--remote", "--url", p.RemoteSocket}
- remoteArgs = append(remoteArgs, args...)
- podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, nil, extraFiles)
+ args = p.makeOptions(args, false, false)
+ podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, nil, extraFiles)
return &PodmanSessionIntegration{podmanSession}
}
@@ -96,57 +92,39 @@ func (p *PodmanTestIntegration) StartRemoteService() {
command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
p.RemoteCommand = command
p.RemoteSession = command.Process
- err := p.DelayForService()
- p.RemoteStartErr = err
+ p.RemoteStartErr = p.DelayForService()
}
func (p *PodmanTestIntegration) StopRemoteService() {
- var out bytes.Buffer
- var pids []int
- remoteSession := p.RemoteSession
-
if !rootless.IsRootless() {
- if err := remoteSession.Kill(); err != nil {
+ if err := p.RemoteSession.Kill(); err != nil {
fmt.Fprintf(os.Stderr, "error on remote stop-kill %q", err)
}
- if _, err := remoteSession.Wait(); err != nil {
+ if _, err := p.RemoteSession.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "error on remote stop-wait %q", err)
}
} else {
- parentPid := fmt.Sprintf("%d", p.RemoteSession.Pid)
- pgrep := exec.Command("pgrep", "-P", parentPid)
- fmt.Printf("running: pgrep %s\n", parentPid)
- pgrep.Stdout = &out
- err := pgrep.Run()
- if err != nil {
- fmt.Fprint(os.Stderr, "unable to find remote pid")
- }
-
- for _, s := range strings.Split(out.String(), "\n") {
- if len(s) == 0 {
- continue
- }
- p, err := strconv.Atoi(s)
- if err != nil {
- fmt.Fprintf(os.Stderr, "unable to convert %s to int", s)
- }
- if p != 0 {
- pids = append(pids, p)
+ // Stop any children of `podman system service`
+ pkill := exec.Command("pkill", "-P", strconv.Itoa(p.RemoteSession.Pid), "-15")
+ if err := pkill.Run(); err != nil {
+ exitErr := err.(*exec.ExitError)
+ if exitErr.ExitCode() != 1 {
+ fmt.Fprintf(os.Stderr, "pkill unable to clean up service %d children, exit code %d\n",
+ p.RemoteSession.Pid, exitErr.ExitCode())
}
}
-
- pids = append(pids, p.RemoteSession.Pid)
- for _, pid := range pids {
- syscall.Kill(pid, syscall.SIGKILL)
+ if err := p.RemoteSession.Kill(); err != nil {
+ fmt.Fprintf(os.Stderr, "unable to clean up service %d, %v\n", p.RemoteSession.Pid, err)
}
}
+
socket := strings.Split(p.RemoteSocket, ":")[1]
- if err := os.Remove(socket); err != nil {
- fmt.Println(err)
+ if err := os.Remove(socket); err != nil && !errors.Is(err, os.ErrNotExist) {
+ fmt.Fprintf(os.Stderr, "%v\n", err)
}
if p.RemoteSocketLock != "" {
- if err := os.Remove(p.RemoteSocketLock); err != nil {
- fmt.Println(err)
+ if err := os.Remove(p.RemoteSocketLock); err != nil && !errors.Is(err, os.ErrNotExist) {
+ fmt.Fprintf(os.Stderr, "%v\n", err)
}
}
}
@@ -185,8 +163,9 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
}
func (p *PodmanTestIntegration) DelayForService() error {
+ var session *PodmanSessionIntegration
for i := 0; i < 5; i++ {
- session := p.Podman([]string{"info"})
+ session = p.Podman([]string{"info"})
session.WaitWithDefaultTimeout()
if session.ExitCode() == 0 {
return nil
@@ -195,5 +174,5 @@ func (p *PodmanTestIntegration) DelayForService() error {
}
time.Sleep(2 * time.Second)
}
- return errors.New("Service not detected")
+ return fmt.Errorf("service not detected, exit code(%d)", session.ExitCode())
}
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index cb795438d..934a306ce 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -443,4 +443,27 @@ var _ = Describe("Podman logs", func() {
Expect(output).To(ContainElement(ContainSubstring(containerName1)))
Expect(output).To(ContainElement(ContainSubstring(containerName2)))
})
+ It("podman pod logs with different colors", func() {
+ SkipIfRemote("Remote can only process one container at a time")
+ SkipIfInContainer("journalctl inside a container doesn't work correctly")
+ podName := "testPod"
+ containerName1 := "container1"
+ containerName2 := "container2"
+ testPod := podmanTest.Podman([]string{"pod", "create", fmt.Sprintf("--name=%s", podName)})
+ testPod.WaitWithDefaultTimeout()
+ Expect(testPod).To(Exit(0))
+ log1 := podmanTest.Podman([]string{"run", "--name", containerName1, "-d", "--pod", podName, BB, "/bin/sh", "-c", "echo log1"})
+ log1.WaitWithDefaultTimeout()
+ Expect(log1).To(Exit(0))
+ log2 := podmanTest.Podman([]string{"run", "--name", containerName2, "-d", "--pod", podName, BB, "/bin/sh", "-c", "echo log2"})
+ log2.WaitWithDefaultTimeout()
+ Expect(log2).To(Exit(0))
+ results := podmanTest.Podman([]string{"pod", "logs", "--color", podName})
+ results.WaitWithDefaultTimeout()
+ Expect(results).To(Exit(0))
+ output := results.OutputToStringArray()
+ Expect(output).To(HaveLen(2))
+ Expect(output[0]).To(MatchRegexp(`\x1b\[3[0-9a-z ]+\x1b\[0m`))
+ Expect(output[1]).To(MatchRegexp(`\x1b\[3[0-9a-z ]+\x1b\[0m`))
+ })
})
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 6a4083565..f16180854 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -1679,6 +1679,32 @@ var _ = Describe("Podman play kube", func() {
Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`))
})
+ It("podman play kube test env value from configmap and --replace should reuse the configmap volume", func() {
+ SkipIfRemote("configmap list is not supported as a param")
+ cmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml")
+ cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo"))
+ err := generateKubeYaml("configmap", cm, cmYamlPathname)
+ Expect(err).To(BeNil())
+
+ pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "FOO", false))))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube).Should(Exit(0))
+
+ // create pod again with --replace
+ kube = podmanTest.Podman([]string{"play", "kube", "--replace", kubeYaml, "--configmap", cmYamlPathname})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube).Should(Exit(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).Should(Exit(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`))
+ })
+
It("podman play kube test required env value from configmap with missing key", func() {
SkipIfRemote("configmap list is not supported as a param")
cmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml")
diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go
index 04e8cfd07..8def80213 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -874,6 +874,10 @@ ENTRYPOINT ["sleep","99999"]
ctr3 := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/tmp1/test"})
ctr3.WaitWithDefaultTimeout()
Expect(ctr3.OutputToString()).To(ContainSubstring("hello"))
+
+ ctr4 := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "touch", "/tmp1/testing.txt"})
+ ctr4.WaitWithDefaultTimeout()
+ Expect(ctr4).Should(Exit(0))
})
It("podman pod create --device", func() {
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 1a93296b7..4c399dc2b 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -535,6 +535,11 @@ var _ = Describe("Podman run", func() {
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
+ session = podmanTest.Podman([]string{"run", "--user=1:1", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapEff", "/proc/self/status"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
+
if os.Geteuid() > 0 {
if os.Getenv("SKIP_USERNS") != "" {
Skip("Skip userns tests.")
@@ -609,6 +614,13 @@ USER bin`, BB)
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(Equal("111"))
+
+ currentOOMScoreAdj, err := ioutil.ReadFile("/proc/self/oom_score_adj")
+ Expect(err).To(BeNil())
+ session = podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "cat", "/proc/self/oom_score_adj"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(Equal(strings.TrimRight(string(currentOOMScoreAdj), "\n")))
})
It("podman run limits host test", func() {
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index d23c5dc14..471b3a342 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -778,6 +778,13 @@ VOLUME /test/`, ALPINE)
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).Should(Equal("888:888"))
+ // anonymous volume mount
+ vol = "type=volume," + "dst=" + dest
+ session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).Should(Equal("888:888"))
+
// named volume mount
namedVolume := podmanTest.Podman([]string{"volume", "create", "foo"})
namedVolume.WaitWithDefaultTimeout()
diff --git a/test/e2e/unshare_test.go b/test/e2e/unshare_test.go
index ac4fa46bf..8b06dd4f5 100644
--- a/test/e2e/unshare_test.go
+++ b/test/e2e/unshare_test.go
@@ -16,7 +16,6 @@ var _ = Describe("Podman unshare", func() {
podmanTest *PodmanTestIntegration
)
BeforeEach(func() {
- SkipIfRemote("podman-remote unshare is not supported")
if _, err := os.Stat("/proc/self/uid_map"); err != nil {
Skip("User namespaces not supported.")
}
@@ -43,6 +42,7 @@ var _ = Describe("Podman unshare", func() {
})
It("podman unshare", func() {
+ SkipIfRemote("podman-remote unshare is not supported")
userNS, _ := os.Readlink("/proc/self/ns/user")
session := podmanTest.Podman([]string{"unshare", "readlink", "/proc/self/ns/user"})
session.WaitWithDefaultTimeout()
@@ -50,7 +50,8 @@ var _ = Describe("Podman unshare", func() {
Expect(session.OutputToString()).ToNot(ContainSubstring(userNS))
})
- It("podman unshare --rootles-cni", func() {
+ It("podman unshare --rootless-cni", func() {
+ SkipIfRemote("podman-remote unshare is not supported")
session := podmanTest.Podman([]string{"unshare", "--rootless-netns", "ip", "addr"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
@@ -58,6 +59,7 @@ var _ = Describe("Podman unshare", func() {
})
It("podman unshare exit codes", func() {
+ SkipIfRemote("podman-remote unshare is not supported")
session := podmanTest.Podman([]string{"unshare", "false"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(1))
@@ -88,4 +90,12 @@ var _ = Describe("Podman unshare", func() {
Expect(session.OutputToString()).Should(Equal(""))
Expect(session.ErrorToString()).Should(ContainSubstring("unknown flag: --bogus"))
})
+
+ It("podman unshare check remote error", func() {
+ SkipIfNotRemote("check for podman-remote unshare error")
+ session := podmanTest.Podman([]string{"unshare"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(Equal(`Error: cannot use command "podman-remote unshare" with the remote podman client`))
+ })
})
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index 6f93beff6..a30db80eb 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -87,10 +87,18 @@ var _ = Describe("Podman version", func() {
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
+ session = podmanTest.Podman([]string{"version", "--format", "{{ .Client.Os }}"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
session = podmanTest.Podman([]string{"version", "--format", "{{ .Server.Version }}"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
+ session = podmanTest.Podman([]string{"version", "--format", "{{ .Server.Os }}"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
session = podmanTest.Podman([]string{"version", "--format", "{{ .Version }}"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index ec85ef166..72e4a2bc8 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -815,4 +815,10 @@ EOF
run_podman run --uidmap 0:10001:10002 --rm --hostname ${HOST} $IMAGE grep ${HOST} /etc/hosts
is "${lines[0]}" ".*${HOST}.*"
}
+
+@test "podman run doesn't override oom-score-adj" {
+ current_oom_score_adj=$(cat /proc/self/oom_score_adj)
+ run_podman run --rm $IMAGE cat /proc/self/oom_score_adj
+ is "$output" "$current_oom_score_adj" "different oom_score_adj in the container"
+}
# vim: filetype=sh
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index a95561635..78ad3fe04 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -632,4 +632,25 @@ EOF
is "$output" ".*nameserver $subnet.1.*" "integrated dns nameserver is set"
}
+@test "podman run port forward range" {
+ for netmode in bridge slirp4netns:port_handler=slirp4netns slirp4netns:port_handler=rootlesskit; do
+ local port=$(random_free_port)
+ local end_port=$(( $port + 2 ))
+ local range="$port-$end_port:$port-$end_port"
+ local random=$(random_string)
+
+ run_podman run --network $netmode -p "$range" -d $IMAGE sleep inf
+ cid="$output"
+ for port in $(seq $port $end_port); do
+ run_podman exec -d $cid nc -l -p $port -e /bin/cat
+ # -w 1 adds a 1 second timeout, for some reason ubuntus ncat doesn't close the connection on EOF,
+ # other options to change this are not portable across distros but -w seems to work
+ run nc -w 1 127.0.0.1 $port <<<$random
+ is "$output" "$random" "ncat got data back (netmode=$netmode port=$port)"
+ done
+
+ run_podman rm -f -t0 $cid
+ done
+}
+
# vim: filetype=sh
diff --git a/test/upgrade/test-upgrade.bats b/test/upgrade/test-upgrade.bats
index 58e4fe0a3..198d8a169 100644
--- a/test/upgrade/test-upgrade.bats
+++ b/test/upgrade/test-upgrade.bats
@@ -75,6 +75,16 @@ setup() {
cat >| $pmscript <<EOF
#!/bin/bash
+#
+# Argh! podman >= 3.4 something something namespace something, fails with
+# Error: invalid config provided: cannot set hostname when running in the host UTS namespace: invalid configuration
+#
+# https://github.com/containers/podman/issues/11969#issuecomment-943386484
+#
+if grep -q utsns /etc/containers/containers.conf; then
+ sed -i -e '/^\utsns=/d' /etc/containers/containers.conf
+fi
+
# events-backend=journald does not work inside a container
opts="--events-backend=file $_PODMAN_TEST_OPTS"
@@ -105,7 +115,7 @@ podman \$opts run -d --name myrunningcontainer --label mylabel=$LABEL_RUNNING \
podman \$opts pod create --name mypod
-podman \$opts network create mynetwork
+podman \$opts network create --disable-dns mynetwork
echo READY
while :;do
@@ -113,7 +123,10 @@ while :;do
echo STOPPING
podman \$opts stop -t 0 myrunningcontainer || true
podman \$opts rm -f myrunningcontainer || true
- podman \$opts network rm -f mynetwork
+ # sigh, network rm fails with exec: "ip": executable file not found in $PATH
+ # we cannot change the images afterwards so we remove it manually (#11403)
+ # hardcode /etc/cni/net.d dir for now
+ podman \$opts network rm -f mynetwork || rm -f /etc/cni/net.d/mynetwork.conflist
exit 0
fi
sleep 0.5
@@ -124,17 +137,14 @@ EOF
# Clean up vestiges of previous run
$PODMAN rm -f podman_parent || true
-
- local netname=testnet-$(random_string 10)
- $PODMAN network create $netname
-
# Not entirely a NOP! This is just so we get the /run/... mount points created on a CI VM
- # --mac-address is needed to create /run/cni, --network is needed to create /run/containers for dnsname
- $PODMAN run --rm --mac-address 78:28:a6:8d:24:8a --network $netname $OLD_PODMAN true
- $PODMAN network rm -f $netname
+ # Also use --network host to prevent any netavark/cni conflicts
+ $PODMAN run --rm --network host $OLD_PODMAN true
# Podman 4.0 might no longer use cni so /run/cni and /run/containers will no be created in this case
- mkdir -p /run/cni /run/containers
+ # Create directories manually to fix this. Also running with netavark can
+ # cause connectivity issues since cni and netavark should never be mixed.
+ mkdir -p /run/netns /run/cni /run/containers /var/lib/cni /etc/cni/net.d
#
@@ -242,6 +252,8 @@ failed | exited | 17
# if we can connect on an existing running container
@test "network - connect" {
skip_if_version_older 2.2.0
+ touch $PODMAN_UPGRADE_WORKDIR/ran-network-connect-test
+
run_podman network connect mynetwork myrunningcontainer
run_podman network disconnect podman myrunningcontainer
run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt
@@ -250,7 +262,26 @@ failed | exited | 17
@test "network - restart" {
# restart the container and check if we can still use the port
+
+ # https://github.com/containers/podman/issues/13679
+ # The upgrade to podman4 changes the network db format.
+ # While it is compatible from 3.X to 4.0 it will fail the other way around.
+ # This can be the case when the cleanup process runs before the stop process
+ # can do the cleanup.
+
+ # Since there is no easy way to fix this and downgrading is not something
+ # we support, just fix this bug in the tests by manually calling
+ # network disconnect to teardown the netns.
+ if test -e $PODMAN_UPGRADE_WORKDIR/ran-network-connect-test; then
+ run_podman network disconnect mynetwork myrunningcontainer
+ fi
+
run_podman stop -t0 myrunningcontainer
+
+ # now connect again, do this before starting the container
+ if test -e $PODMAN_UPGRADE_WORKDIR/ran-network-connect-test; then
+ run_podman network connect mynetwork myrunningcontainer
+ fi
run_podman start myrunningcontainer
run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt
is "$output" "$RANDOM_STRING_1" "curl on restarted container"
diff --git a/test/utils/utils.go b/test/utils/utils.go
index a6295cd19..57f002130 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -100,9 +100,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string
if p.NetworkBackend == Netavark {
runCmd = append(runCmd, []string{"--network-backend", "netavark"}...)
}
- if p.RemoteTest {
- podmanOptions = append([]string{"--remote", "--url", p.RemoteSocket}, podmanOptions...)
- }
+
if env == nil {
fmt.Printf("Running: %s %s\n", strings.Join(runCmd, " "), strings.Join(podmanOptions, " "))
} else {