mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-10-22 05:47:35 +00:00
commit
4808a63e5f
91 changed files with 850 additions and 309 deletions
9
Makefile
9
Makefile
|
|
@ -59,10 +59,11 @@ PKG_MANAGER ?= $(shell command -v dnf yum|head -n1)
|
|||
PRE_COMMIT = $(shell command -v bin/venv/bin/pre-commit ~/.local/bin/pre-commit pre-commit | head -n1)
|
||||
|
||||
# This isn't what we actually build; it's a superset, used for target
|
||||
# dependencies. Basically: all *.go files, except *_test.go, and except
|
||||
# anything in a dot subdirectory. If any of these files is newer than
|
||||
# our target (bin/podman{,-remote}), a rebuild is triggered.
|
||||
SOURCES = $(shell find . -path './.*' -prune -o \( -name '*.go' -a ! -name '*_test.go' \) -print)
|
||||
# dependencies. Basically: all *.go and *.c files, except *_test.go,
|
||||
# and except anything in a dot subdirectory. If any of these files is
|
||||
# newer than our target (bin/podman{,-remote}), a rebuild is
|
||||
# triggered.
|
||||
SOURCES = $(shell find . -path './.*' -prune -o \( \( -name '*.go' -o -name '*.c' \) -a ! -name '*_test.go' \) -print)
|
||||
|
||||
BUILDFLAGS := -mod=vendor $(BUILDFLAGS)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,24 @@
|
|||
# Release Notes
|
||||
|
||||
## 3.4.2
|
||||
### Bugfixes
|
||||
- Fixed a bug where `podman tag` could not tag manifest lists ([#12046](https://github.com/containers/podman/issues/12046)).
|
||||
- Fixed a bug where built-in volumes specified by images would not be created correctly under some circumstances.
|
||||
- Fixed a bug where, when using Podman Machine on OS X, containers in pods did not have working port forwarding from the host ([#12207](https://github.com/containers/podman/issues/12207)).
|
||||
- Fixed a bug where the `podman network reload` command command on containers using the `slirp4netns` network mode and the `rootlessport` port forwarding driver would make an unnecessary attempt to restart `rootlessport` on containers that did not forward ports.
|
||||
- Fixed a bug where the `podman generate kube` command would generate YAML including some unnecessary (set to default) fields (e.g. empty SELinux and DNS configuration blocks, and the `privileged` flag when set to false) ([#11995](https://github.com/containers/podman/issues/11995)).
|
||||
- Fixed a bug where the `podman pod rm` command could, if interrupted at the right moment, leave a reference to an already-removed infra container behind ([#12034](https://github.com/containers/podman/issues/12034)).
|
||||
- Fixed a bug where the `podman pod rm` command would not remove pods with more than one container if all containers save for the infra container were stopped unless `--force` was specified ([#11713](https://github.com/containers/podman/issues/11713)).
|
||||
- Fixed a bug where the `--memory` flag to `podman run` and `podman create` did not accept a limit of 0 (which should specify unlimited memory) ([#12002](https://github.com/containers/podman/issues/12002)).
|
||||
- Fixed a bug where the remote Podman client's `podman build` command could attempt to build a Dockerfile in the working directory of the `podman system service` instance instead of the Dockerfile specified by the user ([#12054](https://github.com/containers/podman/issues/12054)).
|
||||
- Fixed a bug where the `podman logs --tail` command could function improperly (printing more output than requested) when the `journald` log driver was used.
|
||||
- Fixed a bug where containers run using the `slirp4netns` network mode with IPv6 enabled would not have IPv6 connectivity until several seconds after they started ([#11062](https://github.com/containers/podman/issues/11062)).
|
||||
- Fixed a bug where some Podman commands could cause an extra `dbus-daemon` process to be created ([#9727](https://github.com/containers/podman/issues/9727)).
|
||||
- Fixed a bug where rootless Podman would sometimes print warnings about a failure to move the pause process into a given CGroup ([#12065](https://github.com/containers/podman/issues/12065)).
|
||||
- Fixed a bug where the `checkpointed` field in `podman inspect` on a container was not set to false after a container was restored.
|
||||
- Fixed a bug where the `podman system service` command would print overly-verbose logs about request IDs ([#12181](https://github.com/containers/podman/issues/12181)).
|
||||
- Fixed a bug where Podman could, when creating a new container without a name explicitly specified by the user, sometimes use an auto-generated name already in use by another container if multiple containers were being created in parallel ([#11735](https://github.com/containers/podman/issues/11735)).
|
||||
|
||||
## 3.4.1
|
||||
### Bugfixes
|
||||
- Fixed a bug where `podman machine init` could, under some circumstances, create invalid machine configurations which could not be started ([#11824](https://github.com/containers/podman/issues/11824)).
|
||||
|
|
@ -133,7 +152,7 @@
|
|||
### Features
|
||||
- Containers inside VMs created by `podman machine` will now automatically handle port forwarding - containers in `podman machine` VMs that publish ports via `--publish` or `--publish-all` will have these ports not just forwarded on the VM, but also on the host system.
|
||||
- The `podman play kube` command's `--network` option now accepts advanced network options (e.g. `--network slirp4netns:port_handler=slirp4netns`) ([#10807](https://github.com/containers/podman/issues/10807)).
|
||||
- The `podman play kube` commmand now supports Kubernetes liveness probes, which will be created as Podman healthchecks.
|
||||
- The `podman play kube` command now supports Kubernetes liveness probes, which will be created as Podman healthchecks.
|
||||
- Podman now provides a systemd unit, `podman-restart.service`, which, when enabled, will restart all containers that were started with `--restart=always` after the system reboots.
|
||||
- Rootless Podman can now be configured to use CNI networking by default by using the `rootless_networking` option in `containers.conf`.
|
||||
- Images can now be pulled using `image:tag@digest` syntax (e.g. `podman pull fedora:34@sha256:1b0d4ddd99b1a8c8a80e885aafe6034c95f266da44ead992aab388e6aa91611a`) ([#6721](https://github.com/containers/podman/issues/6721)).
|
||||
|
|
@ -389,7 +408,7 @@
|
|||
- Fixed a bug where images with empty layers were stored incorrectly, causing them to be unable to be pushed or saved.
|
||||
- Fixed a bug where the `podman rmi` command could fail to remove corrupt images from storage.
|
||||
- Fixed a bug where the remote Podman client's `podman save` command did not support the `oci-dir` and `docker-dir` formats ([#9742](https://github.com/containers/podman/issues/9742)).
|
||||
- Fixed a bug where volume mounts from `podman play kube` created with a trailing `/` in the container path were were not properly superceding named volumes from the image ([#9618](https://github.com/containers/podman/issues/9618)).
|
||||
- Fixed a bug where volume mounts from `podman play kube` created with a trailing `/` in the container path were were not properly superseding named volumes from the image ([#9618](https://github.com/containers/podman/issues/9618)).
|
||||
- Fixed a bug where Podman could fail to build on 32-bit architectures.
|
||||
|
||||
### Misc
|
||||
|
|
@ -1034,7 +1053,7 @@
|
|||
## 2.0.5
|
||||
### Features
|
||||
- Rootless Podman will now add an entry to `/etc/passwd` for the user who ran Podman if run with `--userns=keep-id`.
|
||||
- The `podman system connection` command has been reworked to support multiple connections, and reenabled for use!
|
||||
- The `podman system connection` command has been reworked to support multiple connections, and re-enabled for use!
|
||||
- Podman now has a new global flag, `--connection`, to specify a connection to a remote Podman API instance.
|
||||
|
||||
### Changes
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
createFlags.StringVar(
|
||||
&cf.Variant,
|
||||
variantFlagName, "",
|
||||
"Use _VARIANT_ instead of the running architecture variant for choosing images",
|
||||
"Use `VARIANT` instead of the running architecture variant for choosing images",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ func validateStart(cmd *cobra.Command, args []string) error {
|
|||
if len(args) == 0 && !startOptions.Latest && !startOptions.All {
|
||||
return errors.New("start requires at least one argument")
|
||||
}
|
||||
if startOptions.All && startOptions.Latest {
|
||||
return errors.Errorf("--all and --latest cannot be used together")
|
||||
}
|
||||
if len(args) > 0 && startOptions.Latest {
|
||||
return errors.Errorf("--latest and containers cannot be used together")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func pullFlags(cmd *cobra.Command) {
|
|||
_ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS)
|
||||
|
||||
variantFlagName := "variant"
|
||||
flags.StringVar(&pullOptions.Variant, variantFlagName, "", " use VARIANT instead of the running architecture variant for choosing images")
|
||||
flags.StringVar(&pullOptions.Variant, variantFlagName, "", "Use VARIANT instead of the running architecture variant for choosing images")
|
||||
_ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
|
||||
|
||||
platformFlagName := "platform"
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden strin
|
|||
return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
|
||||
}
|
||||
podman := os.Args[0]
|
||||
run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in thie case...
|
||||
run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in this case...
|
||||
_, err = connection.ExecRemoteCommand(dial, run)
|
||||
if err != nil {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ var (
|
|||
versionCommand = &cobra.Command{
|
||||
Use: "version [options]",
|
||||
Args: validate.NoArgs,
|
||||
Short: "Display the Podman Version Information",
|
||||
Short: "Display the Podman version information",
|
||||
RunE: version,
|
||||
ValidArgsFunction: completion.AutocompleteNone,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ setup_rootless() {
|
|||
local rootless_uid
|
||||
local rootless_gid
|
||||
local env_var_val
|
||||
local akfilepath
|
||||
local sshcmd
|
||||
|
||||
# Only do this once; established by setup_environment.sh
|
||||
# shellcheck disable=SC2154
|
||||
|
|
@ -169,24 +171,25 @@ setup_rootless() {
|
|||
ssh-keygen -P "" -f "$HOME/.ssh/id_rsa"
|
||||
|
||||
msg "Allowing ssh key for $ROOTLESS_USER"
|
||||
akfilepath="/home/$ROOTLESS_USER/.ssh/authorized_keys"
|
||||
(umask 077 && mkdir "/home/$ROOTLESS_USER/.ssh")
|
||||
chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh"
|
||||
install -o $ROOTLESS_USER -g $ROOTLESS_USER -m 0600 \
|
||||
"$HOME/.ssh/id_rsa.pub" "/home/$ROOTLESS_USER/.ssh/authorized_keys"
|
||||
"$HOME/.ssh/id_rsa.pub" "$akfilepath"
|
||||
# Makes debugging easier
|
||||
cat /root/.ssh/authorized_keys >> "/home/$ROOTLESS_USER/.ssh/authorized_keys"
|
||||
|
||||
msg "Configuring subuid and subgid"
|
||||
grep -q "${ROOTLESS_USER}" /etc/subuid || \
|
||||
echo "${ROOTLESS_USER}:$[rootless_uid * 100]:65536" | \
|
||||
tee -a /etc/subuid >> /etc/subgid
|
||||
cat /root/.ssh/authorized_keys >> "$akfilepath"
|
||||
|
||||
msg "Ensure the ssh daemon is up and running within 5 minutes"
|
||||
systemctl start sshd
|
||||
lilto ssh $ROOTLESS_USER@localhost \
|
||||
-o UserKnownHostsFile=/dev/null \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o CheckHostIP=no true
|
||||
sshcmd="ssh $ROOTLESS_USER@localhost
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o StrictHostKeyChecking=no
|
||||
-o CheckHostIP=no"
|
||||
lilto $sshcmd true # retry until sshd is up
|
||||
|
||||
msg "Configuring rootless user self-access to ssh to localhost"
|
||||
$sshcmd ssh-keygen -P '""' -f "/home/$ROOTLESS_USER/.ssh/id_rsa"
|
||||
cat "/home/$ROOTLESS_USER/.ssh/id_rsa" >> "$akfilepath"
|
||||
}
|
||||
|
||||
install_test_configs() {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@ fi
|
|||
if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.NEW.TESTS.NEEDED ]]; then
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.TESTS.NEEDED ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# HEAD should be good enough, but the CIRRUS envariable allows us to test
|
||||
head=${CIRRUS_CHANGE_IN_REPO:-HEAD}
|
||||
|
|
@ -52,14 +49,11 @@ if [[ -z "$filtered_changes" ]]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
# One last chance: perhaps the developer included the magic '[NO (NEW) TESTS NEEDED]'
|
||||
# One last chance: perhaps the developer included the magic '[NO NEW TESTS NEEDED]'
|
||||
# string in an amended commit.
|
||||
if git log --format=%B ${base}..${head} | fgrep '[NO NEW TESTS NEEDED]'; then
|
||||
exit 0
|
||||
fi
|
||||
if git log --format=%B ${base}..${head} | fgrep '[NO TESTS NEEDED]'; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
$(basename $0): PR does not include changes in the 'tests' directory
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@ RUN useradd podman; \
|
|||
echo podman:10000:5000 > /etc/subuid; \
|
||||
echo podman:10000:5000 > /etc/subgid;
|
||||
|
||||
VOLUME /var/lib/containers
|
||||
VOLUME /home/podman/.local/share/containers
|
||||
RUN mkdir -p /home/podman/.local/share/containers
|
||||
RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
|
||||
|
||||
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
|
||||
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
|
||||
|
||||
RUN chown podman:podman -R /home/podman
|
||||
# Note VOLUME options must always happen after the chown call above
|
||||
# RUN commands can not modify existing volumes
|
||||
VOLUME /var/lib/containers
|
||||
VOLUME /home/podman/.local/share/containers
|
||||
|
||||
# chmod containers.conf and adjust storage.conf to enable Fuse storage.
|
||||
RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@ RUN useradd podman; \
|
|||
echo podman:10000:5000 > /etc/subuid; \
|
||||
echo podman:10000:5000 > /etc/subgid;
|
||||
|
||||
VOLUME /var/lib/containers
|
||||
VOLUME /home/podman/.local/share/containers
|
||||
RUN mkdir -p /home/podman/.local/share/containers
|
||||
RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
|
||||
|
||||
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
|
||||
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
|
||||
|
||||
RUN chown podman:podman -R /home/podman
|
||||
# Note VOLUME options must always happen after the chown call above
|
||||
# RUN commands can not modify existing volumes
|
||||
VOLUME /var/lib/containers
|
||||
VOLUME /home/podman/.local/share/containers
|
||||
|
||||
# chmod containers.conf and adjust storage.conf to enable Fuse storage.
|
||||
RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
|
||||
|
|
|
|||
|
|
@ -68,14 +68,15 @@ RUN useradd podman; \
|
|||
echo podman:10000:5000 > /etc/subuid; \
|
||||
echo podman:10000:5000 > /etc/subgid;
|
||||
|
||||
VOLUME /var/lib/containers
|
||||
VOLUME /home/podman/.local/share/containers
|
||||
RUN mkdir -p /home/podman/.local/share/containers
|
||||
RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
|
||||
|
||||
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
|
||||
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf
|
||||
|
||||
RUN chown podman:podman -R /home/podman
|
||||
# Note VOLUME options must always happen after the chown call above
|
||||
# RUN commands can not modify existing volumes
|
||||
VOLUME /var/lib/containers
|
||||
VOLUME /home/podman/.local/share/containers
|
||||
|
||||
# chmod containers.conf and adjust storage.conf to enable Fuse storage.
|
||||
RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Epoch: 99
|
|||
%else
|
||||
Epoch: 0
|
||||
%endif
|
||||
Version: 3.4.2
|
||||
Version: 3.4.3
|
||||
Release: #COMMITDATE#.git%{shortcommit0}%{?dist}
|
||||
Summary: Manage Pods, Containers and Container Images
|
||||
License: ASL 2.0
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ StartLimitIntervalSec=0
|
|||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
Environment=LOGGING="--log-level=info"
|
||||
ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target default.target
|
||||
|
|
|
|||
|
|
@ -631,7 +631,7 @@ Sets the configuration for user namespaces when handling `RUN` instructions.
|
|||
The configured value can be "" (the empty string) or "container" to indicate
|
||||
that a new user namespace should be created, it can be "host" to indicate that
|
||||
the user namespace in which `podman` itself is being run should be reused, or
|
||||
it can be the path to an user namespace which is already in use by another
|
||||
it can be the path to a user namespace which is already in use by another
|
||||
process.
|
||||
|
||||
#### **--userns-uid-map**=*mapping*
|
||||
|
|
@ -774,6 +774,14 @@ content label. Shared volume labels allow all containers to read/write content.
|
|||
The `Z` option tells Podman to label the content with a private unshared label.
|
||||
Only the current container can use a private volume.
|
||||
|
||||
Note: Do not relabel system files and directories. Relabeling system content
|
||||
might cause other confined services on your machine to fail. For these types
|
||||
of containers, disabling SELinux separation is recommended. The option
|
||||
`--security-opt label=disable` disables SELinux separation for the container.
|
||||
For example, if a user wanted to volume mount their entire home directory into the build containers, they need to disable SELinux separation.
|
||||
|
||||
$ podman build --security-opt label=disable -v $HOME:/home/user .
|
||||
|
||||
`Overlay Volume Mounts`
|
||||
|
||||
The `:O` flag tells Podman to mount the directory from the host as a
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Shell completion needs to be already enabled in the environment. The following c
|
|||
**echo "autoload -U compinit; compinit" >> ~/.zshrc**
|
||||
|
||||
To make it available for all zsh sessions run:\
|
||||
**podman completion -f "${fpath[1]}/_podman zsh"**
|
||||
**podman completion -f "${fpath[1]}/_podman" zsh**
|
||||
|
||||
Once the shell is reloaded the auto-completion should be working.
|
||||
|
||||
|
|
|
|||
|
|
@ -1231,6 +1231,15 @@ content label. Shared volume labels allow all containers to read/write content.
|
|||
The `Z` option tells Podman to label the content with a private unshared label.
|
||||
Only the current container can use a private volume.
|
||||
|
||||
Note: Do not relabel system files and directories. Relabeling system content
|
||||
might cause other confined services on your machine to fail. For these types
|
||||
of containers we recommend that disable SELinux separation. The option
|
||||
`--security-opt label=disable` disables SELinux separation for containers used in the build.
|
||||
For example if a user wanted to volume mount their entire home directory into a
|
||||
container, they need to disable SELinux separation.
|
||||
|
||||
$ podman create --security-opt label=disable -v $HOME:/home/user fedora touch /home/user/file
|
||||
|
||||
`Overlay Volume Mounts`
|
||||
|
||||
The `:O` flag tells Podman to mount the directory from the host as a
|
||||
|
|
@ -1453,7 +1462,7 @@ Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released.
|
|||
In order for users to run rootless, there must be an entry for their username in /etc/subuid and /etc/subgid which lists the UIDs for their user namespace.
|
||||
|
||||
Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed.
|
||||
The fuse-overlay package provides a userspace overlay storage driver, otherwise users need to use
|
||||
The fuse-overlayfs package provides a userspace overlay storage driver, otherwise users need to use
|
||||
the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
|
||||
required for VPN, without it containers need to be run with the --network=host flag.
|
||||
|
||||
|
|
|
|||
|
|
@ -54,13 +54,7 @@ spec:
|
|||
- docker-entrypoint.sh
|
||||
- mysqld
|
||||
env:
|
||||
- name: PATH
|
||||
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
- name: TERM
|
||||
value: xterm
|
||||
- name: HOSTNAME
|
||||
- name: container
|
||||
value: podman
|
||||
- name: GOSU_VERSION
|
||||
value: "1.10"
|
||||
- name: GPG_KEYS
|
||||
|
|
@ -77,14 +71,14 @@ spec:
|
|||
ports:
|
||||
- containerPort: 3306
|
||||
hostPort: 36533
|
||||
protocol: TCP
|
||||
resources: {}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
capabilities:
|
||||
drop:
|
||||
- CAP_MKNOD
|
||||
- CAP_NET_RAW
|
||||
- CAP_AUDIT_WRITE
|
||||
tty: true
|
||||
workingDir: /
|
||||
status: {}
|
||||
```
|
||||
|
||||
|
|
@ -106,31 +100,18 @@ spec:
|
|||
containers:
|
||||
- command:
|
||||
- /bin/sh
|
||||
env:
|
||||
- name: PATH
|
||||
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
- name: TERM
|
||||
value: xterm
|
||||
- name: container
|
||||
value: podman
|
||||
image: docker.io/library/alpine:latest
|
||||
name: test-bind-mount
|
||||
resources: {}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
capabilities:
|
||||
drop:
|
||||
- CAP_MKNOD
|
||||
- CAP_NET_RAW
|
||||
- CAP_AUDIT_WRITE
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
seLinuxOptions: {}
|
||||
volumeMounts:
|
||||
- mountPath: /volume
|
||||
name: home-user-my-data-host
|
||||
workingDir: /
|
||||
dnsConfig: {}
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- hostPath:
|
||||
|
|
@ -158,31 +139,18 @@ spec:
|
|||
containers:
|
||||
- command:
|
||||
- /bin/sh
|
||||
env:
|
||||
- name: PATH
|
||||
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
- name: TERM
|
||||
value: xterm
|
||||
- name: container
|
||||
value: podman
|
||||
image: docker.io/library/alpine:latest
|
||||
name: test-bind-mount
|
||||
resources: {}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
capabilities:
|
||||
drop:
|
||||
- CAP_MKNOD
|
||||
- CAP_NET_RAW
|
||||
- CAP_AUDIT_WRITE
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
seLinuxOptions: {}
|
||||
volumeMounts:
|
||||
- mountPath: /volume
|
||||
name: priceless-data-pvc
|
||||
workingDir: /
|
||||
dnsConfig: {}
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: priceless-data-pvc
|
||||
|
|
@ -210,22 +178,9 @@ spec:
|
|||
- command:
|
||||
- python3
|
||||
- /root/code/graph.py
|
||||
env:
|
||||
- name: PATH
|
||||
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
- name: TERM
|
||||
value: xterm
|
||||
- name: HOSTNAME
|
||||
- name: container
|
||||
value: podman
|
||||
image: quay.io/baude/demoweb:latest
|
||||
name: practicalarchimedes
|
||||
resources: {}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
capabilities: {}
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
tty: true
|
||||
workingDir: /root/code
|
||||
status: {}
|
||||
|
|
@ -242,7 +197,6 @@ spec:
|
|||
- name: "8050"
|
||||
nodePort: 31269
|
||||
port: 8050
|
||||
protocol: TCP
|
||||
targetPort: 0
|
||||
selector:
|
||||
app: demoweb
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ tied to the Linux kernel.
|
|||
SSH keys are automatically generated to access the VM, and system connections to the root account
|
||||
and a user account inside the VM are added.
|
||||
|
||||
By default, the VM distribution is [Fedora CoreOS](https://getfedora.org/en/coreos?stream=testing).
|
||||
Fedora CoreOS upgrades come out every 14 days and are detected and installed automatically. The VM will be rebooted during the upgrade.
|
||||
For more information on updates and advanced configuration, please see the FCOS update docs [here](https://docs.fedoraproject.org/en-US/fedora-coreos/auto-updates/) and [here](https://coreos.github.io/zincati/usage/updates-strategy/).
|
||||
|
||||
## OPTIONS
|
||||
|
||||
#### **--cpus**=*number*
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ podman\-machine\-ssh - SSH into a virtual machine
|
|||
## DESCRIPTION
|
||||
|
||||
SSH into a Podman-managed virtual machine and optionally execute a command
|
||||
on the virtual machine. Unless using the default virtual machine, the
|
||||
on the virtual machine. Unless using the default virtual machine, the
|
||||
first argument must be the virtual machine name. The optional command to
|
||||
execute can then follow. If no command is provided, an interactive session
|
||||
with the virtual machine is established.
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ podman pause 860a4b23
|
|||
|
||||
Pause all **running** containers.
|
||||
```
|
||||
podman stop -a
|
||||
podman pause -a
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ podman\-pod\-rm - Remove one or more stopped pods and containers
|
|||
**podman pod rm** [*options*] *pod*
|
||||
|
||||
## DESCRIPTION
|
||||
**podman pod rm** will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod.
|
||||
**podman pod rm** will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod. If all containers added by the user are in an exited state, the pod will be removed.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
|
|
|
|||
|
|
@ -1288,6 +1288,15 @@ share the volume content. As a result, Podman labels the content with a shared
|
|||
content label. Shared volume labels allow all containers to read/write content.
|
||||
The **Z** option tells Podman to label the content with a private unshared label.
|
||||
|
||||
Note: Do not relabel system files and directories. Relabeling system content
|
||||
might cause other confined services on your machine to fail. For these types
|
||||
of containers we recommend that disable SELinux separation. The option
|
||||
`--security-opt label=disable` disables SELinux separation for the container.
|
||||
For example if a user wanted to volume mount their entire home directory into a
|
||||
container, they need to disable SELinux separation.
|
||||
|
||||
$ podman run --security-opt label=disable -v $HOME:/home/user fedora touch /home/user/file
|
||||
|
||||
`Overlay Volume Mounts`
|
||||
|
||||
The `:O` flag tells Podman to mount the directory from the host as a
|
||||
|
|
@ -1801,7 +1810,7 @@ Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released.
|
|||
In order for users to run rootless, there must be an entry for their username in _/etc/subuid_ and _/etc/subgid_ which lists the UIDs for their user namespace.
|
||||
|
||||
Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed.
|
||||
The **fuse-overlay** package provides a userspace overlay storage driver, otherwise users need to use
|
||||
The **fuse-overlayfs** package provides a userspace overlay storage driver, otherwise users need to use
|
||||
the **vfs** storage driver, which is diskspace expensive and does not perform well. slirp4netns is
|
||||
required for VPN, without it containers need to be run with the **--network=host** flag.
|
||||
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ Podman can also be used as non-root user. When podman runs in rootless mode, a u
|
|||
|
||||
Containers created by a non-root user are not visible to other users and are not seen or managed by Podman running as root.
|
||||
|
||||
It is required to have multiple uids/gids set for an user. Be sure the user is present in the files `/etc/subuid` and `/etc/subgid`.
|
||||
It is required to have multiple uids/gids set for a user. Be sure the user is present in the files `/etc/subuid` and `/etc/subgid`.
|
||||
|
||||
If you have a recent version of usermod, you can execute the following
|
||||
commands to add the ranges to the files
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -32,7 +32,7 @@ require (
|
|||
github.com/dtylman/scp v0.0.0-20181017070807-f3000a34aef4
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/godbus/dbus/v5 v5.0.5
|
||||
github.com/godbus/dbus/v5 v5.0.6
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -403,8 +403,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68Fp
|
|||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.5 h1:9Eg0XUhQxtkV8ykTMKtMMYY72g4NgxtRq4jgh4Ih5YM=
|
||||
github.com/godbus/dbus/v5 v5.0.5/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||
|
|
|
|||
|
|
@ -479,9 +479,27 @@ func (c *Container) setupStorage(ctx context.Context) error {
|
|||
|
||||
c.setupStorageMapping(&options.IDMappingOptions, &c.config.IDMappings)
|
||||
|
||||
containerInfo, err := c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating container storage")
|
||||
// Unless the user has specified a name, use a randomly generated one.
|
||||
// Note that name conflicts may occur (see #11735), so we need to loop.
|
||||
generateName := c.config.Name == ""
|
||||
var containerInfo ContainerInfo
|
||||
var containerInfoErr error
|
||||
for {
|
||||
if generateName {
|
||||
name, err := c.runtime.generateName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.config.Name = name
|
||||
}
|
||||
containerInfo, containerInfoErr = c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, options)
|
||||
|
||||
if !generateName || errors.Cause(containerInfoErr) != storage.ErrDuplicateName {
|
||||
break
|
||||
}
|
||||
}
|
||||
if containerInfoErr != nil {
|
||||
return errors.Wrapf(containerInfoErr, "error creating container storage")
|
||||
}
|
||||
|
||||
c.config.IDMappings.UIDMap = containerInfo.UIDMap
|
||||
|
|
@ -2100,7 +2118,7 @@ func (c *Container) checkReadyForRemoval() error {
|
|||
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in invalid state", c.ID())
|
||||
}
|
||||
|
||||
if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) {
|
||||
if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) && !c.IsInfra() {
|
||||
return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed without force", c.ID(), c.state.State.String())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1483,6 +1483,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
|||
logrus.Debugf("Restored container %s", c.ID())
|
||||
|
||||
c.state.State = define.ContainerStateRunning
|
||||
c.state.Checkpointed = false
|
||||
|
||||
if !options.Keep {
|
||||
// Delete all checkpoint related files. At this point, in theory, all files
|
||||
|
|
|
|||
|
|
@ -91,8 +91,12 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
|
|||
var cursorError error
|
||||
for i := 1; i <= 3; i++ {
|
||||
cursor, cursorError = journal.GetCursor()
|
||||
hundreds := 1
|
||||
for j := 1; j < i; j++ {
|
||||
hundreds *= 2
|
||||
}
|
||||
if cursorError != nil {
|
||||
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
||||
time.Sleep(time.Duration(hundreds*100) * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
break
|
||||
|
|
@ -117,8 +121,26 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
|
|||
}()
|
||||
|
||||
tailQueue := []*logs.LogLine{} // needed for options.Tail
|
||||
doTail := options.Tail > 0
|
||||
doTail := options.Tail >= 0
|
||||
doTailFunc := func() {
|
||||
// Flush *once* we hit the end of the journal.
|
||||
startIndex := int64(len(tailQueue))
|
||||
outputLines := int64(0)
|
||||
for startIndex > 0 && outputLines < options.Tail {
|
||||
startIndex--
|
||||
for startIndex > 0 && tailQueue[startIndex].Partial() {
|
||||
startIndex--
|
||||
}
|
||||
outputLines++
|
||||
}
|
||||
for i := startIndex; i < int64(len(tailQueue)); i++ {
|
||||
logChannel <- tailQueue[i]
|
||||
}
|
||||
tailQueue = nil
|
||||
doTail = false
|
||||
}
|
||||
lastReadCursor := ""
|
||||
partial := ""
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
@ -148,16 +170,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
|
|||
// Hit the end of the journal (so far?).
|
||||
if cursor == lastReadCursor {
|
||||
if doTail {
|
||||
// Flush *once* we hit the end of the journal.
|
||||
startIndex := int64(len(tailQueue)-1) - options.Tail
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
}
|
||||
for i := startIndex; i < int64(len(tailQueue)); i++ {
|
||||
logChannel <- tailQueue[i]
|
||||
}
|
||||
tailQueue = nil
|
||||
doTail = false
|
||||
doTailFunc()
|
||||
}
|
||||
// Unless we follow, quit.
|
||||
if !options.Follow {
|
||||
|
|
@ -190,6 +203,9 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
|
|||
return
|
||||
}
|
||||
if status == events.Exited {
|
||||
if doTail {
|
||||
doTailFunc()
|
||||
}
|
||||
return
|
||||
}
|
||||
continue
|
||||
|
|
@ -214,6 +230,12 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
|
|||
logrus.Errorf("Failed parse log line: %v", err)
|
||||
return
|
||||
}
|
||||
if logLine.Partial() {
|
||||
partial += logLine.Msg
|
||||
continue
|
||||
}
|
||||
logLine.Msg = partial + logLine.Msg
|
||||
partial = ""
|
||||
if doTail {
|
||||
tailQueue = append(tailQueue, logLine)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ func readKernelVersion() (string, error) {
|
|||
return "", err
|
||||
}
|
||||
f := bytes.Fields(buf)
|
||||
if len(f) < 2 {
|
||||
if len(f) < 3 {
|
||||
return string(bytes.TrimSpace(buf)), nil
|
||||
}
|
||||
return string(f[2]), nil
|
||||
|
|
|
|||
158
libpod/kube.go
158
libpod/kube.go
|
|
@ -166,9 +166,83 @@ func (v *Volume) GenerateForKube() *v1.PersistentVolumeClaim {
|
|||
}
|
||||
}
|
||||
|
||||
// YAMLPodSpec represents the same k8s API core PodSpec struct with a small
|
||||
// change and that is having Containers as a pointer to YAMLContainer.
|
||||
// Because Go doesn't omit empty struct and we want to omit Status in YAML
|
||||
// if it's empty. Fixes: GH-11998
|
||||
type YAMLPodSpec struct {
|
||||
v1.PodSpec
|
||||
Containers []*YAMLContainer `json:"containers"`
|
||||
}
|
||||
|
||||
// YAMLPod represents the same k8s API core Pod struct with a small
|
||||
// change and that is having Spec as a pointer to YAMLPodSpec and
|
||||
// Status as a pointer to k8s API core PodStatus.
|
||||
// Because Go doesn't omit empty struct and we want to omit Status in YAML
|
||||
// if it's empty. Fixes: GH-11998
|
||||
type YAMLPod struct {
|
||||
v1.Pod
|
||||
Spec *YAMLPodSpec `json:"spec,omitempty"`
|
||||
Status *v1.PodStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// YAMLService represents the same k8s API core Service struct with a small
|
||||
// change and that is having Status as a pointer to k8s API core ServiceStatus.
|
||||
// Because Go doesn't omit empty struct and we want to omit Status in YAML
|
||||
// if it's empty. Fixes: GH-11998
|
||||
type YAMLService struct {
|
||||
v1.Service
|
||||
Status *v1.ServiceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// YAMLContainer represents the same k8s API core Container struct with a small
|
||||
// change and that is having Resources as a pointer to k8s API core ResourceRequirements.
|
||||
// Because Go doesn't omit empty struct and we want to omit Status in YAML
|
||||
// if it's empty. Fixes: GH-11998
|
||||
type YAMLContainer struct {
|
||||
v1.Container
|
||||
Resources *v1.ResourceRequirements `json:"resources,omitempty"`
|
||||
}
|
||||
|
||||
// ConvertV1PodToYAMLPod takes k8s API core Pod and returns a pointer to YAMLPod
|
||||
func ConvertV1PodToYAMLPod(pod *v1.Pod) *YAMLPod {
|
||||
cs := []*YAMLContainer{}
|
||||
for _, cc := range pod.Spec.Containers {
|
||||
var res *v1.ResourceRequirements = nil
|
||||
if len(cc.Resources.Limits) > 0 || len(cc.Resources.Requests) > 0 {
|
||||
res = &cc.Resources
|
||||
}
|
||||
cs = append(cs, &YAMLContainer{Container: cc, Resources: res})
|
||||
}
|
||||
mpo := &YAMLPod{Pod: *pod}
|
||||
mpo.Spec = &YAMLPodSpec{PodSpec: (*pod).Spec, Containers: cs}
|
||||
for _, ctr := range pod.Spec.Containers {
|
||||
if ctr.SecurityContext == nil || ctr.SecurityContext.SELinuxOptions == nil {
|
||||
continue
|
||||
}
|
||||
selinuxOpts := ctr.SecurityContext.SELinuxOptions
|
||||
if selinuxOpts.User == "" && selinuxOpts.Role == "" && selinuxOpts.Type == "" && selinuxOpts.Level == "" {
|
||||
ctr.SecurityContext.SELinuxOptions = nil
|
||||
}
|
||||
}
|
||||
dnsCfg := pod.Spec.DNSConfig
|
||||
if dnsCfg != nil && (len(dnsCfg.Nameservers)+len(dnsCfg.Searches)+len(dnsCfg.Options) > 0) {
|
||||
mpo.Spec.DNSConfig = dnsCfg
|
||||
}
|
||||
status := pod.Status
|
||||
if status.Phase != "" || len(status.Conditions) > 0 ||
|
||||
status.Message != "" || status.Reason != "" ||
|
||||
status.NominatedNodeName != "" || status.HostIP != "" ||
|
||||
status.PodIP != "" || status.StartTime != nil ||
|
||||
len(status.InitContainerStatuses) > 0 || len(status.ContainerStatuses) > 0 || status.QOSClass != "" || len(status.EphemeralContainerStatuses) > 0 {
|
||||
mpo.Status = &status
|
||||
}
|
||||
return mpo
|
||||
}
|
||||
|
||||
// GenerateKubeServiceFromV1Pod creates a v1 service object from a v1 pod object
|
||||
func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) v1.Service {
|
||||
service := v1.Service{}
|
||||
func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) YAMLService {
|
||||
service := YAMLService{}
|
||||
selector := make(map[string]string)
|
||||
selector["app"] = pod.Labels["app"]
|
||||
ports := servicePorts
|
||||
|
|
@ -332,7 +406,7 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta
|
|||
InitContainers: initCtrs,
|
||||
Volumes: volumes,
|
||||
}
|
||||
if dnsOptions != nil {
|
||||
if dnsOptions != nil && (len(dnsOptions.Nameservers)+len(dnsOptions.Searches)+len(dnsOptions.Options) > 0) {
|
||||
ps.DNSConfig = dnsOptions
|
||||
}
|
||||
p := v1.Pod{
|
||||
|
|
@ -447,11 +521,6 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
|
|||
kubeVolumes = append(kubeVolumes, volumes...)
|
||||
}
|
||||
|
||||
envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env)
|
||||
if err != nil {
|
||||
return kubeContainer, kubeVolumes, nil, annotations, err
|
||||
}
|
||||
|
||||
portmappings, err := c.PortMappings()
|
||||
if err != nil {
|
||||
return kubeContainer, kubeVolumes, nil, annotations, err
|
||||
|
|
@ -489,15 +558,23 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
|
|||
kubeContainer.Command = nil
|
||||
}
|
||||
|
||||
if c.WorkingDir() != "/" && imgData.Config.WorkingDir != c.WorkingDir() {
|
||||
kubeContainer.WorkingDir = c.WorkingDir()
|
||||
}
|
||||
|
||||
if imgData.User == c.User() {
|
||||
kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil
|
||||
}
|
||||
|
||||
kubeContainer.WorkingDir = c.WorkingDir()
|
||||
envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env, imgData.Config.Env)
|
||||
if err != nil {
|
||||
return kubeContainer, kubeVolumes, nil, annotations, err
|
||||
}
|
||||
kubeContainer.Env = envVariables
|
||||
|
||||
kubeContainer.Ports = ports
|
||||
// This should not be applicable
|
||||
//container.EnvFromSource =
|
||||
kubeContainer.Env = envVariables
|
||||
kubeContainer.SecurityContext = kubeSec
|
||||
kubeContainer.StdinOnce = false
|
||||
kubeContainer.TTY = c.config.Spec.Process.Terminal
|
||||
|
|
@ -600,9 +677,14 @@ func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.C
|
|||
}
|
||||
|
||||
// libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar
|
||||
func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) {
|
||||
func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar, error) {
|
||||
defaultEnv := env.DefaultEnvVariables()
|
||||
envVars := make([]v1.EnvVar, 0, len(envs))
|
||||
imageMap := make(map[string]string, len(imageEnvs))
|
||||
for _, ie := range envs {
|
||||
split := strings.SplitN(ie, "=", 2)
|
||||
imageMap[split[0]] = split[1]
|
||||
}
|
||||
for _, e := range envs {
|
||||
split := strings.SplitN(e, "=", 2)
|
||||
if len(split) != 2 {
|
||||
|
|
@ -611,6 +693,9 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) {
|
|||
if defaultEnv[split[0]] == split[1] {
|
||||
continue
|
||||
}
|
||||
if imageMap[split[0]] == split[1] {
|
||||
continue
|
||||
}
|
||||
ev := v1.EnvVar{
|
||||
Name: split[0],
|
||||
Value: split[1],
|
||||
|
|
@ -808,33 +893,42 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
|
|||
capabilities = newCaps
|
||||
}
|
||||
|
||||
var selinuxOpts v1.SELinuxOptions
|
||||
opts := strings.SplitN(c.config.Spec.Annotations[define.InspectAnnotationLabel], ":", 2)
|
||||
if len(opts) == 2 {
|
||||
switch opts[0] {
|
||||
case "type":
|
||||
selinuxOpts.Type = opts[1]
|
||||
case "level":
|
||||
selinuxOpts.Level = opts[1]
|
||||
}
|
||||
}
|
||||
if len(opts) == 1 {
|
||||
if opts[0] == "disable" {
|
||||
selinuxOpts.Type = "spc_t"
|
||||
}
|
||||
}
|
||||
|
||||
sc := v1.SecurityContext{
|
||||
Capabilities: capabilities,
|
||||
Privileged: &privileged,
|
||||
SELinuxOptions: &selinuxOpts,
|
||||
// RunAsNonRoot is an optional parameter; our first implementations should be root only; however
|
||||
// I'm leaving this as a bread-crumb for later
|
||||
//RunAsNonRoot: &nonRoot,
|
||||
ReadOnlyRootFilesystem: &ro,
|
||||
AllowPrivilegeEscalation: &allowPrivEscalation,
|
||||
}
|
||||
if capabilities != nil {
|
||||
sc.Capabilities = capabilities
|
||||
}
|
||||
var selinuxOpts v1.SELinuxOptions
|
||||
opts := strings.SplitN(c.config.Spec.Annotations[define.InspectAnnotationLabel], ":", 2)
|
||||
switch len(opts) {
|
||||
case 2:
|
||||
switch opts[0] {
|
||||
case "type":
|
||||
selinuxOpts.Type = opts[1]
|
||||
sc.SELinuxOptions = &selinuxOpts
|
||||
case "level":
|
||||
selinuxOpts.Level = opts[1]
|
||||
sc.SELinuxOptions = &selinuxOpts
|
||||
}
|
||||
case 1:
|
||||
if opts[0] == "disable" {
|
||||
selinuxOpts.Type = "spc_t"
|
||||
sc.SELinuxOptions = &selinuxOpts
|
||||
}
|
||||
}
|
||||
|
||||
if !allowPrivEscalation {
|
||||
sc.AllowPrivilegeEscalation = &allowPrivEscalation
|
||||
}
|
||||
if privileged {
|
||||
sc.Privileged = &privileged
|
||||
}
|
||||
if ro {
|
||||
sc.ReadOnlyRootFilesystem = &ro
|
||||
}
|
||||
if c.User() != "" {
|
||||
if !c.batched {
|
||||
c.lock.Lock()
|
||||
|
|
|
|||
|
|
@ -130,8 +130,17 @@ func (locks *SHMLocks) AllocateSemaphore() (uint32, error) {
|
|||
// semaphore indexes, and can still return error codes.
|
||||
retCode := C.allocate_semaphore(locks.lockStruct)
|
||||
if retCode < 0 {
|
||||
var err = syscall.Errno(-1 * retCode)
|
||||
// Negative errno returned
|
||||
return 0, syscall.Errno(-1 * retCode)
|
||||
if errors.Is(err, syscall.ENOSPC) {
|
||||
// ENOSPC expands to "no space left on device". While it is technically true
|
||||
// that there's no room in the SHM inn for this lock, this tends to send normal people
|
||||
// down the path of checking disk-space which is not actually their problem.
|
||||
// Give a clue that it's actually due to num_locks filling up.
|
||||
var errFull = errors.Errorf("allocation failed; exceeded num_locks (%d)", locks.maxLocks)
|
||||
return uint32(retCode), errFull
|
||||
}
|
||||
return uint32(retCode), syscall.Errno(-1 * retCode)
|
||||
}
|
||||
|
||||
return uint32(retCode), nil
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ func newIPAMLocalHostRange(subnet types.IPNet, leaseRange *types.LeaseRange, gw
|
|||
Subnet: subnet.String(),
|
||||
}
|
||||
|
||||
// an user provided a range, we add it here
|
||||
// a user provided a range, we add it here
|
||||
if leaseRange != nil {
|
||||
if leaseRange.StartIP != nil {
|
||||
hostRange.RangeStart = leaseRange.StartIP.String()
|
||||
|
|
|
|||
|
|
@ -199,10 +199,8 @@ func getRuntimeConfig(netns, conName, conID, networkName string, ports []cniPort
|
|||
IfName: opts.InterfaceName,
|
||||
Args: [][2]string{
|
||||
{"IgnoreUnknown", "1"},
|
||||
// FIXME: Should we set the K8S args?
|
||||
//{"K8S_POD_NAMESPACE", conName},
|
||||
//{"K8S_POD_INFRA_CONTAINER_ID", conID},
|
||||
// K8S_POD_NAME is used by dnsname to get the container name
|
||||
// Do not set the K8S env vars, see https://github.com/containers/podman/issues/12083.
|
||||
// Only K8S_POD_NAME is used by dnsname to get the container name.
|
||||
{"K8S_POD_NAME", conName},
|
||||
},
|
||||
CapabilityArgs: map[string]interface{}{},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containers/podman/v3/pkg/errorhandling"
|
||||
"github.com/containers/podman/v3/pkg/rootless"
|
||||
"github.com/containers/podman/v3/pkg/rootlessport"
|
||||
|
|
@ -58,6 +59,8 @@ type slirp4netnsNetworkOptions struct {
|
|||
outboundAddr6 string
|
||||
}
|
||||
|
||||
const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/default/accept_dad"
|
||||
|
||||
func checkSlirpFlags(path string) (*slirpFeatures, error) {
|
||||
cmd := exec.Command(path, "--help")
|
||||
out, err := cmd.CombinedOutput()
|
||||
|
|
@ -297,6 +300,39 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
|
|||
}
|
||||
cmd.Stdout = logFile
|
||||
cmd.Stderr = logFile
|
||||
|
||||
var slirpReadyChan (chan struct{})
|
||||
|
||||
if netOptions.enableIPv6 {
|
||||
slirpReadyChan = make(chan struct{})
|
||||
defer close(slirpReadyChan)
|
||||
go func() {
|
||||
err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error {
|
||||
// Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds.
|
||||
// Since slirp4netns is run it is own namespace and not directly routed
|
||||
// we can skip this to make the ipv6 address immediately available.
|
||||
// We change the default to make sure the slirp tap interface gets the
|
||||
// correct value assigned so DAD is disabled for it
|
||||
// Also make sure to change this value back to the original after slirp4netns
|
||||
// is ready in case users rely on this sysctl.
|
||||
orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// wait for slirp to finish setup
|
||||
<-slirpReadyChan
|
||||
return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644)
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return errors.Wrapf(err, "failed to start slirp4netns process")
|
||||
}
|
||||
|
|
@ -310,6 +346,9 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
|
|||
if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
if slirpReadyChan != nil {
|
||||
slirpReadyChan <- struct{}{}
|
||||
}
|
||||
|
||||
// Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
|
||||
_, ctr.slirp4netnsSubnet, _ = net.ParseCIDR(defaultSlirp4netnsSubnet)
|
||||
|
|
|
|||
|
|
@ -541,6 +541,8 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
|
|||
return err
|
||||
}
|
||||
if became {
|
||||
// Check if the pause process was created. If it was created, then
|
||||
// move it to its own systemd scope.
|
||||
utils.MovePauseProcessToScope(pausePid)
|
||||
os.Exit(ret)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,15 +322,6 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
|||
}
|
||||
}
|
||||
|
||||
if ctr.config.Name == "" {
|
||||
name, err := r.generateName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctr.config.Name = name
|
||||
}
|
||||
|
||||
// Check CGroup parent sanity, and set it if it was not set.
|
||||
// Only if we're actually configuring CGroups.
|
||||
if !ctr.config.NoCgroups {
|
||||
|
|
|
|||
|
|
@ -43,18 +43,6 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option
|
|||
}
|
||||
}
|
||||
|
||||
if pod.config.Name == "" {
|
||||
name, err := r.generateName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pod.config.Name = name
|
||||
}
|
||||
|
||||
if p.InfraContainerSpec != nil && p.InfraContainerSpec.Hostname == "" {
|
||||
p.InfraContainerSpec.Hostname = pod.config.Name
|
||||
}
|
||||
|
||||
// Allocate a lock for the pod
|
||||
lock, err := r.lockManager.AllocateLock()
|
||||
if err != nil {
|
||||
|
|
@ -131,9 +119,33 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option
|
|||
logrus.Infof("Pod has an infra container, but shares no namespaces")
|
||||
}
|
||||
|
||||
if err := r.state.AddPod(pod); err != nil {
|
||||
return nil, errors.Wrapf(err, "error adding pod to state")
|
||||
// Unless the user has specified a name, use a randomly generated one.
|
||||
// Note that name conflicts may occur (see #11735), so we need to loop.
|
||||
generateName := pod.config.Name == ""
|
||||
var addPodErr error
|
||||
for {
|
||||
if generateName {
|
||||
name, err := r.generateName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pod.config.Name = name
|
||||
}
|
||||
|
||||
if p.InfraContainerSpec != nil && p.InfraContainerSpec.Hostname == "" {
|
||||
p.InfraContainerSpec.Hostname = pod.config.Name
|
||||
}
|
||||
if addPodErr = r.state.AddPod(pod); addPodErr == nil {
|
||||
return pod, nil
|
||||
}
|
||||
if !generateName || (errors.Cause(addPodErr) != define.ErrPodExists && errors.Cause(addPodErr) != define.ErrCtrExists) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if addPodErr != nil {
|
||||
return nil, errors.Wrapf(addPodErr, "error adding pod to state")
|
||||
}
|
||||
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
|
|
@ -177,10 +189,9 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numCtrs := len(ctrs)
|
||||
|
||||
// If the only container in the pod is the pause container, remove the pod and container unconditionally.
|
||||
// If the only running container in the pod is the pause container, remove the pod and container unconditionally.
|
||||
pauseCtrID := p.state.InfraContainerID
|
||||
if numCtrs == 1 && ctrs[0].ID() == pauseCtrID {
|
||||
removeCtrs = true
|
||||
|
|
@ -264,6 +275,15 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
|
|||
}
|
||||
}
|
||||
|
||||
// Clear infra container ID before we remove the infra container.
|
||||
// There is a potential issue if we don't do that, and removal is
|
||||
// interrupted between RemoveAllContainers() below and the pod's removal
|
||||
// later - we end up with a reference to a nonexistent infra container.
|
||||
p.state.InfraContainerID = ""
|
||||
if err := p.save(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove all containers in the pod from the state.
|
||||
if err := r.state.RemovePodContainers(p); err != nil {
|
||||
// If this fails, there isn't much more we can do.
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ let
|
|||
|
||||
self = with pkgs; buildGoModule rec {
|
||||
name = "podman";
|
||||
src = ./..;
|
||||
src = builtins.filterSource
|
||||
(path: type: !(type == "directory" && baseNameOf path == "bin")) ./..;
|
||||
vendorSha256 = null;
|
||||
doCheck = false;
|
||||
enableParallelBuilding = true;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ let
|
|||
|
||||
self = with pkgs; buildGoModule rec {
|
||||
name = "podman";
|
||||
src = ./..;
|
||||
src = builtins.filterSource
|
||||
(path: type: !(type == "directory" && baseNameOf path == "bin")) ./..;
|
||||
vendorSha256 = null;
|
||||
doCheck = false;
|
||||
enableParallelBuilding = true;
|
||||
|
|
|
|||
|
|
@ -151,22 +151,19 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
|
|||
var m = []string{}
|
||||
if err := json.Unmarshal([]byte(query.Dockerfile), &m); err != nil {
|
||||
// it's not json, assume just a string
|
||||
m = append(m, query.Dockerfile)
|
||||
m = []string{filepath.Join(contextDirectory, query.Dockerfile)}
|
||||
}
|
||||
containerFiles = m
|
||||
} else {
|
||||
containerFiles = []string{"Dockerfile"}
|
||||
containerFiles = []string{filepath.Join(contextDirectory, "Dockerfile")}
|
||||
if utils.IsLibpodRequest(r) {
|
||||
containerFiles = []string{"Containerfile"}
|
||||
if _, err = os.Stat(filepath.Join(contextDirectory, "Containerfile")); err != nil {
|
||||
if _, err1 := os.Stat(filepath.Join(contextDirectory, "Dockerfile")); err1 == nil {
|
||||
containerFiles = []string{"Dockerfile"}
|
||||
} else {
|
||||
containerFiles = []string{filepath.Join(contextDirectory, "Containerfile")}
|
||||
if _, err = os.Stat(containerFiles[0]); err != nil {
|
||||
containerFiles = []string{filepath.Join(contextDirectory, "Dockerfile")}
|
||||
if _, err1 := os.Stat(containerFiles[0]); err1 != nil {
|
||||
utils.BadRequest(w, "dockerfile", query.Dockerfile, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
containerFiles = []string{"Dockerfile"}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package server
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/podman/v3/pkg/api/types"
|
||||
|
|
@ -15,7 +16,13 @@ import (
|
|||
// and Apache style request logging
|
||||
func referenceIDHandler() mux.MiddlewareFunc {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return handlers.CombinedLoggingHandler(logrus.StandardLogger().Out,
|
||||
// Only log Apache access_log-like entries at Info level or below
|
||||
out := ioutil.Discard
|
||||
if logrus.IsLevelEnabled(logrus.InfoLevel) {
|
||||
out = logrus.StandardLogger().Out
|
||||
}
|
||||
|
||||
return handlers.CombinedLoggingHandler(out,
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rid := r.Header.Get("X-Reference-Id")
|
||||
if rid == "" {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
|
|||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/DockerImageSummary"
|
||||
// $ref: "#/responses/DockerImageSummaryResponse"
|
||||
// 500:
|
||||
// $ref: '#/responses/InternalError'
|
||||
r.Handle(VersionedPath("/images/json"), s.APIHandler(compat.GetImages)).Methods(http.MethodGet)
|
||||
|
|
@ -831,7 +831,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
|
|||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/DockerImageSummary"
|
||||
// $ref: "#/responses/LibpodImageSummaryResponse"
|
||||
// 500:
|
||||
// $ref: '#/responses/InternalError'
|
||||
r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(libpod.GetImages)).Methods(http.MethodGet)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities/reports"
|
||||
"github.com/containers/podman/v3/pkg/errorhandling"
|
||||
docker "github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// No such image
|
||||
|
|
@ -134,9 +135,16 @@ type swagPodAlreadyStopped struct {
|
|||
}
|
||||
}
|
||||
|
||||
// Image summary
|
||||
// swagger:response DockerImageSummary
|
||||
type swagImageSummary struct {
|
||||
// Image summary for compat API
|
||||
// swagger:response DockerImageSummaryResponse
|
||||
type swagDockerImageSummaryResponse struct {
|
||||
// in:body
|
||||
Body []docker.ImageSummary
|
||||
}
|
||||
|
||||
// Image summary for libpod API
|
||||
// swagger:response LibpodImageSummaryResponse
|
||||
type swagLibpodImageSummaryResponse struct {
|
||||
// in:body
|
||||
Body []entities.ImageSummary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -379,6 +379,11 @@ func (h *APIResponse) IsClientError() bool {
|
|||
return h.Response.StatusCode/100 == 4
|
||||
}
|
||||
|
||||
// IsConflictError returns true if the response code is 409
|
||||
func (h *APIResponse) IsConflictError() bool {
|
||||
return h.Response.StatusCode == 409
|
||||
}
|
||||
|
||||
// IsServerError returns true if the response code is 5xx
|
||||
func (h *APIResponse) IsServerError() bool {
|
||||
return h.Response.StatusCode/100 == 5
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
|
|||
// Read multiplexed channels and write to appropriate stream
|
||||
fd, l, err := DemuxHeader(socket, buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
|
@ -531,7 +531,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
|
|||
// Read multiplexed channels and write to appropriate stream
|
||||
fd, l, err := DemuxHeader(socket, buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func Logs(ctx context.Context, nameOrID string, options *LogOptions, stdoutChan,
|
|||
for {
|
||||
fd, l, err := DemuxHeader(response.Body, buffer)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -12,17 +12,22 @@ var (
|
|||
ErrNotImplemented = errors.New("function not implemented")
|
||||
)
|
||||
|
||||
func handleError(data []byte) error {
|
||||
e := errorhandling.ErrorModel{}
|
||||
if err := json.Unmarshal(data, &e); err != nil {
|
||||
func handleError(data []byte, unmarshalErrorInto interface{}) error {
|
||||
if err := json.Unmarshal(data, unmarshalErrorInto); err != nil {
|
||||
return err
|
||||
}
|
||||
return e
|
||||
return unmarshalErrorInto.(error)
|
||||
}
|
||||
|
||||
// Process drains the response body, and processes the HTTP status code
|
||||
// Note: Closing the response.Body is left to the caller
|
||||
func (h APIResponse) Process(unmarshalInto interface{}) error {
|
||||
return h.ProcessWithError(unmarshalInto, &errorhandling.ErrorModel{})
|
||||
}
|
||||
|
||||
// Process drains the response body, and processes the HTTP status code
|
||||
// Note: Closing the response.Body is left to the caller
|
||||
func (h APIResponse) ProcessWithError(unmarshalInto interface{}, unmarshalErrorInto interface{}) error {
|
||||
data, err := ioutil.ReadAll(h.Response.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to process API response")
|
||||
|
|
@ -33,14 +38,22 @@ func (h APIResponse) Process(unmarshalInto interface{}) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if h.IsConflictError() {
|
||||
return handleError(data, unmarshalErrorInto)
|
||||
}
|
||||
|
||||
// TODO should we add a debug here with the response code?
|
||||
return handleError(data)
|
||||
return handleError(data, &errorhandling.ErrorModel{})
|
||||
}
|
||||
|
||||
func CheckResponseCode(inError error) (int, error) {
|
||||
e, ok := inError.(errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
switch e := inError.(type) {
|
||||
case *errorhandling.ErrorModel:
|
||||
return e.Code(), nil
|
||||
case *errorhandling.PodConflictErrorModel:
|
||||
return e.Code(), nil
|
||||
default:
|
||||
return -1, errors.New("error is not type ErrorModel")
|
||||
}
|
||||
return e.Code(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/containers/podman/v3/pkg/api/handlers"
|
||||
"github.com/containers/podman/v3/pkg/bindings"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/containers/podman/v3/pkg/errorhandling"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
|
|
@ -97,7 +98,7 @@ func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entities
|
|||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.Process(&report)
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Pause pauses all running containers in a given pod.
|
||||
|
|
@ -117,7 +118,7 @@ func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entiti
|
|||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.Process(&report)
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Prune by default removes all non-running pods in local storage.
|
||||
|
|
@ -184,7 +185,7 @@ func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*en
|
|||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.Process(&report)
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Remove deletes a Pod from from local storage. The optional force parameter denotes
|
||||
|
|
@ -232,7 +233,8 @@ func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entiti
|
|||
report.Id = nameOrID
|
||||
return &report, nil
|
||||
}
|
||||
return &report, response.Process(&report)
|
||||
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Stop stops all containers in a Pod. The optional timeout parameter can be
|
||||
|
|
@ -260,7 +262,7 @@ func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entities
|
|||
report.Id = nameOrID
|
||||
return &report, nil
|
||||
}
|
||||
return &report, response.Process(&report)
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name
|
||||
|
|
@ -316,7 +318,7 @@ func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*en
|
|||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.Process(&report)
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Stats display resource-usage statistics of one or more pods.
|
||||
|
|
|
|||
|
|
@ -225,12 +225,23 @@ func (b *bindingTest) RunTopContainer(containerName *string, podName *string) (s
|
|||
// This method creates a pod with the given pod name.
|
||||
// Podname is an optional parameter
|
||||
func (b *bindingTest) Podcreate(name *string) {
|
||||
b.PodcreateAndExpose(name, nil)
|
||||
}
|
||||
|
||||
// This method creates a pod with the given pod name and publish port.
|
||||
// Podname is an optional parameter
|
||||
// port is an optional parameter
|
||||
func (b *bindingTest) PodcreateAndExpose(name *string, port *string) {
|
||||
command := []string{"pod", "create"}
|
||||
if name != nil {
|
||||
podname := *name
|
||||
b.runPodman([]string{"pod", "create", "--name", podname}).Wait(45)
|
||||
} else {
|
||||
b.runPodman([]string{"pod", "create"}).Wait(45)
|
||||
command = append(command, "--name", podname)
|
||||
}
|
||||
if port != nil {
|
||||
podport := *port
|
||||
command = append(command, "--publish", podport)
|
||||
}
|
||||
b.runPodman(command).Wait(45)
|
||||
}
|
||||
|
||||
// StringInSlice returns a boolean based on whether a given
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ var _ = Describe("Podman containers ", func() {
|
|||
_, err = bt.RunTopContainer(&name, nil)
|
||||
Expect(err).To(BeNil())
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
exitCode, err = containers.Wait(bt.conn, name, nil)
|
||||
errChan <- err
|
||||
close(errChan)
|
||||
|
|
@ -281,6 +282,7 @@ var _ = Describe("Podman containers ", func() {
|
|||
_, err := bt.RunTopContainer(&name, nil)
|
||||
Expect(err).To(BeNil())
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
exitCode, err = containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition([]define.ContainerStatus{pause}))
|
||||
errChan <- err
|
||||
close(errChan)
|
||||
|
|
@ -366,7 +368,10 @@ var _ = Describe("Podman containers ", func() {
|
|||
|
||||
opts := new(containers.LogOptions).WithStdout(true).WithFollow(true)
|
||||
go func() {
|
||||
containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil)
|
||||
defer GinkgoRecover()
|
||||
err := containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil)
|
||||
close(stdoutChan)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
}()
|
||||
o := <-stdoutChan
|
||||
o = strings.TrimSpace(o)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package test_bindings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -9,7 +10,9 @@ import (
|
|||
"github.com/containers/podman/v3/pkg/bindings"
|
||||
"github.com/containers/podman/v3/pkg/bindings/pods"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/containers/podman/v3/pkg/errorhandling"
|
||||
"github.com/containers/podman/v3/pkg/specgen"
|
||||
"github.com/containers/podman/v3/utils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
|
@ -208,6 +211,29 @@ var _ = Describe("Podman pods", func() {
|
|||
}
|
||||
})
|
||||
|
||||
It("start pod with port conflict", func() {
|
||||
randomport, err := utils.GetRandomPort()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
portPublish := fmt.Sprintf("%d:%d", randomport, randomport)
|
||||
var podwithport string = "newpodwithport"
|
||||
bt.PodcreateAndExpose(&podwithport, &portPublish)
|
||||
|
||||
// Start pod and expose port 12345
|
||||
_, err = pods.Start(bt.conn, podwithport, nil)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// Start another pod and expose same port 12345
|
||||
var podwithport2 string = "newpodwithport2"
|
||||
bt.PodcreateAndExpose(&podwithport2, &portPublish)
|
||||
|
||||
_, err = pods.Start(bt.conn, podwithport2, nil)
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusConflict))
|
||||
Expect(err).To(BeAssignableToTypeOf(&errorhandling.PodConflictErrorModel{}))
|
||||
})
|
||||
|
||||
It("start stop restart pod", func() {
|
||||
// Start an invalid pod
|
||||
_, err = pods.Start(bt.conn, "dummyName", nil)
|
||||
|
|
|
|||
|
|
@ -461,10 +461,10 @@ func (c *CgroupControl) CreateSystemdUnit(path string) error {
|
|||
return systemdCreate(path, conn)
|
||||
}
|
||||
|
||||
// GetUserConnection returns an user connection to D-BUS
|
||||
// GetUserConnection returns a user connection to D-BUS
|
||||
func GetUserConnection(uid int) (*systemdDbus.Conn, error) {
|
||||
return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthConnection(uid, dbus.SessionBusPrivate)
|
||||
return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ func (i *Image) Id() string { // nolint
|
|||
return i.ID
|
||||
}
|
||||
|
||||
// swagger:model LibpodImageSummary
|
||||
type ImageSummary struct {
|
||||
ID string `json:"Id"`
|
||||
ParentId string // nolint
|
||||
|
|
|
|||
|
|
@ -124,8 +124,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := generateKubeYAML(po)
|
||||
b, err := generateKubeYAML(libpod.ConvertV1PodToYAMLPod(po))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
|
|||
for _, name := range namesOrIds {
|
||||
inspect, err := containers.Inspect(ic.ClientCtx, name, options)
|
||||
if err != nil {
|
||||
errModel, ok := err.(errorhandling.ErrorModel)
|
||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en
|
|||
for _, i := range namesOrIDs {
|
||||
r, err := images.GetImage(ir.ClientCtx, i, options)
|
||||
if err != nil {
|
||||
errModel, ok := err.(errorhandling.ErrorModel)
|
||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri
|
|||
for _, name := range namesOrIds {
|
||||
report, err := network.Inspect(ic.ClientCtx, name, options)
|
||||
if err != nil {
|
||||
errModel, ok := err.(errorhandling.ErrorModel)
|
||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
|||
for _, name := range nameOrIDs {
|
||||
inspected, err := secrets.Inspect(ic.ClientCtx, name, nil)
|
||||
if err != nil {
|
||||
errModel, ok := err.(errorhandling.ErrorModel)
|
||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ func (ic *ContainerEngine) SecretRm(ctx context.Context, nameOrIDs []string, opt
|
|||
for _, name := range nameOrIDs {
|
||||
secret, err := secrets.Inspect(ic.ClientCtx, name, nil)
|
||||
if err != nil {
|
||||
errModel, ok := err.(errorhandling.ErrorModel)
|
||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
|
|||
for _, id := range namesOrIds {
|
||||
data, err := volumes.Inspect(ic.ClientCtx, id, nil)
|
||||
if err != nil {
|
||||
errModel, ok := err.(errorhandling.ErrorModel)
|
||||
errModel, ok := err.(*errorhandling.ErrorModel)
|
||||
if !ok {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,12 @@ func Contains(err error, sub error) bool {
|
|||
return strings.Contains(err.Error(), sub.Error())
|
||||
}
|
||||
|
||||
// PodConflictErrorModel is used in remote connections with podman
|
||||
type PodConflictErrorModel struct {
|
||||
Errs []string
|
||||
Id string //nolint
|
||||
}
|
||||
|
||||
// ErrorModel is used in remote connections with podman
|
||||
type ErrorModel struct {
|
||||
// API root cause formatted for automated parsing
|
||||
|
|
@ -106,3 +112,11 @@ func (e ErrorModel) Cause() error {
|
|||
func (e ErrorModel) Code() int {
|
||||
return e.ResponseCode
|
||||
}
|
||||
|
||||
func (e PodConflictErrorModel) Error() string {
|
||||
return strings.Join(e.Errs, ",")
|
||||
}
|
||||
|
||||
func (e PodConflictErrorModel) Code() int {
|
||||
return 409
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,6 +304,24 @@ machine_enabled=true
|
|||
},
|
||||
})
|
||||
|
||||
setDockerHost := `export DOCKER_HOST="unix://$(podman info -f "{{.Host.RemoteSocket.Path}}")"
|
||||
`
|
||||
|
||||
files = append(files, File{
|
||||
Node: Node{
|
||||
Group: getNodeGrp("root"),
|
||||
Path: "/etc/profile.d/docker-host.sh",
|
||||
User: getNodeUsr("root"),
|
||||
},
|
||||
FileEmbedded1: FileEmbedded1{
|
||||
Append: nil,
|
||||
Contents: Resource{
|
||||
Source: encodeDataURLPtr(setDockerHost),
|
||||
},
|
||||
Mode: intToPtr(0644),
|
||||
},
|
||||
})
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,12 +133,14 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOption
|
|||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid value for memory")
|
||||
}
|
||||
memory.Limit = &ml
|
||||
if c.MemorySwap == "" {
|
||||
limit := 2 * ml
|
||||
memory.Swap = &(limit)
|
||||
if ml > 0 {
|
||||
memory.Limit = &ml
|
||||
if c.MemorySwap == "" {
|
||||
limit := 2 * ml
|
||||
memory.Swap = &(limit)
|
||||
}
|
||||
hasLimits = true
|
||||
}
|
||||
hasLimits = true
|
||||
}
|
||||
if m := c.MemoryReservation; len(m) > 0 {
|
||||
mr, err := units.RAMInBytes(m)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func IsSystemdSessionValid(uid int) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// GetDbusConnection returns an user connection to D-BUS
|
||||
// GetDbusConnection returns a user connection to D-BUS
|
||||
func GetLogindConnection(uid int) (*godbus.Conn, error) {
|
||||
return dbusAuthConnectionLogind(uid)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ func SkipIfContainerized(reason string) {
|
|||
|
||||
// PodmanAsUser is the exec call to podman on the filesystem with the specified uid/gid and environment
|
||||
func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd string, env []string) *PodmanSessionIntegration {
|
||||
podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil)
|
||||
podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil, nil)
|
||||
return &PodmanSessionIntegration{podmanSession}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ var _ = Describe("Podman generate kube", func() {
|
|||
err := yaml.Unmarshal(kube.Out.Contents(), pod)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(pod.Spec.HostNetwork).To(Equal(false))
|
||||
Expect(pod.Spec.SecurityContext).To(BeNil())
|
||||
Expect(pod.Spec.DNSConfig).To(BeNil())
|
||||
Expect(pod.Spec.Containers[0].WorkingDir).To(Equal(""))
|
||||
Expect(pod.Spec.Containers[0].Env).To(BeNil())
|
||||
|
||||
numContainers := 0
|
||||
for range pod.Spec.Containers {
|
||||
|
|
@ -103,6 +107,7 @@ var _ = Describe("Podman generate kube", func() {
|
|||
err = yaml.Unmarshal(kube.Out.Contents(), pod)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(kube.OutputToString()).To(ContainSubstring("type: spc_t"))
|
||||
|
||||
})
|
||||
|
||||
It("podman generate service kube on container with --security-opt type", func() {
|
||||
|
|
@ -1079,7 +1084,7 @@ USER test1`
|
|||
top1.WaitWithDefaultTimeout()
|
||||
Expect(top1).Should(Exit(0))
|
||||
|
||||
top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"})
|
||||
top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--workdir", "/root", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"})
|
||||
top2.WaitWithDefaultTimeout()
|
||||
Expect(top2).Should(Exit(0))
|
||||
|
||||
|
|
@ -1090,6 +1095,8 @@ USER test1`
|
|||
pod := new(v1.Pod)
|
||||
err := yaml.Unmarshal(kube.Out.Contents(), pod)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(pod.Spec.Containers[0].WorkingDir).To(Equal(""))
|
||||
Expect(pod.Spec.Containers[1].WorkingDir).To(Equal("/root"))
|
||||
|
||||
for _, ctr := range []string{"top1", "top2"} {
|
||||
v, ok := pod.GetAnnotations()["io.containers.autoupdate/"+ctr]
|
||||
|
|
|
|||
|
|
@ -38,11 +38,25 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration
|
|||
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...)
|
||||
|
||||
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)
|
||||
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, extraFiles)
|
||||
podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, nil, extraFiles)
|
||||
return &PodmanSessionIntegration{podmanSession}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v3/pkg/rootless"
|
||||
)
|
||||
|
||||
func IsRemote() bool {
|
||||
|
|
@ -23,9 +25,19 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration
|
|||
return &PodmanSessionIntegration{podmanSession}
|
||||
}
|
||||
|
||||
// PodmanSystemdScope runs the podman command in a new systemd scope
|
||||
func (p *PodmanTestIntegration) PodmanSystemdScope(args []string) *PodmanSessionIntegration {
|
||||
wrapper := []string{"systemd-run", "--scope"}
|
||||
if rootless.IsRootless() {
|
||||
wrapper = []string{"systemd-run", "--scope", "--user"}
|
||||
}
|
||||
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 {
|
||||
podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, extraFiles)
|
||||
podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, nil, extraFiles)
|
||||
return &PodmanSessionIntegration{podmanSession}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,19 @@ import (
|
|||
. "github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
func isEventBackendJournald(podmanTest *PodmanTestIntegration) bool {
|
||||
if !podmanTest.RemoteTest {
|
||||
// If not remote test, '--events-backend' is set to 'file' or 'none'
|
||||
return false
|
||||
}
|
||||
info := podmanTest.Podman([]string{"info", "--format", "{{.Host.EventLogger}}"})
|
||||
info.WaitWithDefaultTimeout()
|
||||
if info.OutputToString() == "journald" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var _ = Describe("Podman logs", func() {
|
||||
var (
|
||||
tempdir string
|
||||
|
|
@ -38,8 +51,18 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
for _, log := range []string{"k8s-file", "journald", "json-file"} {
|
||||
// This is important to move the 'log' var to the correct scope under Ginkgo flow.
|
||||
log := log
|
||||
|
||||
skipIfJournaldInContainer := func() {
|
||||
if log == "journald" {
|
||||
SkipIfInContainer("journalctl inside a container doesn't work correctly")
|
||||
}
|
||||
}
|
||||
|
||||
It("all lines: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -53,6 +76,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("tail two lines: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -65,6 +90,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("tail zero lines: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -77,6 +104,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("tail 99 lines: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -89,6 +118,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("tail 800 lines: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "i=1; while [ \"$i\" -ne 1000 ]; do echo \"line $i\"; i=$((i + 1)); done"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -101,6 +132,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("tail 2 lines with timestamps: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -113,6 +146,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("since time 2017-08-07: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -125,6 +160,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("since duration 10m: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -137,6 +174,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("until duration 10m: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -149,6 +188,7 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("until time NOW: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
|
|
@ -165,13 +205,17 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("latest and container name should fail: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
results := podmanTest.Podman([]string{"logs", "-l", "foobar"})
|
||||
results.WaitWithDefaultTimeout()
|
||||
Expect(results).To(ExitWithError())
|
||||
})
|
||||
|
||||
It("two containers showing short container IDs: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
SkipIfRemote("FIXME: podman-remote logs does not support showing two containers at the same time")
|
||||
|
||||
log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||
log1.WaitWithDefaultTimeout()
|
||||
Expect(log1).Should(Exit(0))
|
||||
|
|
@ -192,6 +236,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("podman logs on a created container should result in 0 exit code: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
session := podmanTest.Podman([]string{"create", "--log-driver", log, "-t", "--name", "log", ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(Exit(0))
|
||||
|
|
@ -202,6 +248,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("streaming output: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
containerName := "logs-f"
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-dt", ALPINE, "sh", "-c", "echo podman-1; sleep 1; echo podman-2"})
|
||||
|
|
@ -210,6 +258,14 @@ var _ = Describe("Podman logs", func() {
|
|||
|
||||
results := podmanTest.Podman([]string{"logs", "-f", containerName})
|
||||
results.WaitWithDefaultTimeout()
|
||||
|
||||
if log == "journald" && !isEventBackendJournald(podmanTest) {
|
||||
// --follow + journald log-driver is only supported with journald events-backend(PR #10431)
|
||||
Expect(results).To(Exit(125))
|
||||
Expect(results.ErrorToString()).To(ContainSubstring("using --follow with the journald --log-driver but without the journald --events-backend"))
|
||||
return
|
||||
}
|
||||
|
||||
Expect(results).To(Exit(0))
|
||||
|
||||
Expect(results.OutputToString()).To(ContainSubstring("podman-1"))
|
||||
|
|
@ -233,6 +289,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("follow output stopped container: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
containerName := "logs-f"
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-d", ALPINE, "true"})
|
||||
|
|
@ -241,10 +299,17 @@ var _ = Describe("Podman logs", func() {
|
|||
|
||||
results := podmanTest.Podman([]string{"logs", "-f", containerName})
|
||||
results.WaitWithDefaultTimeout()
|
||||
if log == "journald" && !isEventBackendJournald(podmanTest) {
|
||||
// --follow + journald log-driver is only supported with journald events-backend(PR #10431)
|
||||
Expect(results).To(Exit(125))
|
||||
return
|
||||
}
|
||||
Expect(results).To(Exit(0))
|
||||
})
|
||||
|
||||
It("using container with container log-size: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--log-opt=max-size=10k", "-d", ALPINE, "sh", "-c", "echo podman podman podman"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -266,6 +331,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("Make sure logs match expected length: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-t", "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
Expect(logc).To(Exit(0))
|
||||
|
|
@ -284,6 +351,8 @@ var _ = Describe("Podman logs", func() {
|
|||
})
|
||||
|
||||
It("podman logs test stdout and stderr: "+log, func() {
|
||||
skipIfJournaldInContainer()
|
||||
|
||||
cname := "log-test"
|
||||
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"})
|
||||
logc.WaitWithDefaultTimeout()
|
||||
|
|
|
|||
|
|
@ -301,4 +301,21 @@ var _ = Describe("Podman pod rm", func() {
|
|||
Expect(session).Should(Exit(0))
|
||||
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman pod rm with exited containers", func() {
|
||||
_, ec, podid := podmanTest.CreatePod(nil)
|
||||
Expect(ec).To(Equal(0))
|
||||
|
||||
session := podmanTest.Podman([]string{"run", "--pod", podid, ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "--pod", podid, ALPINE})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
result := podmanTest.Podman([]string{"pod", "rm", podid})
|
||||
result.WaitWithDefaultTimeout()
|
||||
Expect(result).Should(Exit(0))
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -357,6 +357,26 @@ var _ = Describe("Podman run networking", func() {
|
|||
Expect(ncBusy).To(ExitWithError())
|
||||
})
|
||||
|
||||
It("podman run slirp4netns verify net.ipv6.conf.default.accept_dad=0", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:enable_ipv6=true", ALPINE, "ip", "addr"})
|
||||
session.Wait(30)
|
||||
Expect(session).Should(Exit(0))
|
||||
// check the ipv6 setup id done without delay (https://github.com/containers/podman/issues/11062)
|
||||
Expect(session.OutputToString()).To(ContainSubstring("inet6 fd00::"))
|
||||
|
||||
const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/all/accept_dad"
|
||||
|
||||
cat := SystemExec("cat", []string{ipv6ConfDefaultAcceptDadSysctl})
|
||||
cat.Wait(30)
|
||||
Expect(cat).Should(Exit(0))
|
||||
sysctlValue := cat.OutputToString()
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "--network", "slirp4netns:enable_ipv6=true", ALPINE, "cat", ipv6ConfDefaultAcceptDadSysctl})
|
||||
session.Wait(30)
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.OutputToString()).To(Equal(sysctlValue))
|
||||
})
|
||||
|
||||
It("podman run network expose host port 18082 to container port 8000 using slirp4netns port handler", func() {
|
||||
session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=slirp4netns", "-dt", "-p", "18082:8000", ALPINE, "/bin/sh"})
|
||||
session.Wait(30)
|
||||
|
|
|
|||
|
|
@ -1322,13 +1322,13 @@ USER mail`, BB)
|
|||
}
|
||||
}
|
||||
|
||||
container := podmanTest.Podman([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"})
|
||||
container := podmanTest.PodmanSystemdScope([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"})
|
||||
container.WaitWithDefaultTimeout()
|
||||
Expect(container).Should(Exit(0))
|
||||
checkLines(container.OutputToStringArray())
|
||||
|
||||
// check that --cgroups=split is honored also when a container runs in a pod
|
||||
container = podmanTest.Podman([]string{"run", "--rm", "--pod", "new:split-test-pod", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"})
|
||||
container = podmanTest.PodmanSystemdScope([]string{"run", "--rm", "--pod", "new:split-test-pod", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"})
|
||||
container.WaitWithDefaultTimeout()
|
||||
Expect(container).Should(Exit(0))
|
||||
checkLines(container.OutputToStringArray())
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ var _ = Describe("podman system reset", func() {
|
|||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
Expect(session.ErrorToString()).To(Not(ContainSubstring("Failed to add pause process")))
|
||||
|
||||
// If remote then the API service should have exited
|
||||
// On local tests this is a noop
|
||||
podmanTest.StartRemoteService()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/containers/podman/v3/test/utils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
|
@ -82,27 +81,13 @@ WantedBy=multi-user.target
|
|||
run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", ubi_init, "/sbin/init"})
|
||||
run.WaitWithDefaultTimeout()
|
||||
Expect(run).Should(Exit(0))
|
||||
ctrID := run.OutputToString()
|
||||
|
||||
logs := podmanTest.Podman([]string{"logs", ctrName})
|
||||
logs.WaitWithDefaultTimeout()
|
||||
Expect(logs).Should(Exit(0))
|
||||
|
||||
// Give container 10 seconds to start
|
||||
started := false
|
||||
for i := 0; i < 10; i++ {
|
||||
runningCtrs := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"})
|
||||
runningCtrs.WaitWithDefaultTimeout()
|
||||
Expect(runningCtrs).Should(Exit(0))
|
||||
|
||||
if strings.Contains(runningCtrs.OutputToString(), ctrID) {
|
||||
started = true
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
started := podmanTest.WaitContainerReady(ctrName, "Reached target Multi-User System.", 30, 1)
|
||||
Expect(started).To(BeTrue())
|
||||
|
||||
systemctl := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "systemctl", "status", "--no-pager"})
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import (
|
|||
|
||||
var _ = Describe("Podman trust", func() {
|
||||
var (
|
||||
tempdir string
|
||||
tempdir string
|
||||
|
||||
err error
|
||||
podmanTest *PodmanTestIntegration
|
||||
)
|
||||
|
|
@ -38,21 +39,17 @@ var _ = Describe("Podman trust", func() {
|
|||
})
|
||||
|
||||
It("podman image trust show", func() {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Dir(path), "--policypath", filepath.Join(filepath.Dir(path), "policy.json")})
|
||||
session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json")})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
outArray := session.OutputToStringArray()
|
||||
Expect(len(outArray)).To(Equal(3))
|
||||
|
||||
// image order is not guaranteed. All we can do is check that
|
||||
// these strings appear in output, we can't cross-check them.
|
||||
Expect(session.OutputToString()).To(ContainSubstring("accept"))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("reject"))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("signed"))
|
||||
// Repository order is not guaranteed. So, check that
|
||||
// all expected lines appear in output; we also check total number of lines, so that handles all of them.
|
||||
Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^default\s+accept\s*$`))
|
||||
Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^docker.io/library/hello-world\s+reject\s*$`))
|
||||
Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^registry.access.redhat.com\s+signedBy\s+security@redhat.com, security@redhat.com\s+https://access.redhat.com/webassets/docker/content/sigstore\s*$`))
|
||||
})
|
||||
|
||||
It("podman image trust set", func() {
|
||||
|
|
@ -76,24 +73,52 @@ var _ = Describe("Podman trust", func() {
|
|||
})
|
||||
|
||||
It("podman image trust show --json", func() {
|
||||
session := podmanTest.Podman([]string{"image", "trust", "show", "--json"})
|
||||
session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--json"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.IsJSONOutputValid()).To(BeTrue())
|
||||
var teststruct []map[string]string
|
||||
json.Unmarshal(session.Out.Contents(), &teststruct)
|
||||
Expect(teststruct[0]["name"]).To(Equal("* (default)"))
|
||||
Expect(teststruct[0]["repo_name"]).To(Equal("default"))
|
||||
Expect(teststruct[0]["type"]).To(Equal("accept"))
|
||||
Expect(teststruct[1]["type"]).To(Equal("insecureAcceptAnything"))
|
||||
Expect(len(teststruct)).To(Equal(3))
|
||||
// To ease comparison, group the unordered array of repos by repo (and we expect only one entry by repo, so order within groups doesn’t matter)
|
||||
repoMap := map[string][]map[string]string{}
|
||||
for _, e := range teststruct {
|
||||
key := e["name"]
|
||||
repoMap[key] = append(repoMap[key], e)
|
||||
}
|
||||
Expect(repoMap).To(Equal(map[string][]map[string]string{
|
||||
"* (default)": {{
|
||||
"name": "* (default)",
|
||||
"repo_name": "default",
|
||||
"sigstore": "",
|
||||
"transport": "",
|
||||
"type": "accept",
|
||||
}},
|
||||
"docker.io/library/hello-world": {{
|
||||
"name": "docker.io/library/hello-world",
|
||||
"repo_name": "docker.io/library/hello-world",
|
||||
"sigstore": "",
|
||||
"transport": "",
|
||||
"type": "reject",
|
||||
}},
|
||||
"registry.access.redhat.com": {{
|
||||
"name": "registry.access.redhat.com",
|
||||
"repo_name": "registry.access.redhat.com",
|
||||
"sigstore": "https://access.redhat.com/webassets/docker/content/sigstore",
|
||||
"transport": "",
|
||||
"type": "signedBy",
|
||||
"gpg_id": "security@redhat.com, security@redhat.com",
|
||||
}},
|
||||
}))
|
||||
})
|
||||
|
||||
It("podman image trust show --raw", func() {
|
||||
session := podmanTest.Podman([]string{"image", "trust", "show", "--raw"})
|
||||
session := podmanTest.Podman([]string{"image", "trust", "show", "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--raw"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
contents, err := ioutil.ReadFile(filepath.Join(INTEGRATION_ROOT, "test/policy.json"))
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(session.IsJSONOutputValid()).To(BeTrue())
|
||||
Expect(session.OutputToString()).To(ContainSubstring("default"))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("insecureAcceptAnything"))
|
||||
Expect(string(session.Out.Contents())).To(Equal(string(contents) + "\n"))
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ var _ = Describe("Podman version", func() {
|
|||
f := CurrentGinkgoTestDescription()
|
||||
processTestResult(f)
|
||||
podmanTest.SeedImages()
|
||||
|
||||
})
|
||||
|
||||
It("podman version", func() {
|
||||
|
|
@ -96,4 +95,13 @@ var _ = Describe("Podman version", func() {
|
|||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
})
|
||||
|
||||
It("podman help", func() {
|
||||
session := podmanTest.Podman([]string{"help"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(session.Out.Contents()).Should(
|
||||
ContainSubstring("Display the Podman version information"),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -86,6 +86,12 @@ function check_help() {
|
|||
run_podman 125 "$@" $cmd -l nonexistent-container
|
||||
is "$output" "Error: .*--latest and \(containers\|pods\|arguments\) cannot be used together" \
|
||||
"'$command_string' with both -l and container"
|
||||
|
||||
# Combine -l and -a, too (but spell it as --all, because "-a"
|
||||
# means "attach" in podman container start)
|
||||
run_podman 125 "$@" $cmd --all --latest
|
||||
is "$output" "Error: \(--all and --latest cannot be used together\|--all, --latest and containers cannot be used together\|--all, --latest and arguments cannot be used together\|unknown flag\)" \
|
||||
"'$command_string' with both --all and --latest"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ echo $rand | 0 | $rand
|
|||
is "$output" ".*invalidflag" "failed when passing undefined flags to the runtime"
|
||||
}
|
||||
|
||||
@test "podman run --memory=0 runtime option" {
|
||||
run_podman run --memory=0 --rm $IMAGE echo hello
|
||||
is "$output" "hello" "failed to run when --memory is set to 0"
|
||||
}
|
||||
|
||||
# 'run --preserve-fds' passes a number of additional file descriptors into the container
|
||||
@test "podman run --preserve-fds" {
|
||||
skip_if_remote "preserve-fds is meaningless over remote"
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@ load helpers
|
|||
expected="Error: either start all containers or the container(s) provided in the arguments"
|
||||
run_podman 125 start --all 12333
|
||||
is "$output" "$expected" "start --all, with args, throws error"
|
||||
if ! is_remote; then
|
||||
run_podman 125 start --all --latest
|
||||
is "$output" "$expected" "podman start --all --latest"
|
||||
fi
|
||||
}
|
||||
|
||||
@test "podman start --filter - start only containers that match the filter" {
|
||||
|
|
|
|||
|
|
@ -167,6 +167,13 @@ load helpers
|
|||
$IMAGE nc -l -n -v -p $myport
|
||||
cid="$output"
|
||||
|
||||
# FIXME: debugging for #11871
|
||||
run_podman exec $cid cat /etc/resolv.conf
|
||||
if is_rootless; then
|
||||
run_podman unshare --rootless-cni cat /etc/resolv.conf
|
||||
fi
|
||||
ps uxww
|
||||
|
||||
# check that dns is working inside the container
|
||||
run_podman exec $cid nslookup google.com
|
||||
|
||||
|
|
|
|||
|
|
@ -121,8 +121,15 @@ EOF
|
|||
# Clean up vestiges of previous run
|
||||
$PODMAN rm -f podman_parent || true
|
||||
|
||||
# Not entirely a NOP! This is just so we get /run/crun created on a CI VM
|
||||
$PODMAN run --rm $OLD_PODMAN 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
|
||||
|
||||
|
||||
#
|
||||
# Use new-podman to run the above script under old-podman.
|
||||
|
|
@ -134,7 +141,8 @@ EOF
|
|||
#
|
||||
# mount /etc/containers/storage.conf to use the same storage settings as on the host
|
||||
# mount /dev/shm because the container locks are stored there
|
||||
# mount /var/lib/cni and /etc/cni/net.d for cni networking
|
||||
# mount /var/lib/cni, /run/cni and /etc/cni/net.d for cni networking
|
||||
# mount /run/containers for the dnsname plugin
|
||||
#
|
||||
$PODMAN run -d --name podman_parent --pid=host \
|
||||
--privileged \
|
||||
|
|
@ -145,6 +153,8 @@ EOF
|
|||
-v /dev/fuse:/dev/fuse \
|
||||
-v /run/crun:/run/crun \
|
||||
-v /run/netns:/run/netns:rshared \
|
||||
-v /run/containers:/run/containers \
|
||||
-v /run/cni:/run/cni \
|
||||
-v /var/lib/cni:/var/lib/cni \
|
||||
-v /etc/cni/net.d:/etc/cni/net.d \
|
||||
-v /dev/shm:/dev/shm \
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ var _ = Describe("PodmanTest test", func() {
|
|||
FakeOutputs["check"] = []string{"check"}
|
||||
os.Setenv("HOOK_OPTION", "hook_option")
|
||||
env := os.Environ()
|
||||
session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env, true, false, nil)
|
||||
session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env, true, false, nil, nil)
|
||||
os.Unsetenv("HOOK_OPTION")
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.Command.Process).ShouldNot(BeNil())
|
||||
|
|
|
|||
|
|
@ -66,27 +66,31 @@ func (p *PodmanTest) MakeOptions(args []string, noEvents, noCache bool) []string
|
|||
|
||||
// PodmanAsUserBase exec podman as user. uid and gid is set for credentials usage. env is used
|
||||
// to record the env for debugging
|
||||
func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string, noEvents, noCache bool, extraFiles []*os.File) *PodmanSession {
|
||||
func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string, noEvents, noCache bool, wrapper []string, extraFiles []*os.File) *PodmanSession {
|
||||
var command *exec.Cmd
|
||||
podmanOptions := p.MakeOptions(args, noEvents, noCache)
|
||||
podmanBinary := p.PodmanBinary
|
||||
if p.RemoteTest {
|
||||
podmanBinary = p.RemotePodmanBinary
|
||||
}
|
||||
|
||||
runCmd := append(wrapper, podmanBinary)
|
||||
if p.RemoteTest {
|
||||
podmanOptions = append([]string{"--remote", "--url", p.RemoteSocket}, podmanOptions...)
|
||||
}
|
||||
if env == nil {
|
||||
fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(podmanOptions, " "))
|
||||
fmt.Printf("Running: %s %s\n", strings.Join(runCmd, " "), strings.Join(podmanOptions, " "))
|
||||
} else {
|
||||
fmt.Printf("Running: (env: %v) %s %s\n", env, podmanBinary, strings.Join(podmanOptions, " "))
|
||||
fmt.Printf("Running: (env: %v) %s %s\n", env, strings.Join(runCmd, " "), strings.Join(podmanOptions, " "))
|
||||
}
|
||||
if uid != 0 || gid != 0 {
|
||||
pythonCmd := fmt.Sprintf("import os; import sys; uid = %d; gid = %d; cwd = '%s'; os.setgid(gid); os.setuid(uid); os.chdir(cwd) if len(cwd)>0 else True; os.execv(sys.argv[1], sys.argv[1:])", gid, uid, cwd)
|
||||
nsEnterOpts := append([]string{"-c", pythonCmd, podmanBinary}, podmanOptions...)
|
||||
runCmd = append(runCmd, podmanOptions...)
|
||||
nsEnterOpts := append([]string{"-c", pythonCmd}, runCmd...)
|
||||
command = exec.Command("python", nsEnterOpts...)
|
||||
} else {
|
||||
command = exec.Command(podmanBinary, podmanOptions...)
|
||||
runCmd = append(runCmd, podmanOptions...)
|
||||
command = exec.Command(runCmd[0], runCmd[1:]...)
|
||||
}
|
||||
if env != nil {
|
||||
command.Env = env
|
||||
|
|
@ -106,7 +110,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string
|
|||
|
||||
// PodmanBase exec podman with default env.
|
||||
func (p *PodmanTest) PodmanBase(args []string, noEvents, noCache bool) *PodmanSession {
|
||||
return p.PodmanAsUserBase(args, 0, 0, "", nil, noEvents, noCache, nil)
|
||||
return p.PodmanAsUserBase(args, 0, 0, "", nil, noEvents, noCache, nil, nil)
|
||||
}
|
||||
|
||||
// WaitForContainer waits on a started container
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ In cases where the container image runs as a specific, non-root user, though, th
|
|||
solution is to fix the user namespace. This would include container images such as
|
||||
the Jupyter Notebook image (which runs as "jovyan") and the Postgres image (which runs
|
||||
as "postgres"). In either case, use the `--userns` switch to map user namespaces,
|
||||
most of the time by using keep_id option.
|
||||
most of the time by using the **keep-id** option.
|
||||
|
||||
$ podman run -v "$PWD":/home/jovyan/work --userns=keep_id jupyter/scipy-notebook
|
||||
$ podman run -v "$PWD":/home/jovyan/work --userns=keep-id jupyter/scipy-notebook
|
||||
|
||||
---
|
||||
### 3) No such image or Bare keys cannot contain ':'
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/cgroups"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -177,13 +178,26 @@ func RunsOnSystemd() bool {
|
|||
func moveProcessToScope(pidPath, slice, scope string) error {
|
||||
data, err := ioutil.ReadFile(pidPath)
|
||||
if err != nil {
|
||||
// do not raise an error if the file doesn't exist
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(err, "cannot read pid file %s", pidPath)
|
||||
}
|
||||
pid, err := strconv.ParseUint(string(data), 10, 0)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot parse pid file %s", pidPath)
|
||||
}
|
||||
return RunUnderSystemdScope(int(pid), slice, scope)
|
||||
err = RunUnderSystemdScope(int(pid), slice, scope)
|
||||
|
||||
// If the PID is not valid anymore, do not return an error.
|
||||
if dbusErr, ok := err.(dbus.Error); ok {
|
||||
if dbusErr.Name == "org.freedesktop.DBus.Error.UnixProcessIdUnknown" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to
|
||||
|
|
@ -191,8 +205,8 @@ func moveProcessToScope(pidPath, slice, scope string) error {
|
|||
func MovePauseProcessToScope(pausePidPath string) {
|
||||
err := moveProcessToScope(pausePidPath, "user.slice", "podman-pause.scope")
|
||||
if err != nil {
|
||||
unified, err := cgroups.IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
unified, err2 := cgroups.IsCgroup2UnifiedMode()
|
||||
if err2 != nil {
|
||||
logrus.Warnf("Failed to detect if running with cgroup unified: %v", err)
|
||||
}
|
||||
if RunsOnSystemd() && unified {
|
||||
|
|
|
|||
2
vendor/github.com/godbus/dbus/v5/auth.go
generated
vendored
2
vendor/github.com/godbus/dbus/v5/auth.go
generated
vendored
|
|
@ -53,7 +53,7 @@ type Auth interface {
|
|||
// bus. Auth must not be called on shared connections.
|
||||
func (conn *Conn) Auth(methods []Auth) error {
|
||||
if methods == nil {
|
||||
uid := strconv.Itoa(os.Getuid())
|
||||
uid := strconv.Itoa(os.Geteuid())
|
||||
methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
|
||||
}
|
||||
in := bufio.NewReader(conn.transport)
|
||||
|
|
|
|||
22
vendor/github.com/godbus/dbus/v5/conn.go
generated
vendored
22
vendor/github.com/godbus/dbus/v5/conn.go
generated
vendored
|
|
@ -73,7 +73,7 @@ func SessionBus() (conn *Conn, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func getSessionBusAddress() (string, error) {
|
||||
func getSessionBusAddress(autolaunch bool) (string, error) {
|
||||
if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
|
||||
return address, nil
|
||||
|
||||
|
|
@ -81,12 +81,26 @@ func getSessionBusAddress() (string, error) {
|
|||
os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
|
||||
return address, nil
|
||||
}
|
||||
if !autolaunch {
|
||||
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||
}
|
||||
return getSessionBusPlatformAddress()
|
||||
}
|
||||
|
||||
// SessionBusPrivate returns a new private connection to the session bus.
|
||||
func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
|
||||
address, err := getSessionBusAddress()
|
||||
address, err := getSessionBusAddress(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Dial(address, opts...)
|
||||
}
|
||||
|
||||
// SessionBusPrivate returns a new private connection to the session bus. If
|
||||
// the session bus is not already open, do not attempt to launch it.
|
||||
func SessionBusPrivateNoAutoStartup(opts ...ConnOption) (*Conn, error) {
|
||||
address, err := getSessionBusAddress(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -121,7 +135,7 @@ func SystemBus() (conn *Conn, err error) {
|
|||
|
||||
// ConnectSessionBus connects to the session bus.
|
||||
func ConnectSessionBus(opts ...ConnOption) (*Conn, error) {
|
||||
address, err := getSessionBusAddress()
|
||||
address, err := getSessionBusAddress(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -180,7 +194,7 @@ func Dial(address string, opts ...ConnOption) (*Conn, error) {
|
|||
//
|
||||
// Deprecated: use Dial with options instead.
|
||||
func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||
return Dial(address, WithSignalHandler(signalHandler))
|
||||
return Dial(address, WithHandler(handler), WithSignalHandler(signalHandler))
|
||||
}
|
||||
|
||||
// ConnOption is a connection option.
|
||||
|
|
|
|||
4
vendor/github.com/godbus/dbus/v5/message.go
generated
vendored
4
vendor/github.com/godbus/dbus/v5/message.go
generated
vendored
|
|
@ -279,8 +279,8 @@ func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds
|
|||
// be either binary.LittleEndian or binary.BigEndian. If the message is not
|
||||
// valid or an error occurs when writing, an error is returned.
|
||||
func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) (err error) {
|
||||
_, err = msg.EncodeToWithFDs(out, order);
|
||||
return err;
|
||||
_, err = msg.EncodeToWithFDs(out, order)
|
||||
return err
|
||||
}
|
||||
|
||||
// IsValid checks whether msg is a valid message and returns an
|
||||
|
|
|
|||
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
generated
vendored
Normal file
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package dbus
|
||||
|
||||
import "io"
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
|
|
@ -353,7 +353,7 @@ github.com/ghodss/yaml
|
|||
github.com/go-logr/logr
|
||||
# github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
||||
github.com/go-task/slim-sprig
|
||||
# github.com/godbus/dbus/v5 v5.0.5
|
||||
# github.com/godbus/dbus/v5 v5.0.6
|
||||
github.com/godbus/dbus/v5
|
||||
# github.com/gogo/protobuf v1.3.2
|
||||
github.com/gogo/protobuf/gogoproto
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const (
|
|||
// NOTE: remember to bump the version at the top
|
||||
// of the top-level README.md file when this is
|
||||
// bumped.
|
||||
var Version = semver.MustParse("3.4.2-dev")
|
||||
var Version = semver.MustParse("3.4.3-dev")
|
||||
|
||||
// See https://docs.docker.com/engine/api/v1.40/
|
||||
// libpod compat handlers are expected to honor docker API versions
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue