mirror of
https://github.com/containers/podman.git
synced 2025-06-23 18:59:30 +08:00
Merge branch 'master' of github.com:containers/libpod into vendor
This commit is contained in:
57
.cirrus.yml
57
.cirrus.yml
@ -18,9 +18,9 @@ gce_instance:
|
||||
env:
|
||||
FEDORA_CNI_COMMIT: "412b6d31280682bb4fab4446f113c22ff1886554"
|
||||
CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9"
|
||||
CRIO_COMMIT: "662dbb31b5d4f5ed54511a47cde7190c61c28677"
|
||||
CRIU_COMMIT: "584cbe4643c3fc7dc901ff08bf923ca0fe7326f9"
|
||||
RUNC_COMMIT: "78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d"
|
||||
CRIO_COMMIT: "7a283c391abb7bd25086a8ff91dbb36ebdd24466"
|
||||
CRIU_COMMIT: "c74b83cd49c00589c0c0468ba5fe685b67fdbd0a"
|
||||
RUNC_COMMIT: "96ec2177ae841256168fcf76954f7177af9446eb"
|
||||
# File to update in home-dir with task-specific env. var values
|
||||
ENVLIB: ".bash_profile"
|
||||
# Overrides default location (/tmp/cirrus) for repo clone
|
||||
@ -42,11 +42,12 @@ full_vm_testing_task:
|
||||
# 'matrix' combinations. All run in parallel.
|
||||
matrix:
|
||||
# Images are generated separetly, from build_images_task (below)
|
||||
image_name: "ubuntu-1804-bionic-v20180911-libpod-63a86a18"
|
||||
image_name: "ubuntu-18-libpod-0c954a67"
|
||||
# TODO: Make these work (also build_images_task below)
|
||||
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
|
||||
#image_name: "centos-7-v20180911-libpod-fce09afe"
|
||||
#image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe"
|
||||
|
||||
timeout_in: 120m
|
||||
|
||||
# Every *_script runs in sequence, for each task. The name prefix is for
|
||||
@ -65,19 +66,49 @@ full_vm_testing_task:
|
||||
success_script: $SCRIPT_BASE/success.sh
|
||||
|
||||
|
||||
# This task build new images for future PR testing, but only after a PR merge.
|
||||
# These images save needing to install/setup the same environment to test every
|
||||
# PR. The 'active' image for testing is selected by the 'image_name' items in
|
||||
# task above. Currently this requires manually updating them, but this could
|
||||
# be automated (see comment at end).
|
||||
# Because system tests are stored within the repository, it is sometimes
|
||||
# necessary to execute them within a PR to validate changes.
|
||||
|
||||
optional_system_testing_task:
|
||||
|
||||
# Only run system tests in PRs (not on merge) if magic string is present
|
||||
# in the PR description. Post-merge system testing is assumed to happen
|
||||
# later from OS distribution's build systems.
|
||||
only_if: >-
|
||||
$CIRRUS_BRANCH != 'master' &&
|
||||
$CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*.*'
|
||||
|
||||
gce_instance:
|
||||
matrix:
|
||||
image_name: "ubuntu-1804-bionic-v20180911-libpod-e8d18305"
|
||||
# TODO: Make these work (also build_images_task below)
|
||||
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
|
||||
#image_name: "centos-7-v20180911-libpod-fce09afe"
|
||||
#image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe"
|
||||
|
||||
timeout_in: 60m
|
||||
|
||||
setup_environment_script: $SCRIPT_BASE/setup_environment.sh
|
||||
system_test_script: $SCRIPT_BASE/system_test.sh
|
||||
success_script: $SCRIPT_BASE/success.sh
|
||||
|
||||
|
||||
# This task builds new cache-images for future PR testing. These images save
|
||||
# time installing/setting up the environment while an engineer is waiting.
|
||||
# The 'active' cache-images for full_vm_testing are selected by the
|
||||
# 'image_name' keys. Updating those items requires manually modification,
|
||||
# but this could be automated (see comment at end of build_vm_images_task).
|
||||
|
||||
build_vm_images_task:
|
||||
# Only produce new images after a PR merge
|
||||
only_if: $CIRRUS_BRANCH == 'master'
|
||||
# Only produce new cache-images after a PR merge, and if a magic string
|
||||
# is present in the most recent commit-message.
|
||||
only_if: >-
|
||||
$CIRRUS_BRANCH == 'master' &&
|
||||
$CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*REBUILD\s*IMAGES\s*\*\*\*.*'
|
||||
|
||||
# Require tests to pass first.
|
||||
depends_on:
|
||||
- test # i.e. 'test_task'
|
||||
- full_vm_testing # i.e. 'full_vm_testing_task'
|
||||
|
||||
env:
|
||||
# CSV of packer builder names to enable (see $PACKER_BASE/libpod_images.json)
|
||||
@ -99,6 +130,8 @@ build_vm_images_task:
|
||||
# Version of packer to use
|
||||
PACKER_VER: "1.3.1"
|
||||
|
||||
# VMs created by packer are not cleaned up by cirrus
|
||||
auto_cancellation: $CI != "true"
|
||||
|
||||
gce_instance:
|
||||
image_name: "image-builder-image" # Simply CentOS 7 + packer dependencies
|
||||
|
3
.papr.sh
3
.papr.sh
@ -139,6 +139,3 @@ if [ $integrationtest -eq 1 ]; then
|
||||
fi
|
||||
make ginkgo GOPATH=/go $INTEGRATION_TEST_ENVS
|
||||
fi
|
||||
|
||||
|
||||
exit 0
|
||||
|
@ -122,7 +122,7 @@ packages:
|
||||
- python3-varlink
|
||||
- python3-dateutil
|
||||
- python3-psutil
|
||||
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/55.dev.git578fe65.fc28/x86_64/runc-1.0.0-55.dev.git578fe65.fc28.x86_64.rpm
|
||||
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/54.dev.git00dc700.fc28/x86_64/runc-1.0.0-54.dev.git00dc700.fc28.x86_64.rpm
|
||||
|
||||
tests:
|
||||
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf
|
||||
@ -163,6 +163,7 @@ packages:
|
||||
- python3-dateutil
|
||||
- python3-psutil
|
||||
- container-selinux
|
||||
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/54.dev.git00dc700.fc28/x86_64/runc-1.0.0-54.dev.git00dc700.fc28.x86_64.rpm
|
||||
|
||||
tests:
|
||||
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf
|
||||
|
@ -10,6 +10,13 @@ if [[ ${DIST} != "Fedora" ]]; then
|
||||
PYTHON=python
|
||||
fi
|
||||
|
||||
# Since CRIU 3.11 has been pushed to Fedora 28 the checkpoint/restore
|
||||
# test cases are actually run. As CRIU uses iptables to lock and unlock
|
||||
# the network during checkpoint and restore it needs the following two
|
||||
# modules loaded.
|
||||
modprobe ip6table_nat || :
|
||||
modprobe iptable_nat || :
|
||||
|
||||
# Build the test image
|
||||
${CONTAINER_RUNTIME} build -t ${IMAGE} -f Dockerfile.${DIST} . 2>build.log
|
||||
|
||||
|
@ -40,6 +40,8 @@ ${LINTER} \
|
||||
--exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$'\
|
||||
--exclude='duplicate of.*_test.go.*\(dupl\)$'\
|
||||
--exclude='cmd\/client\/.*\.go.*\(dupl\)$'\
|
||||
--exclude='libpod\/.*_easyjson.go:.*'\
|
||||
--exclude='.* other occurrence\(s\) of "(container|host|tmpfs|unknown)" found in: .*\(goconst\)$'\
|
||||
--exclude='vendor\/.*'\
|
||||
--exclude='podman\/.*'\
|
||||
--exclude='server\/seccomp\/.*\.go.*$'\
|
||||
|
103
API.md
103
API.md
@ -9,6 +9,14 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) string](#Commit)
|
||||
|
||||
[func ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) string](#ContainerCheckpoint)
|
||||
|
||||
[func ContainerExists(name: string) int](#ContainerExists)
|
||||
|
||||
[func ContainerRestore(name: string, keep: bool, tcpEstablished: bool) string](#ContainerRestore)
|
||||
|
||||
[func ContainerRunlabel(runlabel: Runlabel) ](#ContainerRunlabel)
|
||||
|
||||
[func CreateContainer(create: Create) string](#CreateContainer)
|
||||
|
||||
[func CreateImage() NotImplemented](#CreateImage)
|
||||
@ -43,6 +51,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func HistoryImage(name: string) ImageHistory](#HistoryImage)
|
||||
|
||||
[func ImageExists(name: string) int](#ImageExists)
|
||||
|
||||
[func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage)
|
||||
|
||||
[func InspectContainer(name: string) string](#InspectContainer)
|
||||
@ -57,6 +67,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func ListContainerChanges(name: string) ContainerChanges](#ListContainerChanges)
|
||||
|
||||
[func ListContainerMounts() []string](#ListContainerMounts)
|
||||
|
||||
[func ListContainerPorts(name: string) NotImplemented](#ListContainerPorts)
|
||||
|
||||
[func ListContainerProcesses(name: string, opts: []string) []string](#ListContainerProcesses)
|
||||
|
||||
[func ListContainers() ListContainerData](#ListContainers)
|
||||
@ -65,6 +79,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func ListPods() ListPodData](#ListPods)
|
||||
|
||||
[func MountContainer(name: string) string](#MountContainer)
|
||||
|
||||
[func PauseContainer(name: string) string](#PauseContainer)
|
||||
|
||||
[func PausePod(name: string) string](#PausePod)
|
||||
@ -103,6 +119,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func TopPod() NotImplemented](#TopPod)
|
||||
|
||||
[func UnmountContainer(name: string, force: bool) ](#UnmountContainer)
|
||||
|
||||
[func UnpauseContainer(name: string) string](#UnpauseContainer)
|
||||
|
||||
[func UnpausePod(name: string) string](#UnpausePod)
|
||||
@ -165,6 +183,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[type PodmanInfo](#PodmanInfo)
|
||||
|
||||
[type Runlabel](#Runlabel)
|
||||
|
||||
[type Sockets](#Sockets)
|
||||
|
||||
[type StringResponse](#StringResponse)
|
||||
@ -211,6 +231,31 @@ attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOL
|
||||
container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot
|
||||
be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise,
|
||||
the resulting image's ID will be returned as a string.
|
||||
### <a name="ContainerCheckpoint"></a>func ContainerCheckpoint
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ContainerCheckpoint(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), leaveRunning: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
|
||||
ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container
|
||||
ID. On successful checkpoint, the id of the checkpointed container is returned.
|
||||
### <a name="ContainerExists"></a>func ContainerExists
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ContainerExists(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div>
|
||||
ContainerExists takes a full or partial container ID or name and returns an int as to
|
||||
whether the container exists in local storage. A result of 0 means the container does
|
||||
exists; whereas a result of 1 means it could not be found.
|
||||
### <a name="ContainerRestore"></a>func ContainerRestore
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ContainerRestore(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
|
||||
ContainerRestore restores a container that has been checkpointed. The container to be restored can
|
||||
be identified by its name or full/partial container ID. A successful restore will result in the return
|
||||
of the container's ID.
|
||||
### <a name="ContainerRunlabel"></a>func ContainerRunlabel
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ContainerRunlabel(runlabel: [Runlabel](#Runlabel)) </div>
|
||||
ContainerRunlabel runs executes a command as described by a given container image label.
|
||||
### <a name="CreateContainer"></a>func CreateContainer
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
@ -403,6 +448,13 @@ method HistoryImage(name: [string](https://godoc.org/builtin#string)) [ImageHist
|
||||
HistoryImage takes the name or ID of an image and returns information about its history and layers. The returned
|
||||
history is in the form of an array of ImageHistory structures. If the image cannot be found, an
|
||||
[ImageNotFound](#ImageNotFound) error is returned.
|
||||
### <a name="ImageExists"></a>func ImageExists
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ImageExists(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div>
|
||||
ImageExists talks a full or partial image ID or name and returns an int as to whether
|
||||
the image exists in local storage. An int result of 0 means the image does exist in
|
||||
local storage; whereas 1 indicates the image does not exists in local storage.
|
||||
### <a name="ImportImage"></a>func ImportImage
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
@ -453,6 +505,17 @@ See also [StopPod](StopPod).
|
||||
method ListContainerChanges(name: [string](https://godoc.org/builtin#string)) [ContainerChanges](#ContainerChanges)</div>
|
||||
ListContainerChanges takes a name or ID of a container and returns changes between the container and
|
||||
its base image. It returns a struct of changed, deleted, and added path names.
|
||||
### <a name="ListContainerMounts"></a>func ListContainerMounts
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ListContainerMounts() [[]string](#[]string)</div>
|
||||
ListContainerMounts gathers all the mounted container mount points and returns them as an array
|
||||
of strings
|
||||
### <a name="ListContainerPorts"></a>func ListContainerPorts
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ListContainerPorts(name: [string](https://godoc.org/builtin#string)) [NotImplemented](#NotImplemented)</div>
|
||||
This function is not implemented yet.
|
||||
### <a name="ListContainerProcesses"></a>func ListContainerProcesses
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
@ -491,6 +554,12 @@ an image currently in storage. See also [InspectImage](InspectImage).
|
||||
method ListPods() [ListPodData](#ListPodData)</div>
|
||||
ListPods returns a list of pods in no particular order. They are
|
||||
returned as an array of ListPodData structs. See also [GetPod](#GetPod).
|
||||
### <a name="MountContainer"></a>func MountContainer
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method MountContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
|
||||
MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination
|
||||
mount is returned as a string.
|
||||
### <a name="PauseContainer"></a>func PauseContainer
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
@ -696,6 +765,11 @@ be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise,
|
||||
|
||||
method TopPod() [NotImplemented](#NotImplemented)</div>
|
||||
This method has not been implemented yet.
|
||||
### <a name="UnmountContainer"></a>func UnmountContainer
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method UnmountContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) </div>
|
||||
UnmountContainer umounts a container by its name or full/partial container ID.
|
||||
### <a name="UnpauseContainer"></a>func UnpauseContainer
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
@ -1293,6 +1367,33 @@ insecure_registries [[]string](#[]string)
|
||||
store [InfoStore](#InfoStore)
|
||||
|
||||
podman [InfoPodmanBinary](#InfoPodmanBinary)
|
||||
### <a name="Runlabel"></a>type Runlabel
|
||||
|
||||
Runlabel describes the required input for container runlabel
|
||||
|
||||
image [string](https://godoc.org/builtin#string)
|
||||
|
||||
authfile [string](https://godoc.org/builtin#string)
|
||||
|
||||
certDir [string](https://godoc.org/builtin#string)
|
||||
|
||||
creds [string](https://godoc.org/builtin#string)
|
||||
|
||||
display [bool](https://godoc.org/builtin#bool)
|
||||
|
||||
name [string](https://godoc.org/builtin#string)
|
||||
|
||||
pull [bool](https://godoc.org/builtin#bool)
|
||||
|
||||
signaturePolicyPath [string](https://godoc.org/builtin#string)
|
||||
|
||||
tlsVerify [bool](https://godoc.org/builtin#bool)
|
||||
|
||||
label [string](https://godoc.org/builtin#string)
|
||||
|
||||
extraArgs [[]string](#[]string)
|
||||
|
||||
opts [map[string]](#map[string])
|
||||
### <a name="Sockets"></a>type Sockets
|
||||
|
||||
Sockets describes sockets location for a container
|
||||
@ -1336,7 +1437,7 @@ ImageNotFound means the image could not be found by the provided name or ID in l
|
||||
NoContainerRunning means none of the containers requested are running in a command that requires a running container.
|
||||
### <a name="NoContainersInPod"></a>type NoContainersInPod
|
||||
|
||||
NoContainersInPod means a pod has no containers on which to perform operation. It contains
|
||||
NoContainersInPod means a pod has no containers on which to perform the operation. It contains
|
||||
the pod ID.
|
||||
### <a name="PodContainerError"></a>type PodContainerError
|
||||
|
||||
|
@ -180,6 +180,27 @@ Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
|
||||
### Go Format and lint
|
||||
|
||||
All code changes must pass ``make validate`` and ``make lint``, as
|
||||
executed in a standard container. The container image for this
|
||||
purpose is provided at: ``quay.io/libpod/gate:latest``. However,
|
||||
for changes to the image itself, it may also be built locally
|
||||
from the repository root, with the command:
|
||||
|
||||
```
|
||||
sudo podman build -t quay.io/libpod/gate:latest -f contrib/gate/Dockerfile .
|
||||
```
|
||||
|
||||
The container executes 'make' by default, on a copy of the repository.
|
||||
This avoids changing or leaving build artifacts in your working directory.
|
||||
Execution does not require any special permissions from the host. However,
|
||||
the repository root must be bind-mounted into the container at
|
||||
'/usr/src/libpod'. For example, running `make lint` is done (from
|
||||
the repository root) with the command:
|
||||
|
||||
``sudo podman run -it --rm -v $PWD:/usr/src/libpod:z quay.io/libpod/gate:latest lint``
|
||||
|
||||
### Integration Tests
|
||||
|
||||
Our primary means of performing integration testing for libpod is with the
|
||||
|
@ -52,7 +52,7 @@ ADD . /go/src/github.com/containers/libpod
|
||||
RUN set -x && cd /go/src/github.com/containers/libpod && make install.libseccomp.sudo
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d
|
||||
ENV RUNC_COMMIT 96ec2177ae841256168fcf76954f7177af9446eb
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
@ -64,7 +64,7 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install conmon
|
||||
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
|
||||
ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \
|
||||
@ -112,8 +112,7 @@ RUN set -x \
|
||||
&& go get -u github.com/mailru/easyjson/... \
|
||||
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
|
||||
|
||||
# Install criu
|
||||
ENV CRIU_COMMIT 584cbe4643c3fc7dc901ff08bf923ca0fe7326f9
|
||||
# Install latest stable criu version
|
||||
RUN set -x \
|
||||
&& cd /tmp \
|
||||
&& git clone https://github.com/checkpoint-restore/criu.git \
|
||||
|
@ -68,7 +68,7 @@ RUN set -x \
|
||||
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
|
||||
|
||||
# Install conmon
|
||||
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
|
||||
ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \
|
||||
|
@ -17,7 +17,7 @@ RUN dnf -y install btrfs-progs-devel \
|
||||
libseccomp-devel \
|
||||
libselinux-devel \
|
||||
skopeo-containers \
|
||||
https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/55.dev.git578fe65.fc28/x86_64/runc-1.0.0-55.dev.git578fe65.fc28.x86_64.rpm \
|
||||
runc \
|
||||
make \
|
||||
ostree-devel \
|
||||
python \
|
||||
@ -72,7 +72,7 @@ RUN set -x \
|
||||
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
|
||||
|
||||
# Install conmon
|
||||
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
|
||||
ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \
|
||||
|
25
Makefile
25
Makefile
@ -1,6 +1,6 @@
|
||||
GO ?= go
|
||||
DESTDIR ?= /
|
||||
EPOCH_TEST_COMMIT ?= 733cfe96819e1dc044e982b5321b3c902d1a47c6
|
||||
EPOCH_TEST_COMMIT ?= 1b52843cfd2ae254a6e52c74e564730f1c875c4c
|
||||
HEAD ?= HEAD
|
||||
CHANGELOG_BASE ?= HEAD~
|
||||
CHANGELOG_TARGET ?= HEAD
|
||||
@ -23,7 +23,7 @@ BUILDTAGS_CROSS ?= containers_image_openpgp containers_image_ostree_stub exclude
|
||||
ifneq (,$(findstring varlink,$(BUILDTAGS)))
|
||||
PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go
|
||||
endif
|
||||
CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null | echo docker)
|
||||
CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker)
|
||||
|
||||
HAS_PYTHON3 := $(shell command -v python3 2>/dev/null)
|
||||
|
||||
@ -31,12 +31,13 @@ BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
|
||||
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
|
||||
|
||||
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
|
||||
PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e)
|
||||
PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e | grep -v system )
|
||||
|
||||
COMMIT_NO ?= $(shell git rev-parse HEAD 2> /dev/null || true)
|
||||
GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
|
||||
BUILD_INFO ?= $(shell date +%s)
|
||||
LDFLAGS_PODMAN ?= $(LDFLAGS) -X main.gitCommit=$(GIT_COMMIT) -X main.buildInfo=$(BUILD_INFO)
|
||||
LIBPOD := ${PROJECT}/libpod
|
||||
LDFLAGS_PODMAN ?= $(LDFLAGS) -X $(LIBPOD).gitCommit=$(GIT_COMMIT) -X $(LIBPOD).buildInfo=$(BUILD_INFO)
|
||||
ISODATE ?= $(shell date --iso-8601)
|
||||
LIBSECCOMP_COMMIT := release-2.3
|
||||
|
||||
@ -103,6 +104,9 @@ test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go)
|
||||
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
|
||||
$(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
|
||||
|
||||
test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go)
|
||||
$(GO) build -ldflags '$(LDFLAGS)' -o $@ $(PROJECT)/test/goecho
|
||||
|
||||
podman: .gopathok $(PODMAN_VARLINK_DEPENDENCIES)
|
||||
$(GO) build -i -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman
|
||||
|
||||
@ -129,6 +133,7 @@ clean:
|
||||
test/bin2img/bin2img \
|
||||
test/checkseccomp/checkseccomp \
|
||||
test/copyimg/copyimg \
|
||||
test/goecho/goecho \
|
||||
test/testdata/redis-image \
|
||||
cmd/podman/varlink/iopodman.go \
|
||||
libpod/container_ffjson.go \
|
||||
@ -165,7 +170,7 @@ shell: libpodimage
|
||||
testunit: libpodimage
|
||||
${CONTAINER_RUNTIME} run -e STORAGE_OPTIONS="--storage-driver=vfs" -e TESTFLAGS -e CGROUP_MANAGER=cgroupfs -e TRAVIS -t --privileged --rm -v ${CURDIR}:/go/src/${PROJECT} ${LIBPOD_IMAGE} make localunit
|
||||
|
||||
localunit: varlink_generate
|
||||
localunit: test/goecho/goecho varlink_generate
|
||||
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
|
||||
|
||||
ginkgo:
|
||||
@ -173,6 +178,12 @@ ginkgo:
|
||||
|
||||
localintegration: varlink_generate test-binaries clientintegration ginkgo
|
||||
|
||||
localsystem: .install.ginkgo .install.gomega
|
||||
ginkgo -v -noColor test/system/
|
||||
|
||||
system.test-binary: .install.ginkgo .install.gomega
|
||||
$(GO) test -c ./test/system
|
||||
|
||||
clientintegration:
|
||||
$(MAKE) -C contrib/python/podman integration
|
||||
$(MAKE) -C contrib/python/pypodman integration
|
||||
@ -182,7 +193,7 @@ vagrant-check:
|
||||
|
||||
binaries: varlink_generate easyjson_generate podman
|
||||
|
||||
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
|
||||
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp test/goecho/goecho
|
||||
|
||||
MANPAGES_MD ?= $(wildcard docs/*.md pkg/*/docs/*.md)
|
||||
MANPAGES ?= $(MANPAGES_MD:%.md=%)
|
||||
@ -282,7 +293,7 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man .ins
|
||||
if [ ! -x "$(GOBIN)/gometalinter" ]; then \
|
||||
$(GO) get -u github.com/alecthomas/gometalinter; \
|
||||
cd $(FIRST_GOPATH)/src/github.com/alecthomas/gometalinter; \
|
||||
git checkout 23261fa046586808612c61da7a81d75a658e0814; \
|
||||
git checkout e8d801238da6f0dfd14078d68f9b53fa50a7eeb5; \
|
||||
$(GO) install github.com/alecthomas/gometalinter; \
|
||||
$(GOBIN)/gometalinter --install; \
|
||||
fi
|
||||
|
5
OWNERS
5
OWNERS
@ -3,9 +3,14 @@ approvers:
|
||||
- baude
|
||||
- mrunalp
|
||||
- rhatdan
|
||||
- TomSweeneyRedHat
|
||||
- umohnani8
|
||||
- giuseppe
|
||||
- vrothberg
|
||||
reviewers:
|
||||
- mheon
|
||||
- baude
|
||||
- mrunalp
|
||||
- rhatdan
|
||||
- TomSweeneyRedHat
|
||||
- umohnani8
|
||||
|
@ -1,9 +1,11 @@
|
||||

|
||||
# libpod - library for running OCI-based containers in Pods
|
||||
|
||||
### Latest Version: 0.10.1.3
|
||||
### Latest Version: 0.11.1.1
|
||||
### Status: Active Development
|
||||
|
||||
### Continuous Integration: [](https://cirrus-ci.com/github/containers/libpod)
|
||||
|
||||
## What is the scope of this project?
|
||||
|
||||
libpod provides a library for applications looking to use the Container Pod concept popularized by Kubernetes.
|
||||
|
@ -1,5 +1,53 @@
|
||||
# Release Notes
|
||||
|
||||
## 0.11.1.1
|
||||
### Bugfixes
|
||||
- Fixed a bug where Podman was not correctly adding firewall rules for containers, preventing them from accessing the network
|
||||
- Fixed a bug where full error messages were being lost when creating containers with user namespaces
|
||||
- Fixed a bug where container state was not properly updated if a failure occurred during network setup, which could cause mounts to be left behind when the container was removed
|
||||
- Fixed a bug where `podman exec` could time out on slower systems by increasing the relevant timeout
|
||||
|
||||
### Misc
|
||||
- `podman rm -f` now removes paused containers. As such, `podman rm -af` completing successfully guarantees all Podman containers have been removed
|
||||
- Added a field to `podman info` to show if Podman is being run as rootless
|
||||
- Made a small output format change to `podman images` - image sizes now feature a space between number and unit (e.g. `123 MB` now instead of `123MB`)
|
||||
- Vendored an updated version of `containers/storage` to fix several bugs reported upstream
|
||||
|
||||
## 0.11.1
|
||||
### Features
|
||||
- Added `--all` and `--latest` flags to `podman checkpoint` and `podman restore`
|
||||
- Added `--max-workers` flag to all Podman commands that support operating in parallel, allowing the maximum number of parallel workers used to be specified
|
||||
- Added `--all` flag to `podman restart`
|
||||
|
||||
### Bugfixes
|
||||
- Fixed a bug where `podman port -l` would segfault if no containers were present
|
||||
- Fixed a bug where `podman stats -a` would error if containers were present but not running
|
||||
- Fixed a bug where container status checks would sometimes leave zombie OCI runtime processes
|
||||
- Fixed checkpoint and restore code to verify an appropriate version of `criu` is being used
|
||||
- Fixed a bug where environment variables with no specified value (e.g. `-e FOO`) caused errors (they are now added as empty)
|
||||
- Fixed a bug where rootless Podman would attempt to configure the system firewall, causing errors on some systems where iptables is not in the user's PATH
|
||||
- Fixed a bug where rootless Podman was unable to successfully write the container ID to a file when `--cid-file` was specified to `podman run`
|
||||
- Fixed a bug where `podman unmount` would refuse to unmount a container if it was running (the unmount will now be deferred until the container stops)
|
||||
- Fixed a bug where rootless `podman attach` would fail to attach due to a too-long path name
|
||||
- Fixed a bug where `podman info` was not properly reporting the Git commit Podman was built from
|
||||
- Fixed a bug where `podman run --interactive` was not holding STDIN open when `-a` flag was specified
|
||||
- Fixed a bug where Podman with the `cgroupfs` CGroup driver was sometimes not successfully removing pod CGroups
|
||||
- Fixed a bug where rootless Podman was unable to run systemd containers (note that this also requires an update to systemd)
|
||||
- Fixed a bug where `podman run` with the `--user` flag would fail if the container image did not contain `/etc/passwd` or `/etc/group`
|
||||
|
||||
### Misc
|
||||
- `podman rm`, `podman restart`, `podman kill`, `podman pause`, and `podman unpause` now operate in parallel, greatly improving speed when multiple containers are specified
|
||||
- `podman create`, `podman run`, and `podman ps` have a number of improvements which should greatly increase their speed
|
||||
- Greatly improved performance and reduced memory utilization of container status checks, which should improve the speed of most Podman commands
|
||||
- Improve ability of `podman runlabel` to run commands that are not Podman
|
||||
- Podman containers with an IP address now add their hostnames to `/etc/hosts`
|
||||
- Changed default location of temporary libpod files in rootless Podman
|
||||
- Updated the default Podman seccomp profile
|
||||
|
||||
### Compatability
|
||||
Several paths related to rootless Podman had their default values changed in this release.
|
||||
If paths were not hardcoded in libpod.conf, your system may lose track of running containers and believe they are newly-created.
|
||||
|
||||
## 0.10.1.3
|
||||
### Bugfixes
|
||||
- Fixed a bug where `podman build` would not work while any containers were running
|
||||
|
130
changelog.txt
130
changelog.txt
@ -1,3 +1,133 @@
|
||||
- Changelog for v0.11.1.1 (2018-11-15)
|
||||
* Vendor in containers/storage
|
||||
* Add release notes for 0.11.1.1
|
||||
* Increase pidWaitTimeout to 60s
|
||||
* Cirrus: Add master branch testing status badge
|
||||
* rootless: call IsRootless just once
|
||||
* Bump golang to v1.10 in install.md
|
||||
* Standardized container image for gofmt and lint
|
||||
* Make list of approvers same as reviewers
|
||||
* vendor: update ostree-go
|
||||
* vendor.conf: fix typo
|
||||
* Cleanup podman spec to not show git checkout is dirty
|
||||
* Add space between num & unit in images output
|
||||
* Update troubleshooting guide to deal with rootless path
|
||||
* troubleshooting.md: add a recipe for rootless ping
|
||||
* remove $-prefix from (most) shell examples
|
||||
* docs: Fix duplicated entry for pod-container-unmount
|
||||
* Better document rootless containers
|
||||
* info: add rootless field
|
||||
* Accurately update state if prepare() partially fails
|
||||
* Do not hide errors when creating container with UserNSRoot
|
||||
* rm -f now removes a paused container
|
||||
* correct assignment of networkStatus
|
||||
* podman_tutorial: cni build path has changed
|
||||
* Bump gitvalidation epoch
|
||||
* Bump to v0.11.2-dev
|
||||
* Cirrus: Ignore any error from the IRC messenger
|
||||
* rootless: default to fuse-overlayfs when available
|
||||
|
||||
- Changelog for v0.11.1 (2018-11-08)
|
||||
* Update release notes for 0.11.1
|
||||
* update seccomp.json
|
||||
* Touch up --log* options and daemons in man pages
|
||||
* Fix run --hostname test that started failing post-merge
|
||||
* move defer'd function declaration ahead of prepare error return
|
||||
* Don't fail if /etc/passwd or /etc/group does not exists
|
||||
* Print error status code if we fail to parse it
|
||||
* Properly set Running state when starting containers
|
||||
* Fix misspelling
|
||||
* Retrieve container PID from conmon
|
||||
* If a container ceases to exist in runc, set exit status
|
||||
* EXPERIMENTAL: Do not call out to runc for sync
|
||||
* Actually save changes from post-stop sync
|
||||
* rootless: mount /sys/fs/cgroup/systemd from the host
|
||||
* rootless: don't bind mount /sys/fs/cgroup/systemd in systemd mode
|
||||
* Add hostname to /etc/hosts
|
||||
* Temporarily fix the Python tests to fix some PRs
|
||||
* Remove conmon cgroup before pod cgroup for cgroupfs
|
||||
* Fix cleanup for "Pause a bunch of running containers"
|
||||
* --interactive shall keep STDIN attached even when not explicitly called out
|
||||
* Do never override podman with docker
|
||||
* Make kill, pause, and unpause parallel.
|
||||
* Fix long image name handling
|
||||
* Make restart parallel and add --all
|
||||
* Add ChangeAction to parse sub-options from --change
|
||||
* replace quay.io/baude to quay.io/libpod
|
||||
* Change humanize to use MB vs MiB.
|
||||
* allow ppc64le to pass libpod integration tests
|
||||
* Cirrus-CI: Add option to run system-tests
|
||||
* Cirrus: Skip rebuilding images unless instructed
|
||||
* Cirrus: Disable image build job abort on push
|
||||
* Cirrus: Add a readme
|
||||
* Ubuntu VM image build: try update twice
|
||||
* Cirrus: Enable updating F28 image
|
||||
* rootless: do not add an additional /run to runroot
|
||||
* rootless: avoid hang on failed slirp4netns
|
||||
* Fix setting of version information
|
||||
* runtime: do not allow runroot longer than 50 characters
|
||||
* attach: fix attach when cuid is too long
|
||||
* truncate command output in ps by default
|
||||
* Update the runc commit used for testing
|
||||
* make various changes to ps output
|
||||
* Sync default config with libpod.conf
|
||||
* Use two spaces to pad PS fields
|
||||
* unmount: fix error logic
|
||||
* get user and group information using securejoin and runc's user library
|
||||
* CONTRIBUTING.md: add section about describing changes
|
||||
* Change to exported name in ParseDevice
|
||||
* Vendor in latest containers/storage
|
||||
* fix bug in rm -fa parallel deletes
|
||||
* Ensure test container in running state
|
||||
* Add tests for selinux labels
|
||||
* Add --max-workers and heuristics for parallel operations
|
||||
* Increase security and performance when looking up groups
|
||||
* run prepare in parallel
|
||||
* downgrade runc due a rootless bug
|
||||
* runlabel: run any command
|
||||
* Eat our own dogfood
|
||||
* vendor: update containers/storage
|
||||
* Add support for /usr/local installation
|
||||
* create: fix writing cidfile when using rootless
|
||||
* Explain the device format in man pages
|
||||
* read conmon output and convert to json in two steps
|
||||
* Cirrus: Use images w/ buildah fix
|
||||
* Add --all and --latest to checkpoint/restore
|
||||
* Use the newly added getAllOrLatestContainers() function
|
||||
* Use the new checkAllAndLatest() function
|
||||
* Also factor out getAllOrLatestContainers() function
|
||||
* Add checkAllAndLatest() function
|
||||
* Downgrade code to support python3.4
|
||||
* Allow containers/storage to handle on SELinux labeling
|
||||
* Use more reliable check for rootless for firewall init
|
||||
* Vendor in latest containers/storage opencontainers/selinux
|
||||
* Make podman ps fast
|
||||
* Support auth file environment variable in podman build
|
||||
* fix environment variable parsing
|
||||
* tests: use existing CRIU version check
|
||||
* Use the CRIU version check in checkpoint/restore
|
||||
* Add helper function to read out CRIU version
|
||||
* vendor in go-criu and dependencies
|
||||
* oci: cleanup process status
|
||||
* Handle http/https in registry given to login/out
|
||||
* re-enable f29 testing
|
||||
* correct stats err with non-running containers
|
||||
* Use restoreArtifacts to save time in integration tests
|
||||
* Make rm faster
|
||||
* Fix man page to show info on storage
|
||||
* Move rootless directory handling to the libpod/pkg/util directory
|
||||
* Fix podman port -l
|
||||
* Fix trivial missing markup in manpage
|
||||
* Cirrus: Install CRIU in test images
|
||||
* Cirrus: Use different CNI_COMMIT for Fedora
|
||||
* Fix Cirrus/Packer VM image building
|
||||
* Revert "Cirrus: Enable debugging delay on non-zero exit"
|
||||
* Cirrus: IRC message when cirrus testing successful
|
||||
* cirrus: Add simple IRC messenger
|
||||
* fix NOTIFY_SOCKET in e2e testfix NOTIFY_SOCKET in e2e tests
|
||||
* Bump gitvalidation epoch
|
||||
* Bump to v0.10.2-dev
|
||||
|
||||
- Changelog for v0.10.1.3 (2018-10-17)
|
||||
* Update release notes for 0.10.1.3
|
||||
* Vendor in new new buildah/ci
|
||||
|
@ -1,6 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/buildah/imagebuildah"
|
||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||
@ -10,14 +15,14 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
layerFlags = []cli.Flag{
|
||||
cli.BoolTFlag{
|
||||
Name: "force-rm",
|
||||
Usage: "Always remove intermediate containers after a build, even if the build is unsuccessful. (default true)",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "layers",
|
||||
Usage: "cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override. ",
|
||||
@ -230,7 +235,7 @@ func buildCmd(c *cli.Context) error {
|
||||
Layers: layers,
|
||||
NoCache: c.Bool("no-cache"),
|
||||
RemoveIntermediateCtrs: c.BoolT("rm"),
|
||||
ForceRmIntermediateCtrs: c.Bool("force-rm"),
|
||||
ForceRmIntermediateCtrs: c.BoolT("force-rm"),
|
||||
}
|
||||
|
||||
if c.Bool("quiet") {
|
||||
|
@ -23,6 +23,14 @@ var (
|
||||
Name: "keep, k",
|
||||
Usage: "keep all temporary checkpoint files",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "leave-running, R",
|
||||
Usage: "leave the container running after writing checkpoint to disk",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tcp-established",
|
||||
Usage: "checkpoint a container with established TCP connections",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "all, a",
|
||||
Usage: "checkpoint all running containers",
|
||||
@ -50,7 +58,11 @@ func checkpointCmd(c *cli.Context) error {
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
keep := c.Bool("keep")
|
||||
options := libpod.ContainerCheckpointOptions{
|
||||
Keep: c.Bool("keep"),
|
||||
KeepRunning: c.Bool("leave-running"),
|
||||
TCPEstablished: c.Bool("tcp-established"),
|
||||
}
|
||||
|
||||
if err := checkAllAndLatest(c); err != nil {
|
||||
return err
|
||||
@ -59,7 +71,7 @@ func checkpointCmd(c *cli.Context) error {
|
||||
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
|
||||
for _, ctr := range containers {
|
||||
if err = ctr.Checkpoint(context.TODO(), keep); err != nil {
|
||||
if err = ctr.Checkpoint(context.TODO(), options); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func commitCmd(c *cli.Context) error {
|
||||
for _, change := range c.StringSlice("change") {
|
||||
splitChange := strings.Split(strings.ToUpper(change), "=")
|
||||
if !util.StringInSlice(splitChange[0], libpod.ChangeCmds) {
|
||||
return errors.Errorf("invalid syntax for --change ", change)
|
||||
return errors.Errorf("invalid syntax for --change: %s", change)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/storage"
|
||||
"github.com/fatih/camelcase"
|
||||
"github.com/pkg/errors"
|
||||
@ -161,6 +162,13 @@ func getContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
func getDefaultNetwork() string {
|
||||
if rootless.IsRootless() {
|
||||
return "slirp4netns"
|
||||
}
|
||||
return "bridge"
|
||||
}
|
||||
|
||||
// Common flags shared between commands
|
||||
var createFlags = []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
@ -372,7 +380,7 @@ var createFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "net, network",
|
||||
Usage: "Connect a container to a network",
|
||||
Value: "bridge",
|
||||
Value: getDefaultNetwork(),
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "oom-kill-disable",
|
||||
|
@ -9,6 +9,7 @@ var (
|
||||
attachCommand,
|
||||
checkpointCommand,
|
||||
cleanupCommand,
|
||||
containerExistsCommand,
|
||||
commitCommand,
|
||||
createCommand,
|
||||
diffCommand,
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
ann "github.com/containers/libpod/pkg/annotations"
|
||||
@ -66,7 +67,7 @@ func createCmd(c *cli.Context) error {
|
||||
rootless.SetSkipStorageSetup(true)
|
||||
}
|
||||
|
||||
runtime, err := libpodruntime.GetContainerRuntime(c)
|
||||
runtime, err := libpodruntime.GetRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating libpod runtime")
|
||||
}
|
||||
@ -375,8 +376,8 @@ func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string {
|
||||
return entrypoint
|
||||
}
|
||||
|
||||
func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string) (map[string]string, error) {
|
||||
pod, err := runtime.LookupPod(c.String("pod"))
|
||||
func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) {
|
||||
pod, err := runtime.LookupPod(podName)
|
||||
if err != nil {
|
||||
return namespaces, err
|
||||
}
|
||||
@ -409,6 +410,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
inputCommand, command []string
|
||||
memoryLimit, memoryReservation, memorySwap, memoryKernel int64
|
||||
blkioWeight uint16
|
||||
namespaces map[string]string
|
||||
)
|
||||
idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
|
||||
if err != nil {
|
||||
@ -492,12 +494,21 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together")
|
||||
}
|
||||
|
||||
// EXPOSED PORTS
|
||||
var portBindings map[nat.Port][]nat.PortBinding
|
||||
if data != nil {
|
||||
portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel Namespaces
|
||||
// TODO Fix handling of namespace from pod
|
||||
// Instead of integrating here, should be done in libpod
|
||||
// However, that also involves setting up security opts
|
||||
// when the pod's namespace is integrated
|
||||
namespaces := map[string]string{
|
||||
namespaces = map[string]string{
|
||||
"pid": c.String("pid"),
|
||||
"net": c.String("net"),
|
||||
"ipc": c.String("ipc"),
|
||||
@ -505,8 +516,41 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
"uts": c.String("uts"),
|
||||
}
|
||||
|
||||
originalPodName := c.String("pod")
|
||||
podName := strings.Replace(originalPodName, "new:", "", 1)
|
||||
// after we strip out :new, make sure there is something left for a pod name
|
||||
if len(podName) < 1 && c.IsSet("pod") {
|
||||
return nil, errors.Errorf("new pod name must be at least one character")
|
||||
}
|
||||
if c.IsSet("pod") {
|
||||
namespaces, err = configurePod(c, runtime, namespaces)
|
||||
if strings.HasPrefix(originalPodName, "new:") {
|
||||
// pod does not exist; lets make it
|
||||
var podOptions []libpod.PodCreateOption
|
||||
podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups())
|
||||
if len(portBindings) > 0 {
|
||||
ociPortBindings, err := cc.NatToOCIPortBindings(portBindings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings))
|
||||
}
|
||||
|
||||
podNsOptions, err := shared.GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ","))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podOptions = append(podOptions, podNsOptions...)
|
||||
// make pod
|
||||
pod, err := runtime.NewPod(ctx, podOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Debugf("pod %s created by new container request", pod.ID())
|
||||
|
||||
// The container now cannot have port bindings; so we reset the map
|
||||
portBindings = make(map[nat.Port][]nat.PortBinding)
|
||||
}
|
||||
namespaces, err = configurePod(c, runtime, namespaces, podName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -535,7 +579,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
// Make sure if network is set to container namespace, port binding is not also being asked for
|
||||
netMode := ns.NetworkMode(namespaces["net"])
|
||||
if netMode.IsContainer() {
|
||||
if len(c.StringSlice("publish")) > 0 || c.Bool("publish-all") {
|
||||
if len(portBindings) > 0 {
|
||||
return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
|
||||
}
|
||||
}
|
||||
@ -644,15 +688,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image")
|
||||
}
|
||||
|
||||
// EXPOSED PORTS
|
||||
var portBindings map[nat.Port][]nat.PortBinding
|
||||
if data != nil {
|
||||
portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// SHM Size
|
||||
shmSize, err := units.FromHumanSize(c.String("shm-size"))
|
||||
if err != nil {
|
||||
@ -670,6 +705,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
if util.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
|
||||
return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
|
||||
}
|
||||
if !netMode.IsPrivate() {
|
||||
if c.IsSet("dns-search") || c.IsSet("dns") || c.IsSet("dns-opt") {
|
||||
return nil, errors.Errorf("specifying DNS flags when network mode is shared with the host or another container is not allowed")
|
||||
}
|
||||
}
|
||||
|
||||
// Validate domains are good
|
||||
for _, dom := range c.StringSlice("dns-search") {
|
||||
@ -741,7 +781,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
||||
NetMode: netMode,
|
||||
UtsMode: utsMode,
|
||||
PidMode: pidMode,
|
||||
Pod: c.String("pod"),
|
||||
Pod: podName,
|
||||
Privileged: c.Bool("privileged"),
|
||||
Publish: c.StringSlice("publish"),
|
||||
PublishAll: c.Bool("publish-all"),
|
||||
|
120
cmd/podman/exists.go
Normal file
120
cmd/podman/exists.go
Normal file
@ -0,0 +1,120 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
imageExistsDescription = `
|
||||
podman image exists
|
||||
|
||||
Check if an image exists in local storage
|
||||
`
|
||||
|
||||
imageExistsCommand = cli.Command{
|
||||
Name: "exists",
|
||||
Usage: "Check if an image exists in local storage",
|
||||
Description: imageExistsDescription,
|
||||
Action: imageExistsCmd,
|
||||
ArgsUsage: "IMAGE-NAME",
|
||||
OnUsageError: usageErrorHandler,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
containerExistsDescription = `
|
||||
podman container exists
|
||||
|
||||
Check if a container exists in local storage
|
||||
`
|
||||
|
||||
containerExistsCommand = cli.Command{
|
||||
Name: "exists",
|
||||
Usage: "Check if a container exists in local storage",
|
||||
Description: containerExistsDescription,
|
||||
Action: containerExistsCmd,
|
||||
ArgsUsage: "CONTAINER-NAME",
|
||||
OnUsageError: usageErrorHandler,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
podExistsDescription = `
|
||||
podman pod exists
|
||||
|
||||
Check if a pod exists in local storage
|
||||
`
|
||||
|
||||
podExistsCommand = cli.Command{
|
||||
Name: "exists",
|
||||
Usage: "Check if a pod exists in local storage",
|
||||
Description: podExistsDescription,
|
||||
Action: podExistsCmd,
|
||||
ArgsUsage: "POD-NAME",
|
||||
OnUsageError: usageErrorHandler,
|
||||
}
|
||||
)
|
||||
|
||||
func imageExistsCmd(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) > 1 || len(args) < 1 {
|
||||
return errors.New("you may only check for the existence of one image at a time")
|
||||
}
|
||||
runtime, err := libpodruntime.GetRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
if _, err := runtime.ImageRuntime().NewFromLocal(args[0]); err != nil {
|
||||
if errors.Cause(err) == image.ErrNoSuchImage {
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func containerExistsCmd(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) > 1 || len(args) < 1 {
|
||||
return errors.New("you may only check for the existence of one container at a time")
|
||||
}
|
||||
runtime, err := libpodruntime.GetRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
if _, err := runtime.LookupContainer(args[0]); err != nil {
|
||||
if errors.Cause(err) == libpod.ErrNoSuchCtr {
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func podExistsCmd(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) > 1 || len(args) < 1 {
|
||||
return errors.New("you may only check for the existence of one pod at a time")
|
||||
}
|
||||
runtime, err := libpodruntime.GetRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
if _, err := runtime.LookupPod(args[0]); err != nil {
|
||||
if errors.Cause(err) == libpod.ErrNoSuchPod {
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -9,6 +9,7 @@ var (
|
||||
buildCommand,
|
||||
historyCommand,
|
||||
importCommand,
|
||||
imageExistsCommand,
|
||||
inspectCommand,
|
||||
loadCommand,
|
||||
lsImagesCommand,
|
||||
|
@ -6,8 +6,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"unicode"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/formats"
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
@ -16,6 +15,7 @@ import (
|
||||
"github.com/docker/go-units"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@ -289,6 +289,8 @@ func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, image
|
||||
sizeStr = err.Error()
|
||||
} else {
|
||||
sizeStr = units.HumanSizeWithPrecision(float64(*size), 3)
|
||||
lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber)
|
||||
sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:]
|
||||
}
|
||||
params := imagesTemplateParams{
|
||||
Repository: repo,
|
||||
@ -374,13 +376,13 @@ func CreateFilterFuncs(ctx context.Context, r *libpod.Runtime, c *cli.Context, i
|
||||
case "before":
|
||||
before, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to find image % in local stores", splitFilter[1])
|
||||
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
|
||||
}
|
||||
filterFuncs = append(filterFuncs, image.CreatedBeforeFilter(before.Created()))
|
||||
case "after":
|
||||
after, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to find image % in local stores", splitFilter[1])
|
||||
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
|
||||
}
|
||||
filterFuncs = append(filterFuncs, image.CreatedAfterFilter(after.Created()))
|
||||
case "dangling":
|
||||
|
@ -139,7 +139,7 @@ func downloadFromURL(source string) (string, error) {
|
||||
|
||||
_, err = io.Copy(outFile, response.Body)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error saving %q to %q", source, outFile)
|
||||
return "", errors.Wrapf(err, "error saving %s to %s", source, outFile.Name())
|
||||
}
|
||||
|
||||
return outFile.Name(), nil
|
||||
|
@ -81,6 +81,7 @@ func debugInfo(c *cli.Context) map[string]interface{} {
|
||||
info["compiler"] = runtime.Compiler
|
||||
info["go version"] = runtime.Version()
|
||||
info["podman version"] = c.App.Version
|
||||
info["git commit"] = libpod.GitCommit
|
||||
version, _ := libpod.GetVersion()
|
||||
info["git commit"] = version.GitCommit
|
||||
return info
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
|
||||
}
|
||||
libpodInspectData, err := ctr.Inspect(c.Bool("size"))
|
||||
if err != nil {
|
||||
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
|
||||
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
|
||||
break
|
||||
}
|
||||
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
|
||||
@ -154,12 +154,12 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
|
||||
} else {
|
||||
libpodInspectData, err := ctr.Inspect(c.Bool("size"))
|
||||
if err != nil {
|
||||
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
|
||||
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
|
||||
break
|
||||
}
|
||||
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
|
||||
if err != nil {
|
||||
inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID)
|
||||
inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"fmt"
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@ -41,6 +42,11 @@ var (
|
||||
|
||||
// killCmd kills one or more containers with a signal
|
||||
func killCmd(c *cli.Context) error {
|
||||
var (
|
||||
killFuncs []shared.ParallelWorkerInput
|
||||
killSignal uint = uint(syscall.SIGTERM)
|
||||
)
|
||||
|
||||
if err := checkAllAndLatest(c); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -56,7 +62,6 @@ func killCmd(c *cli.Context) error {
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
var killSignal uint = uint(syscall.SIGTERM)
|
||||
if c.String("signal") != "" {
|
||||
// Check if the signalString provided by the user is valid
|
||||
// Invalid signals will return err
|
||||
@ -67,17 +72,32 @@ func killCmd(c *cli.Context) error {
|
||||
killSignal = uint(sysSignal)
|
||||
}
|
||||
|
||||
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
if err != nil {
|
||||
if len(containers) == 0 {
|
||||
return err
|
||||
}
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
for _, ctr := range containers {
|
||||
if err := ctr.Kill(killSignal); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "unable to find container %v", ctr.ID())
|
||||
} else {
|
||||
fmt.Println(ctr.ID())
|
||||
con := ctr
|
||||
f := func() error {
|
||||
return con.Kill(killSignal)
|
||||
}
|
||||
|
||||
killFuncs = append(killFuncs, shared.ParallelWorkerInput{
|
||||
ContainerID: con.ID(),
|
||||
ParallelFunc: f,
|
||||
})
|
||||
}
|
||||
return lastError
|
||||
|
||||
maxWorkers := shared.Parallelize("kill")
|
||||
if c.GlobalIsSet("max-workers") {
|
||||
maxWorkers = c.GlobalInt("max-workers")
|
||||
}
|
||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||
|
||||
killErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, killFuncs)
|
||||
return printParallelOutput(killErrors, errCount)
|
||||
}
|
||||
|
23
cmd/podman/kube.go
Normal file
23
cmd/podman/kube.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
kubeSubCommands = []cli.Command{
|
||||
containerKubeCommand,
|
||||
}
|
||||
|
||||
kubeDescription = "Work with Kubernetes objects"
|
||||
kubeCommand = cli.Command{
|
||||
Name: "kube",
|
||||
Usage: "Import and export Kubernetes objections from and to Podman",
|
||||
Description: containerDescription,
|
||||
ArgsUsage: "",
|
||||
Subcommands: kubeSubCommands,
|
||||
UseShortOptionHandling: true,
|
||||
OnUsageError: usageErrorHandler,
|
||||
Hidden: true,
|
||||
}
|
||||
)
|
93
cmd/podman/kube_generate.go
Normal file
93
cmd/podman/kube_generate.go
Normal file
@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
containerKubeFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "service, s",
|
||||
Usage: "only generate YAML for kubernetes service object",
|
||||
},
|
||||
LatestFlag,
|
||||
}
|
||||
containerKubeDescription = "Generate Kubernetes Pod YAML"
|
||||
containerKubeCommand = cli.Command{
|
||||
Name: "generate",
|
||||
Usage: "Generate Kubernetes pod YAML for a container",
|
||||
Description: containerKubeDescription,
|
||||
Flags: sortFlags(containerKubeFlags),
|
||||
Action: generateKubeYAMLCmd,
|
||||
ArgsUsage: "CONTAINER-NAME",
|
||||
UseShortOptionHandling: true,
|
||||
OnUsageError: usageErrorHandler,
|
||||
}
|
||||
)
|
||||
|
||||
// generateKubeYAMLCmdgenerates or replays kube
|
||||
func generateKubeYAMLCmd(c *cli.Context) error {
|
||||
var (
|
||||
container *libpod.Container
|
||||
err error
|
||||
output []byte
|
||||
)
|
||||
|
||||
if rootless.IsRootless() {
|
||||
return errors.Wrapf(libpod.ErrNotImplemented, "rootless users")
|
||||
}
|
||||
args := c.Args()
|
||||
if len(args) > 1 || (len(args) < 1 && !c.Bool("latest")) {
|
||||
return errors.Errorf("you must provide one container ID or name or --latest")
|
||||
}
|
||||
if c.Bool("service") {
|
||||
return errors.Wrapf(libpod.ErrNotImplemented, "service generation")
|
||||
}
|
||||
|
||||
runtime, err := libpodruntime.GetRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
// Get the container in question
|
||||
if c.Bool("latest") {
|
||||
container, err = runtime.GetLatestContainer()
|
||||
} else {
|
||||
container, err = runtime.LookupContainer(args[0])
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(container.Dependencies()) > 0 {
|
||||
return errors.Wrapf(libpod.ErrNotImplemented, "containers with dependencies")
|
||||
}
|
||||
|
||||
podYAML, err := container.InspectForKube()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
developmentComment := []byte("# Generation of Kubenetes YAML is still under development!\n")
|
||||
logrus.Warn("This function is still under heavy development.")
|
||||
// Marshall the results
|
||||
b, err := yaml.Marshal(podYAML)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output = append(output, developmentComment...)
|
||||
output = append(output, b...)
|
||||
// Output the v1.Pod with the v1.Container
|
||||
fmt.Println(string(output))
|
||||
|
||||
return nil
|
||||
}
|
@ -5,43 +5,33 @@ import (
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/containers/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// GetRuntime generates a new libpod runtime configured by command line options
|
||||
func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
|
||||
storageOpts, err := util.GetDefaultStoreOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetRuntimeWithStorageOpts(c, &storageOpts)
|
||||
}
|
||||
|
||||
// GetContainerRuntime generates a new libpod runtime configured by command line options for containers
|
||||
func GetContainerRuntime(c *cli.Context) (*libpod.Runtime, error) {
|
||||
mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageOpts, err := util.GetDefaultStoreOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageOpts.UIDMap = mappings.UIDMap
|
||||
storageOpts.GIDMap = mappings.GIDMap
|
||||
return GetRuntimeWithStorageOpts(c, &storageOpts)
|
||||
}
|
||||
|
||||
// GetRuntime generates a new libpod runtime configured by command line options
|
||||
func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions) (*libpod.Runtime, error) {
|
||||
storageOpts := new(storage.StoreOptions)
|
||||
options := []libpod.RuntimeOption{}
|
||||
|
||||
if c.IsSet("uidmap") || c.IsSet("gidmap") || c.IsSet("subuidmap") || c.IsSet("subgidmap") {
|
||||
mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageOpts.UIDMap = mappings.UIDMap
|
||||
storageOpts.GIDMap = mappings.GIDMap
|
||||
}
|
||||
|
||||
if c.GlobalIsSet("root") {
|
||||
storageOpts.GraphRoot = c.GlobalString("root")
|
||||
}
|
||||
if c.GlobalIsSet("runroot") {
|
||||
storageOpts.RunRoot = c.GlobalString("runroot")
|
||||
}
|
||||
if len(storageOpts.RunRoot) > 50 {
|
||||
return nil, errors.New("the specified runroot is longer than 50 characters")
|
||||
}
|
||||
if c.GlobalIsSet("storage-driver") {
|
||||
storageOpts.GraphDriverName = c.GlobalString("storage-driver")
|
||||
}
|
||||
@ -86,8 +76,8 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions
|
||||
if c.GlobalIsSet("default-mounts-file") {
|
||||
options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file")))
|
||||
}
|
||||
if c.GlobalIsSet("hooks-dir-path") {
|
||||
options = append(options, libpod.WithHooksDir(c.GlobalString("hooks-dir-path")))
|
||||
if c.GlobalIsSet("hooks-dir") {
|
||||
options = append(options, libpod.WithHooksDir(c.GlobalStringSlice("hooks-dir")...))
|
||||
}
|
||||
|
||||
// TODO flag to set CNI plugins dir?
|
||||
|
@ -40,14 +40,15 @@ var (
|
||||
logsDescription = "The podman logs command batch-retrieves whatever logs are present for a container at the time of execution. This does not guarantee execution" +
|
||||
"order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs"
|
||||
logsCommand = cli.Command{
|
||||
Name: "logs",
|
||||
Usage: "Fetch the logs of a container",
|
||||
Description: logsDescription,
|
||||
Flags: sortFlags(logsFlags),
|
||||
Action: logsCmd,
|
||||
ArgsUsage: "CONTAINER",
|
||||
SkipArgReorder: true,
|
||||
OnUsageError: usageErrorHandler,
|
||||
Name: "logs",
|
||||
Usage: "Fetch the logs of a container",
|
||||
Description: logsDescription,
|
||||
Flags: sortFlags(logsFlags),
|
||||
Action: logsCmd,
|
||||
ArgsUsage: "CONTAINER",
|
||||
SkipArgReorder: true,
|
||||
OnUsageError: usageErrorHandler,
|
||||
UseShortOptionHandling: true,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/hooks"
|
||||
_ "github.com/containers/libpod/pkg/hooks/0.1.0"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/libpod/version"
|
||||
@ -77,6 +76,7 @@ func main() {
|
||||
infoCommand,
|
||||
inspectCommand,
|
||||
killCommand,
|
||||
kubeCommand,
|
||||
loadCommand,
|
||||
loginCommand,
|
||||
logoutCommand,
|
||||
@ -205,11 +205,9 @@ func main() {
|
||||
Usage: "path to default mounts file",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hooks-dir-path",
|
||||
Usage: "set the OCI hooks directory path",
|
||||
Value: hooks.DefaultDir,
|
||||
Hidden: true,
|
||||
cli.StringSliceFlag{
|
||||
Name: "hooks-dir",
|
||||
Usage: "set the OCI hooks directory path (may be set multiple times)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "max-workers",
|
||||
|
@ -1,15 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
pauseFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "all, a",
|
||||
Usage: "pause all running containers",
|
||||
},
|
||||
}
|
||||
pauseDescription = `
|
||||
podman pause
|
||||
|
||||
@ -19,6 +27,7 @@ var (
|
||||
Name: "pause",
|
||||
Usage: "Pauses all the processes in one or more containers",
|
||||
Description: pauseDescription,
|
||||
Flags: pauseFlags,
|
||||
Action: pauseCmd,
|
||||
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
|
||||
OnUsageError: usageErrorHandler,
|
||||
@ -26,6 +35,10 @@ var (
|
||||
)
|
||||
|
||||
func pauseCmd(c *cli.Context) error {
|
||||
var (
|
||||
pauseContainers []*libpod.Container
|
||||
pauseFuncs []shared.ParallelWorkerInput
|
||||
)
|
||||
if os.Geteuid() != 0 {
|
||||
return errors.New("pause is not supported for rootless containers")
|
||||
}
|
||||
@ -37,28 +50,44 @@ func pauseCmd(c *cli.Context) error {
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
args := c.Args()
|
||||
if len(args) < 1 {
|
||||
if len(args) < 1 && !c.Bool("all") {
|
||||
return errors.Errorf("you must provide at least one container name or id")
|
||||
}
|
||||
|
||||
var lastError error
|
||||
for _, arg := range args {
|
||||
ctr, err := runtime.LookupContainer(arg)
|
||||
if c.Bool("all") {
|
||||
containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
if err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "error looking up container %q", arg)
|
||||
continue
|
||||
return err
|
||||
}
|
||||
if err = ctr.Pause(); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
pauseContainers = append(pauseContainers, containers...)
|
||||
} else {
|
||||
for _, arg := range args {
|
||||
ctr, err := runtime.LookupContainer(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lastError = errors.Wrapf(err, "failed to pause container %v", ctr.ID())
|
||||
} else {
|
||||
fmt.Println(ctr.ID())
|
||||
pauseContainers = append(pauseContainers, ctr)
|
||||
}
|
||||
}
|
||||
return lastError
|
||||
|
||||
// Now assemble the slice of pauseFuncs
|
||||
for _, ctr := range pauseContainers {
|
||||
con := ctr
|
||||
|
||||
f := func() error {
|
||||
return con.Pause()
|
||||
}
|
||||
pauseFuncs = append(pauseFuncs, shared.ParallelWorkerInput{
|
||||
ContainerID: con.ID(),
|
||||
ParallelFunc: f,
|
||||
})
|
||||
}
|
||||
|
||||
maxWorkers := shared.Parallelize("pause")
|
||||
if c.GlobalIsSet("max-workers") {
|
||||
maxWorkers = c.GlobalInt("max-workers")
|
||||
}
|
||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||
|
||||
pauseErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, pauseFuncs)
|
||||
return printParallelOutput(pauseErrors, errCount)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ Pods are a group of one or more containers sharing the same network, pid and ipc
|
||||
`
|
||||
podSubCommands = []cli.Command{
|
||||
podCreateCommand,
|
||||
podExistsCommand,
|
||||
podInspectCommand,
|
||||
podKillCommand,
|
||||
podPauseCommand,
|
||||
|
@ -3,11 +3,15 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
@ -58,6 +62,10 @@ var podCreateFlags = []cli.Flag{
|
||||
Name: "pod-id-file",
|
||||
Usage: "Write the pod ID to the file",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "publish, p",
|
||||
Usage: "Publish a container's port, or a range of ports, to the host (default [])",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "share",
|
||||
Usage: "A comma delimited list of kernel namespaces the pod will share",
|
||||
@ -102,6 +110,16 @@ func podCreateCmd(c *cli.Context) error {
|
||||
defer podIdFile.Close()
|
||||
defer podIdFile.Sync()
|
||||
}
|
||||
|
||||
if len(c.StringSlice("publish")) > 0 {
|
||||
if !c.BoolT("infra") {
|
||||
return errors.Errorf("you must have an infra container to publish port bindings to the host")
|
||||
}
|
||||
if rootless.IsRootless() {
|
||||
return errors.Errorf("rootless networking does not allow port binding to the host")
|
||||
}
|
||||
}
|
||||
|
||||
if !c.BoolT("infra") && c.IsSet("share") && c.String("share") != "none" && c.String("share") != "" {
|
||||
return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
|
||||
}
|
||||
@ -131,6 +149,14 @@ func podCreateCmd(c *cli.Context) error {
|
||||
options = append(options, nsOptions...)
|
||||
}
|
||||
|
||||
if len(c.StringSlice("publish")) > 0 {
|
||||
portBindings, err := CreatePortBindings(c.StringSlice("publish"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options = append(options, libpod.WithInfraContainerPorts(portBindings))
|
||||
|
||||
}
|
||||
// always have containers use pod cgroups
|
||||
// User Opt out is not yet supported
|
||||
options = append(options, libpod.WithPodCgroups())
|
||||
@ -152,3 +178,36 @@ func podCreateCmd(c *cli.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
|
||||
func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
|
||||
var portBindings []ocicni.PortMapping
|
||||
// The conversion from []string to natBindings is temporary while mheon reworks the port
|
||||
// deduplication code. Eventually that step will not be required.
|
||||
_, natBindings, err := nat.ParsePortSpecs(ports)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for containerPb, hostPb := range natBindings {
|
||||
var pm ocicni.PortMapping
|
||||
pm.ContainerPort = int32(containerPb.Int())
|
||||
for _, i := range hostPb {
|
||||
var hostPort int
|
||||
var err error
|
||||
pm.HostIP = i.HostIP
|
||||
if i.HostPort == "" {
|
||||
hostPort = containerPb.Int()
|
||||
} else {
|
||||
hostPort, err = strconv.Atoi(i.HostPort)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to convert host port to integer")
|
||||
}
|
||||
}
|
||||
|
||||
pm.HostPort = int32(hostPort)
|
||||
pm.Protocol = containerPb.Proto()
|
||||
portBindings = append(portBindings, pm)
|
||||
}
|
||||
}
|
||||
return portBindings, nil
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ var (
|
||||
Usage: "Display the extended information",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "pod",
|
||||
Name: "pod, p",
|
||||
Usage: "Print the ID and name of the pod the containers are associated with",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
|
@ -1,18 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
restartFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "all, a",
|
||||
Usage: "restart all non-running containers",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "running",
|
||||
Usage: "restart only running containers when --all is used",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "timeout, time, t",
|
||||
Usage: "Seconds to wait for stop before killing the container",
|
||||
@ -35,11 +41,18 @@ var (
|
||||
)
|
||||
|
||||
func restartCmd(c *cli.Context) error {
|
||||
var (
|
||||
restartFuncs []shared.ParallelWorkerInput
|
||||
containers []*libpod.Container
|
||||
restartContainers []*libpod.Container
|
||||
)
|
||||
|
||||
args := c.Args()
|
||||
if len(args) < 1 && !c.Bool("latest") {
|
||||
runOnly := c.Bool("running")
|
||||
all := c.Bool("all")
|
||||
if len(args) < 1 && !c.Bool("latest") && !all {
|
||||
return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID")
|
||||
}
|
||||
|
||||
if err := validateFlags(c, restartFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -50,8 +63,6 @@ func restartCmd(c *cli.Context) error {
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
var lastError error
|
||||
|
||||
timeout := c.Uint("timeout")
|
||||
useTimeout := c.IsSet("timeout")
|
||||
|
||||
@ -59,39 +70,57 @@ func restartCmd(c *cli.Context) error {
|
||||
if c.Bool("latest") {
|
||||
lastCtr, err := runtime.GetLatestContainer()
|
||||
if err != nil {
|
||||
lastError = errors.Wrapf(err, "unable to get latest container")
|
||||
} else {
|
||||
ctrTimeout := lastCtr.StopTimeout()
|
||||
if useTimeout {
|
||||
ctrTimeout = timeout
|
||||
return errors.Wrapf(err, "unable to get latest container")
|
||||
}
|
||||
restartContainers = append(restartContainers, lastCtr)
|
||||
} else if runOnly {
|
||||
containers, err = getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
restartContainers = append(restartContainers, containers...)
|
||||
} else if all {
|
||||
containers, err = runtime.GetAllContainers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
restartContainers = append(restartContainers, containers...)
|
||||
} else {
|
||||
for _, id := range args {
|
||||
ctr, err := runtime.LookupContainer(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lastError = lastCtr.RestartWithTimeout(context.TODO(), ctrTimeout)
|
||||
restartContainers = append(restartContainers, ctr)
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range args {
|
||||
ctr, err := runtime.LookupContainer(id)
|
||||
if err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "unable to find container %s", id)
|
||||
continue
|
||||
}
|
||||
|
||||
// We now have a slice of all the containers to be restarted. Iterate them to
|
||||
// create restart Funcs with a timeout as needed
|
||||
for _, ctr := range restartContainers {
|
||||
con := ctr
|
||||
ctrTimeout := ctr.StopTimeout()
|
||||
if useTimeout {
|
||||
ctrTimeout = timeout
|
||||
}
|
||||
|
||||
if err := ctr.RestartWithTimeout(context.TODO(), ctrTimeout); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "error restarting container %s", ctr.ID())
|
||||
f := func() error {
|
||||
return con.RestartWithTimeout(getContext(), ctrTimeout)
|
||||
}
|
||||
|
||||
restartFuncs = append(restartFuncs, shared.ParallelWorkerInput{
|
||||
ContainerID: con.ID(),
|
||||
ParallelFunc: f,
|
||||
})
|
||||
}
|
||||
|
||||
return lastError
|
||||
maxWorkers := shared.Parallelize("restart")
|
||||
if c.GlobalIsSet("max-workers") {
|
||||
maxWorkers = c.GlobalInt("max-workers")
|
||||
}
|
||||
|
||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||
|
||||
restartErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, restartFuncs)
|
||||
return printParallelOutput(restartErrors, errCount)
|
||||
}
|
||||
|
@ -26,6 +26,10 @@ var (
|
||||
// restore --all would make more sense if there would be
|
||||
// dedicated state for container which are checkpointed.
|
||||
// TODO: add ContainerStateCheckpointed
|
||||
cli.BoolFlag{
|
||||
Name: "tcp-established",
|
||||
Usage: "checkpoint a container with established TCP connections",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "all, a",
|
||||
Usage: "restore all checkpointed containers",
|
||||
@ -53,16 +57,19 @@ func restoreCmd(c *cli.Context) error {
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
keep := c.Bool("keep")
|
||||
options := libpod.ContainerCheckpointOptions{
|
||||
Keep: c.Bool("keep"),
|
||||
TCPEstablished: c.Bool("tcp-established"),
|
||||
}
|
||||
|
||||
if err := checkAllAndLatest(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "checkpointed")
|
||||
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateExited, "checkpointed")
|
||||
|
||||
for _, ctr := range containers {
|
||||
if err = ctr.Restore(context.TODO(), keep); err != nil {
|
||||
if err = ctr.Restore(context.TODO(), options); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
@ -46,9 +45,7 @@ Running containers will not be removed without the -f option.
|
||||
// saveCmd saves the image to either docker-archive or oci
|
||||
func rmCmd(c *cli.Context) error {
|
||||
var (
|
||||
delContainers []*libpod.Container
|
||||
lastError error
|
||||
deleteFuncs []shared.ParallelWorkerInput
|
||||
deleteFuncs []shared.ParallelWorkerInput
|
||||
)
|
||||
|
||||
ctx := getContext()
|
||||
@ -65,7 +62,13 @@ func rmCmd(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
delContainers, lastError = getAllOrLatestContainers(c, runtime, -1, "all")
|
||||
delContainers, err := getAllOrLatestContainers(c, runtime, -1, "all")
|
||||
if err != nil {
|
||||
if len(delContainers) == 0 {
|
||||
return err
|
||||
}
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
for _, container := range delContainers {
|
||||
con := container
|
||||
@ -84,14 +87,7 @@ func rmCmd(c *cli.Context) error {
|
||||
}
|
||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||
|
||||
deleteErrors := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
|
||||
for cid, result := range deleteErrors {
|
||||
if result != nil {
|
||||
fmt.Println(result.Error())
|
||||
lastError = result
|
||||
continue
|
||||
}
|
||||
fmt.Println(cid)
|
||||
}
|
||||
return lastError
|
||||
// Run the parallel funcs
|
||||
deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
|
||||
return printParallelOutput(deleteErrors, errCount)
|
||||
}
|
||||
|
@ -91,8 +91,23 @@ func rmiCmd(c *cli.Context) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to query local images")
|
||||
}
|
||||
for _, i := range imagesToDelete {
|
||||
removeImage(i)
|
||||
lastNumberofImages := 0
|
||||
for len(imagesToDelete) > 0 {
|
||||
if lastNumberofImages == len(imagesToDelete) {
|
||||
return errors.New("unable to delete all images; re-run the rmi command again.")
|
||||
}
|
||||
for _, i := range imagesToDelete {
|
||||
isParent, err := i.IsParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isParent {
|
||||
continue
|
||||
}
|
||||
removeImage(i)
|
||||
}
|
||||
lastNumberofImages = len(imagesToDelete)
|
||||
imagesToDelete, err = runtime.ImageRuntime().GetImages()
|
||||
}
|
||||
} else {
|
||||
// Create image.image objects for deletion from user input.
|
||||
|
@ -44,7 +44,7 @@ func runCmd(c *cli.Context) error {
|
||||
rootless.SetSkipStorageSetup(true)
|
||||
}
|
||||
|
||||
runtime, err := libpodruntime.GetContainerRuntime(c)
|
||||
runtime, err := libpodruntime.GetRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating libpod runtime")
|
||||
}
|
||||
@ -96,8 +96,6 @@ func runCmd(c *cli.Context) error {
|
||||
inputStream = nil
|
||||
}
|
||||
|
||||
inputStream = nil
|
||||
|
||||
attachTo := c.StringSlice("attach")
|
||||
for _, stream := range attachTo {
|
||||
switch strings.ToLower(stream) {
|
||||
|
@ -6,11 +6,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/containers/libpod/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -94,7 +92,7 @@ func runlabelCmd(c *cli.Context) error {
|
||||
imageName string
|
||||
stdErr, stdOut io.Writer
|
||||
stdIn io.Reader
|
||||
newImage *image.Image
|
||||
extraArgs []string
|
||||
)
|
||||
|
||||
// Evil images could trick into recursively executing the runlabel
|
||||
@ -124,6 +122,9 @@ func runlabelCmd(c *cli.Context) error {
|
||||
return errors.Errorf("the display and quiet flags cannot be used together.")
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
extraArgs = args[2:]
|
||||
}
|
||||
pull := c.Bool("pull")
|
||||
label := args[0]
|
||||
|
||||
@ -151,75 +152,24 @@ func runlabelCmd(c *cli.Context) error {
|
||||
stdIn = nil
|
||||
}
|
||||
|
||||
if pull {
|
||||
var registryCreds *types.DockerAuthConfig
|
||||
if c.IsSet("creds") {
|
||||
creds, err := util.ParseRegistryCreds(c.String("creds"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registryCreds = creds
|
||||
}
|
||||
dockerRegistryOptions := image.DockerRegistryOptions{
|
||||
DockerRegistryCreds: registryCreds,
|
||||
DockerCertPath: c.String("cert-dir"),
|
||||
DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
|
||||
}
|
||||
authfile := getAuthFile(c.String("authfile"))
|
||||
|
||||
newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, c.String("signature-policy"), authfile, stdOut, &dockerRegistryOptions, image.SigningOptions{}, false, false)
|
||||
} else {
|
||||
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to find image")
|
||||
dockerRegistryOptions := image.DockerRegistryOptions{
|
||||
DockerCertPath: c.String("cert-dir"),
|
||||
DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
|
||||
}
|
||||
|
||||
if len(newImage.Names()) < 1 {
|
||||
imageName = newImage.ID()
|
||||
} else {
|
||||
imageName = newImage.Names()[0]
|
||||
}
|
||||
|
||||
runLabel, err := newImage.GetLabel(ctx, label)
|
||||
authfile := getAuthFile(c.String("authfile"))
|
||||
runLabel, imageName, err := shared.GetRunlabel(label, runlabelImage, ctx, runtime, pull, c.String("creds"), dockerRegistryOptions, authfile, c.String("signature-policy"), stdOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no label to execute, we return
|
||||
if runLabel == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The user provided extra arguments that need to be tacked onto the label's command
|
||||
if len(args) > 2 {
|
||||
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " "))
|
||||
}
|
||||
|
||||
cmd, err := shared.GenerateCommand(runLabel, imageName, c.String("name"))
|
||||
cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, c.String("name"), opts, extraArgs)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to generate command")
|
||||
return err
|
||||
}
|
||||
env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts)
|
||||
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
|
||||
|
||||
envmap := envSliceToMap(env)
|
||||
|
||||
envmapper := func(k string) string {
|
||||
switch k {
|
||||
case "OPT1":
|
||||
return envmap["OPT1"]
|
||||
case "OPT2":
|
||||
return envmap["OPT2"]
|
||||
case "OPT3":
|
||||
return envmap["OPT3"]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
newS := os.Expand(strings.Join(cmd, " "), envmapper)
|
||||
cmd = strings.Split(newS, " ")
|
||||
|
||||
if !c.Bool("quiet") {
|
||||
fmt.Printf("Command: %s\n", strings.Join(cmd, " "))
|
||||
if c.Bool("display") {
|
||||
@ -228,12 +178,3 @@ func runlabelCmd(c *cli.Context) error {
|
||||
}
|
||||
return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
|
||||
}
|
||||
|
||||
func envSliceToMap(env []string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, i := range env {
|
||||
split := strings.Split(i, "=")
|
||||
m[split[0]] = strings.Join(split[1:], " ")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/go-units"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@ -589,3 +594,79 @@ func portsToString(ports []ocicni.PortMapping) string {
|
||||
}
|
||||
return strings.Join(portDisplay, ", ")
|
||||
}
|
||||
|
||||
// GetRunlabel is a helper function for runlabel; it gets the image if needed and begins the
|
||||
// contruction of the runlabel output and environment variables
|
||||
func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtime *libpod.Runtime, pull bool, inputCreds string, dockerRegistryOptions image.DockerRegistryOptions, authfile string, signaturePolicyPath string, output io.Writer) (string, string, error) {
|
||||
var (
|
||||
newImage *image.Image
|
||||
err error
|
||||
imageName string
|
||||
)
|
||||
if pull {
|
||||
var registryCreds *types.DockerAuthConfig
|
||||
if inputCreds != "" {
|
||||
creds, err := util.ParseRegistryCreds(inputCreds)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
registryCreds = creds
|
||||
}
|
||||
dockerRegistryOptions.DockerRegistryCreds = registryCreds
|
||||
newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, false, false)
|
||||
} else {
|
||||
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
|
||||
}
|
||||
if err != nil {
|
||||
return "", "", errors.Wrapf(err, "unable to find image")
|
||||
}
|
||||
|
||||
if len(newImage.Names()) < 1 {
|
||||
imageName = newImage.ID()
|
||||
} else {
|
||||
imageName = newImage.Names()[0]
|
||||
}
|
||||
|
||||
runLabel, err := newImage.GetLabel(ctx, label)
|
||||
return runLabel, imageName, err
|
||||
}
|
||||
|
||||
// GenerateRunlabelCommand generates the command that will eventually be execucted by podman
|
||||
func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string) ([]string, []string, error) {
|
||||
// The user provided extra arguments that need to be tacked onto the label's command
|
||||
if len(extraArgs) > 0 {
|
||||
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " "))
|
||||
}
|
||||
cmd, err := GenerateCommand(runLabel, imageName, name)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "unable to generate command")
|
||||
}
|
||||
env := GenerateRunEnvironment(name, imageName, opts)
|
||||
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
|
||||
|
||||
envmap := envSliceToMap(env)
|
||||
|
||||
envmapper := func(k string) string {
|
||||
switch k {
|
||||
case "OPT1":
|
||||
return envmap["OPT1"]
|
||||
case "OPT2":
|
||||
return envmap["OPT2"]
|
||||
case "OPT3":
|
||||
return envmap["OPT3"]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
newS := os.Expand(strings.Join(cmd, " "), envmapper)
|
||||
cmd = strings.Split(newS, " ")
|
||||
return cmd, env, nil
|
||||
}
|
||||
|
||||
func envSliceToMap(env []string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, i := range env {
|
||||
split := strings.Split(i, "=")
|
||||
m[split[0]] = strings.Join(split[1:], " ")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
)
|
||||
|
||||
func substituteCommand(cmd string) (string, error) {
|
||||
@ -42,7 +44,11 @@ func GenerateCommand(command, imageName, name string) ([]string, error) {
|
||||
if name == "" {
|
||||
name = imageName
|
||||
}
|
||||
cmd := strings.Split(command, " ")
|
||||
|
||||
cmd, err := shlex.Split(command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prog, err := substituteCommand(cmd[0])
|
||||
if err != nil {
|
||||
|
@ -18,10 +18,11 @@ var (
|
||||
)
|
||||
|
||||
func TestGenerateCommand(t *testing.T) {
|
||||
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
|
||||
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
|
||||
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo \"hello world\""
|
||||
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
|
||||
newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "hello world", newCommand[11])
|
||||
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
|
||||
}
|
||||
|
||||
@ -108,8 +109,8 @@ func TestGenerateCommandNoSetName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateCommandNoName(t *testing.T) {
|
||||
inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
|
||||
correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
|
||||
inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
|
||||
correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
|
||||
newCommand, err := GenerateCommand(inputCommand, "foo", "")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
|
||||
|
@ -30,9 +30,10 @@ func ParallelWorker(wg *sync.WaitGroup, jobs <-chan ParallelWorkerInput, results
|
||||
|
||||
// ParallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker
|
||||
// int determines how many workers/threads should be premade.
|
||||
func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map[string]error {
|
||||
func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) (map[string]error, int) {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
wg sync.WaitGroup
|
||||
errorCount int
|
||||
)
|
||||
|
||||
resultChan := make(chan containerError, len(functions))
|
||||
@ -62,9 +63,12 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
|
||||
close(resultChan)
|
||||
for ctrError := range resultChan {
|
||||
results[ctrError.ContainerID] = ctrError.Err
|
||||
if ctrError.Err != nil {
|
||||
errorCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
return results, errorCount
|
||||
}
|
||||
|
||||
// Parallelize provides the maximum number of parallel workers (int) as calculated by a basic
|
||||
@ -72,20 +76,37 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
|
||||
func Parallelize(job string) int {
|
||||
numCpus := runtime.NumCPU()
|
||||
switch job {
|
||||
case "stop":
|
||||
if numCpus <= 2 {
|
||||
return 4
|
||||
} else {
|
||||
case "kill":
|
||||
if numCpus <= 3 {
|
||||
return numCpus * 3
|
||||
}
|
||||
return numCpus * 4
|
||||
case "pause":
|
||||
if numCpus <= 3 {
|
||||
return numCpus * 3
|
||||
}
|
||||
return numCpus * 4
|
||||
case "ps":
|
||||
return 8
|
||||
case "restart":
|
||||
return numCpus * 2
|
||||
case "rm":
|
||||
if numCpus <= 3 {
|
||||
return numCpus * 3
|
||||
} else {
|
||||
return numCpus * 4
|
||||
}
|
||||
case "ps":
|
||||
return 8
|
||||
case "stop":
|
||||
if numCpus <= 2 {
|
||||
return 4
|
||||
} else {
|
||||
return numCpus * 3
|
||||
}
|
||||
case "unpause":
|
||||
if numCpus <= 3 {
|
||||
return numCpus * 3
|
||||
}
|
||||
return numCpus * 4
|
||||
}
|
||||
return 3
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
@ -59,7 +60,13 @@ func stopCmd(c *cli.Context) error {
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
|
||||
if err != nil {
|
||||
if len(containers) == 0 {
|
||||
return err
|
||||
}
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
var stopFuncs []shared.ParallelWorkerInput
|
||||
for _, ctr := range containers {
|
||||
@ -71,7 +78,11 @@ func stopCmd(c *cli.Context) error {
|
||||
stopTimeout = ctr.StopTimeout()
|
||||
}
|
||||
f := func() error {
|
||||
return con.StopWithTimeout(stopTimeout)
|
||||
if err := con.StopWithTimeout(stopTimeout); err != nil && errors.Cause(err) != libpod.ErrCtrStopped {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
stopFuncs = append(stopFuncs, shared.ParallelWorkerInput{
|
||||
ContainerID: con.ID(),
|
||||
@ -85,15 +96,6 @@ func stopCmd(c *cli.Context) error {
|
||||
}
|
||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||
|
||||
stopErrors := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs)
|
||||
|
||||
for cid, result := range stopErrors {
|
||||
if result != nil && result != libpod.ErrCtrStopped {
|
||||
fmt.Println(result.Error())
|
||||
lastError = result
|
||||
continue
|
||||
}
|
||||
fmt.Println(cid)
|
||||
}
|
||||
return lastError
|
||||
stopErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs)
|
||||
return printParallelOutput(stopErrors, errCount)
|
||||
}
|
||||
|
@ -1,15 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
unpauseFlags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "all, a",
|
||||
Usage: "unpause all paused containers",
|
||||
},
|
||||
}
|
||||
unpauseDescription = `
|
||||
podman unpause
|
||||
|
||||
@ -19,6 +27,7 @@ var (
|
||||
Name: "unpause",
|
||||
Usage: "Unpause the processes in one or more containers",
|
||||
Description: unpauseDescription,
|
||||
Flags: unpauseFlags,
|
||||
Action: unpauseCmd,
|
||||
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
|
||||
OnUsageError: usageErrorHandler,
|
||||
@ -26,6 +35,10 @@ var (
|
||||
)
|
||||
|
||||
func unpauseCmd(c *cli.Context) error {
|
||||
var (
|
||||
unpauseContainers []*libpod.Container
|
||||
unpauseFuncs []shared.ParallelWorkerInput
|
||||
)
|
||||
if os.Geteuid() != 0 {
|
||||
return errors.New("unpause is not supported for rootless containers")
|
||||
}
|
||||
@ -37,28 +50,44 @@ func unpauseCmd(c *cli.Context) error {
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
args := c.Args()
|
||||
if len(args) < 1 {
|
||||
if len(args) < 1 && !c.Bool("all") {
|
||||
return errors.Errorf("you must provide at least one container name or id")
|
||||
}
|
||||
|
||||
var lastError error
|
||||
for _, arg := range args {
|
||||
ctr, err := runtime.LookupContainer(arg)
|
||||
if c.Bool("all") {
|
||||
cs, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStatePaused, "paused")
|
||||
if err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
lastError = errors.Wrapf(err, "error looking up container %q", arg)
|
||||
continue
|
||||
return err
|
||||
}
|
||||
if err = ctr.Unpause(); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
unpauseContainers = append(unpauseContainers, cs...)
|
||||
} else {
|
||||
for _, arg := range args {
|
||||
ctr, err := runtime.LookupContainer(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lastError = errors.Wrapf(err, "failed to unpause container %v", ctr.ID())
|
||||
} else {
|
||||
fmt.Println(ctr.ID())
|
||||
unpauseContainers = append(unpauseContainers, ctr)
|
||||
}
|
||||
}
|
||||
return lastError
|
||||
|
||||
// Assemble the unpause funcs
|
||||
for _, ctr := range unpauseContainers {
|
||||
con := ctr
|
||||
f := func() error {
|
||||
return con.Unpause()
|
||||
}
|
||||
|
||||
unpauseFuncs = append(unpauseFuncs, shared.ParallelWorkerInput{
|
||||
ContainerID: con.ID(),
|
||||
ParallelFunc: f,
|
||||
})
|
||||
}
|
||||
|
||||
maxWorkers := shared.Parallelize("unpause")
|
||||
if c.GlobalIsSet("max-workers") {
|
||||
maxWorkers = c.GlobalInt("max-workers")
|
||||
}
|
||||
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
||||
|
||||
unpauseErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, unpauseFuncs)
|
||||
return printParallelOutput(unpauseErrors, errCount)
|
||||
}
|
||||
|
@ -207,3 +207,20 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error
|
||||
}
|
||||
return pods, lastError
|
||||
}
|
||||
|
||||
//printParallelOutput takes the map of parallel worker results and outputs them
|
||||
// to stdout
|
||||
func printParallelOutput(m map[string]error, errCount int) error {
|
||||
var lastError error
|
||||
for cid, result := range m {
|
||||
if result != nil {
|
||||
if errCount > 1 {
|
||||
fmt.Println(result.Error())
|
||||
}
|
||||
lastError = result
|
||||
continue
|
||||
}
|
||||
fmt.Println(cid)
|
||||
}
|
||||
return lastError
|
||||
}
|
||||
|
@ -371,6 +371,22 @@ type PodContainerErrorData (
|
||||
reason: string
|
||||
)
|
||||
|
||||
# Runlabel describes the required input for container runlabel
|
||||
type Runlabel(
|
||||
image: string,
|
||||
authfile: string,
|
||||
certDir: string,
|
||||
creds: string,
|
||||
display: bool,
|
||||
name: string,
|
||||
pull: bool,
|
||||
signaturePolicyPath: string,
|
||||
tlsVerify: bool,
|
||||
label: string,
|
||||
extraArgs: []string,
|
||||
opts: [string]string
|
||||
)
|
||||
|
||||
# Ping provides a response for developers to ensure their varlink setup is working.
|
||||
# #### Example
|
||||
# ~~~
|
||||
@ -804,6 +820,42 @@ method TopPod() -> (notimplemented: NotImplemented)
|
||||
# ~~~
|
||||
method GetPodStats(name: string) -> (pod: string, containers: []ContainerStats)
|
||||
|
||||
# ImageExists talks a full or partial image ID or name and returns an int as to whether
|
||||
# the image exists in local storage. An int result of 0 means the image does exist in
|
||||
# local storage; whereas 1 indicates the image does not exists in local storage.
|
||||
method ImageExists(name: string) -> (exists: int)
|
||||
|
||||
# ContainerExists takes a full or partial container ID or name and returns an int as to
|
||||
# whether the container exists in local storage. A result of 0 means the container does
|
||||
# exists; whereas a result of 1 means it could not be found.
|
||||
method ContainerExists(name: string) -> (exists: int)
|
||||
|
||||
# ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container
|
||||
# ID. On successful checkpoint, the id of the checkpointed container is returned.
|
||||
method ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) -> (id: string)
|
||||
|
||||
# ContainerRestore restores a container that has been checkpointed. The container to be restored can
|
||||
# be identified by its name or full/partial container ID. A successful restore will result in the return
|
||||
# of the container's ID.
|
||||
method ContainerRestore(name: string, keep: bool, tcpEstablished: bool) -> (id: string)
|
||||
|
||||
# ContainerRunlabel runs executes a command as described by a given container image label.
|
||||
method ContainerRunlabel(runlabel: Runlabel) -> ()
|
||||
|
||||
# ListContainerMounts gathers all the mounted container mount points and returns them as an array
|
||||
# of strings
|
||||
method ListContainerMounts() -> (mounts: []string)
|
||||
|
||||
# MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination
|
||||
# mount is returned as a string.
|
||||
method MountContainer(name: string) -> (path: string)
|
||||
|
||||
# UnmountContainer umounts a container by its name or full/partial container ID.
|
||||
method UnmountContainer(name: string, force: bool) -> ()
|
||||
|
||||
# This function is not implemented yet.
|
||||
method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
|
||||
|
||||
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
|
||||
error ImageNotFound (name: string)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/formats"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
@ -15,13 +16,26 @@ func versionCmd(c *cli.Context) error {
|
||||
if err != nil {
|
||||
errors.Wrapf(err, "unable to determine version")
|
||||
}
|
||||
|
||||
versionOutputFormat := c.String("format")
|
||||
if versionOutputFormat != "" {
|
||||
var out formats.Writer
|
||||
switch versionOutputFormat {
|
||||
case formats.JSONString:
|
||||
out = formats.JSONStruct{Output: output}
|
||||
default:
|
||||
out = formats.StdoutTemplate{Output: output, Template: versionOutputFormat}
|
||||
}
|
||||
formats.Writer(out).Out()
|
||||
return nil
|
||||
}
|
||||
fmt.Println("Version: ", output.Version)
|
||||
fmt.Println("Go Version: ", output.GoVersion)
|
||||
if output.GitCommit != "" {
|
||||
fmt.Println("Git Commit: ", output.GitCommit)
|
||||
}
|
||||
// Prints out the build time in readable format
|
||||
if libpod.BuildInfo != "" {
|
||||
if output.Built != 0 {
|
||||
fmt.Println("Built: ", time.Unix(output.Built, 0).Format(time.ANSIC))
|
||||
}
|
||||
|
||||
@ -30,8 +44,17 @@ func versionCmd(c *cli.Context) error {
|
||||
}
|
||||
|
||||
// Cli command to print out the full version of podman
|
||||
var versionCommand = cli.Command{
|
||||
Name: "version",
|
||||
Usage: "Display the PODMAN Version Information",
|
||||
Action: versionCmd,
|
||||
}
|
||||
var (
|
||||
versionCommand = cli.Command{
|
||||
Name: "version",
|
||||
Usage: "Display the Podman Version Information",
|
||||
Action: versionCmd,
|
||||
Flags: versionFlags,
|
||||
}
|
||||
versionFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "Change the output format to JSON or a Go template",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
82
contrib/cirrus/README.md
Normal file
82
contrib/cirrus/README.md
Normal file
@ -0,0 +1,82 @@
|
||||

|
||||
|
||||
# Cirrus-CI
|
||||
|
||||
Similar to other integrated github CI/CD services, Cirrus utilizes a simple
|
||||
YAML-based configuration/description file: ``.cirrus.yml``. Ref: https://cirrus-ci.org/
|
||||
|
||||
## Workflow
|
||||
|
||||
All tasks execute in parallel, unless there are conditions or dependencies
|
||||
which alter this behavior. Within each task, each script executes in sequence,
|
||||
so long as any previous script exited successfully. The overall state of each
|
||||
task (pass or fail) is set based on the exit status of the last script to execute.
|
||||
|
||||
### ``full_vm_testing`` Task
|
||||
|
||||
1. Unconditionally, spin up one VM per ``matrix: image_name`` item defined
|
||||
in ``.cirrus.yml``. Once accessible, ``ssh`` into each VM and run the following
|
||||
scripts.
|
||||
|
||||
2. ``setup_environment.sh``: Configure root's ``.bash_profile``
|
||||
for all subsequent scripts (each run in a new shell). Any
|
||||
distribution-specific environment variables are also defined
|
||||
here. For example, setting tags/flags to use compiling.
|
||||
|
||||
3. ``verify_source.sh``: Perform per-distribution source
|
||||
verification, lint-checking, etc. This acts as a minimal
|
||||
gate, blocking extended use of VMs when a PR's code or commits
|
||||
would otherwise not be accepted. Should run for less than a minute.
|
||||
|
||||
4. ``unit_test.sh``: Execute unit-testing, as defined by the ``Makefile``.
|
||||
This should execute within 10-minutes, but often much faster.
|
||||
|
||||
5. ``integration_test.sh``: Execute integration-testing. This is
|
||||
much more involved, and relies on access to external
|
||||
resources like container images and code from other repositories.
|
||||
Total execution time is capped at 2-hours (includes all the above)
|
||||
but this script normally completes in less than an hour.
|
||||
|
||||
### ``optional_system_testing`` Task
|
||||
|
||||
1. Optionally executes in parallel with ``full_vm_testing``. Requires
|
||||
**prior** to job-start, the magic string ``***CIRRUS: SYSTEM TEST***``
|
||||
is found in the pull-request *description*. The *description* is the first
|
||||
text-box under the main *summary* line in the github WebUI.
|
||||
|
||||
2. ``setup_environment.sh``: Same as for other tasks.
|
||||
|
||||
3. ``system_test.sh``: Build both dependencies and libpod, install them,
|
||||
then execute `make localsystem` from the repository root.
|
||||
|
||||
### ``build_vm_images`` Task
|
||||
|
||||
1. When a PR is merged (``$CIRRUS_BRANCH`` == ``master``), Cirrus
|
||||
checks the last commit message. If it contains the magic string
|
||||
``***CIRRUS: REBUILD IMAGES***``, then this task continues.
|
||||
|
||||
2. Execute run another round of the ``full_vm_testing`` task (above).
|
||||
After the tests pass (post-merge), spin up a special VM
|
||||
(from the `image-builder-image`) capable of communicating with the
|
||||
GCE API. Once accessible, ``ssh`` into the VM and run the following scripts.
|
||||
|
||||
3. ``setup_environment.sh``: Same as for other tasks.
|
||||
|
||||
4. ``build_vm_images.sh``: Utilize [the packer tool](http://packer.io/docs/)
|
||||
to produce new VM images. Create a new VM from each base-image, connect
|
||||
to them with ``ssh``, and perform the steps as defined by the
|
||||
``$PACKER_BASE/libpod_images.json`` file:
|
||||
|
||||
1. On a base-image VM, as root, copy the current state of the repository
|
||||
into ``/tmp/libpod``.
|
||||
2. Execute distribution-specific scripts to prepare the image for
|
||||
use by the ``full_vm_testing`` task (above). These scripts all
|
||||
end with the suffix `_setup.sh` within the `$PACKER_BASE` directory.
|
||||
3. If successful, shut down each VM and create a new GCE Image
|
||||
named after the base image and the commit sha of the merge.
|
||||
|
||||
***Note:*** The ``.cirrus.yml`` file must be manually updated with the new
|
||||
images names, then the change sent in via a secondary pull-request. This
|
||||
ensures that all the ``full_vm_testing`` tasks can pass with the new images,
|
||||
before subjecting all future PRs to them. A workflow to automate this
|
||||
process is described in comments at the end of the ``.cirrus.yml`` file.
|
@ -22,10 +22,6 @@ SCRIPT_BASE $SCRIPT_BASE
|
||||
PACKER_BASE $PACKER_BASE
|
||||
"
|
||||
|
||||
# TODO: Skip building images if $CIRRUS_BRANCH =~ "master" and
|
||||
# commit message of $CIRRUS_CHANGE_IN_REPO contains a magic word
|
||||
# produced by 'commit_and_create_upstream_pr.sh' script (see .cirrus.yml)
|
||||
|
||||
show_env_vars
|
||||
|
||||
# Everything here is running on the 'image-builder-image' GCE image
|
||||
|
@ -106,7 +106,10 @@ ircmsg() {
|
||||
SCRIPT="$GOSRC/$SCRIPT_BASE/podbot.py"
|
||||
NICK="podbot_$CIRRUS_TASK_ID"
|
||||
NICK="${NICK:0:15}" # Any longer will break things
|
||||
set +e
|
||||
$SCRIPT $NICK $1
|
||||
echo "Ignoring exit($?)"
|
||||
set -e
|
||||
}
|
||||
|
||||
# Run sudo in directory with GOPATH set
|
||||
@ -117,7 +120,6 @@ cdsudo() {
|
||||
sudo --preserve-env=GOPATH --non-interactive bash -c "$CMD"
|
||||
}
|
||||
|
||||
|
||||
# Helper/wrapper script to only show stderr/stdout on non-zero exit
|
||||
install_ooe() {
|
||||
req_env_var "SCRIPT_BASE $SCRIPT_BASE"
|
||||
@ -142,8 +144,8 @@ EOF
|
||||
install_cni_plugins() {
|
||||
echo "Installing CNI Plugins from commit $CNI_COMMIT"
|
||||
req_env_var "
|
||||
GOPATH $GOPATH
|
||||
CNI_COMMIT $CNI_COMMIT
|
||||
GOPATH $GOPATH
|
||||
CNI_COMMIT $CNI_COMMIT
|
||||
"
|
||||
DEST="$GOPATH/src/github.com/containernetworking/plugins"
|
||||
rm -rf "$DEST"
|
||||
@ -155,14 +157,27 @@ install_cni_plugins() {
|
||||
sudo cp bin/* /usr/libexec/cni
|
||||
}
|
||||
|
||||
install_runc_from_git(){
|
||||
wd=$(pwd)
|
||||
DEST="$GOPATH/src/github.com/opencontainers/runc"
|
||||
rm -rf "$DEST"
|
||||
ooe.sh git clone https://github.com/opencontainers/runc.git "$DEST"
|
||||
cd "$DEST"
|
||||
ooe.sh git fetch origin --tags
|
||||
ooe.sh git checkout -q "$RUNC_COMMIT"
|
||||
ooe.sh make static BUILDTAGS="seccomp selinux"
|
||||
sudo install -m 755 runc /usr/bin/runc
|
||||
cd $wd
|
||||
}
|
||||
|
||||
install_runc(){
|
||||
OS_RELEASE_ID=$(os_release_id)
|
||||
echo "Installing RunC from commit $RUNC_COMMIT"
|
||||
echo "Platform is $OS_RELEASE_ID"
|
||||
req_env_var "
|
||||
GOPATH $GOPATH
|
||||
RUNC_COMMIT $RUNC_COMMIT
|
||||
OS_RELEASE_ID $OS_RELEASE_ID
|
||||
GOPATH $GOPATH
|
||||
RUNC_COMMIT $RUNC_COMMIT
|
||||
OS_RELEASE_ID $OS_RELEASE_ID
|
||||
"
|
||||
if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]; then
|
||||
echo "Running make install.libseccomp.sudo for ubuntu"
|
||||
@ -177,14 +192,7 @@ install_runc(){
|
||||
cd "$GOPATH/src/github.com/containers/libpod"
|
||||
ooe.sh sudo make install.libseccomp.sudo
|
||||
fi
|
||||
DEST="$GOPATH/src/github.com/opencontainers/runc"
|
||||
rm -rf "$DEST"
|
||||
ooe.sh git clone https://github.com/opencontainers/runc.git "$DEST"
|
||||
cd "$DEST"
|
||||
ooe.sh git fetch origin --tags
|
||||
ooe.sh git checkout -q "$RUNC_COMMIT"
|
||||
ooe.sh make static BUILDTAGS="seccomp selinux"
|
||||
sudo install -m 755 runc /usr/bin/runc
|
||||
install_runc_from_git
|
||||
}
|
||||
|
||||
install_buildah() {
|
||||
@ -202,8 +210,8 @@ install_buildah() {
|
||||
install_conmon(){
|
||||
echo "Installing conmon from commit $CRIO_COMMIT"
|
||||
req_env_var "
|
||||
GOPATH $GOPATH
|
||||
CRIO_COMMIT $CRIO_COMMIT
|
||||
GOPATH $GOPATH
|
||||
CRIO_COMMIT $CRIO_COMMIT
|
||||
"
|
||||
DEST="$GOPATH/src/github.com/kubernetes-sigs/cri-o.git"
|
||||
rm -rf "$DEST"
|
||||
@ -234,8 +242,8 @@ install_criu(){
|
||||
install_testing_dependencies() {
|
||||
echo "Installing ginkgo, gomega, and easyjson into \$GOPATH=$GOPATH"
|
||||
req_env_var "
|
||||
GOPATH $GOPATH
|
||||
GOSRC $GOSRC
|
||||
GOPATH $GOPATH
|
||||
GOSRC $GOSRC
|
||||
"
|
||||
cd "$GOSRC"
|
||||
ooe.sh go get -u github.com/onsi/ginkgo/ginkgo
|
||||
@ -263,7 +271,7 @@ install_varlink(){
|
||||
_finalize(){
|
||||
echo "Removing leftover giblets from cloud-init"
|
||||
cd /
|
||||
sudo rm -rf /var/lib/cloud
|
||||
sudo rm -rf /var/lib/cloud/instance?
|
||||
sudo rm -rf /root/.ssh/*
|
||||
sudo rm -rf /home/*
|
||||
}
|
||||
|
@ -21,8 +21,7 @@ install_ooe
|
||||
export GOPATH="$(mktemp -d)"
|
||||
trap "sudo rm -rf $GOPATH" EXIT
|
||||
|
||||
# breaks networking on f28/29 in GCE
|
||||
# ooe.sh sudo dnf update -y
|
||||
ooe.sh sudo dnf update -y
|
||||
|
||||
ooe.sh sudo dnf install -y \
|
||||
atomic-registries \
|
||||
|
@ -21,10 +21,13 @@ install_ooe
|
||||
export GOPATH="$(mktemp -d)"
|
||||
trap "sudo rm -rf $GOPATH" EXIT
|
||||
|
||||
ooe.sh sudo apt-get -qq update
|
||||
ooe.sh sudo apt-get -qq update # sometimes it needs to get it twice :S
|
||||
ooe.sh sudo apt-get -qq upgrade
|
||||
ooe.sh sudo apt-get -qq install --no-install-recommends \
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Try twice as workaround for minor networking problems
|
||||
echo "Updating system and installing package dependencies"
|
||||
ooe.sh sudo -E apt-get -qq update || sudo -E apt-get -qq update
|
||||
ooe.sh sudo -E apt-get -qq upgrade || sudo -E apt-get -qq upgrade
|
||||
ooe.sh sudo -E apt-get -qq install --no-install-recommends \
|
||||
apparmor \
|
||||
autoconf \
|
||||
automake \
|
||||
|
@ -53,6 +53,8 @@ then
|
||||
# Some setup needs to vary between distros
|
||||
case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
|
||||
ubuntu-18)
|
||||
# Always install runc on Ubuntu
|
||||
install_runc_from_git
|
||||
envstr='export BUILDTAGS="seccomp $($GOSRC/hack/btrfs_tag.sh) $($GOSRC/hack/btrfs_installed_tag.sh) $($GOSRC/hack/ostree_tag.sh) varlink exclude_graphdriver_devicemapper"'
|
||||
;;
|
||||
fedora-28) ;& # Continue to the next item
|
||||
|
33
contrib/cirrus/system_test.sh
Executable file
33
contrib/cirrus/system_test.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
source $(dirname $0)/lib.sh
|
||||
|
||||
req_env_var "
|
||||
GOSRC $GOSRC
|
||||
OS_RELEASE_ID $OS_RELEASE_ID
|
||||
OS_RELEASE_VER $OS_RELEASE_VER
|
||||
"
|
||||
|
||||
show_env_vars
|
||||
|
||||
set -x
|
||||
cd "$GOSRC"
|
||||
|
||||
case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
|
||||
ubuntu-18)
|
||||
make install.tools "BUILDTAGS=$BUILDTAGS"
|
||||
make "BUILDTAGS=$BUILDTAGS"
|
||||
make test-binaries "BUILDTAGS=$BUILDTAGS"
|
||||
;;
|
||||
fedora-28) ;&
|
||||
centos-7) ;&
|
||||
rhel-7)
|
||||
make install.tools
|
||||
make
|
||||
make test-binaries
|
||||
;;
|
||||
*) bad_os_id_ver ;;
|
||||
esac
|
||||
|
||||
make localsystem
|
69
contrib/gate/Dockerfile
Normal file
69
contrib/gate/Dockerfile
Normal file
@ -0,0 +1,69 @@
|
||||
FROM fedora:28
|
||||
RUN dnf -y install \
|
||||
atomic-registries \
|
||||
btrfs-progs-devel \
|
||||
buildah \
|
||||
bzip2 \
|
||||
conmon \
|
||||
container-selinux \
|
||||
containernetworking-cni \
|
||||
containernetworking-cni-devel \
|
||||
device-mapper-devel \
|
||||
findutils \
|
||||
git \
|
||||
glib2-devel \
|
||||
glibc-static \
|
||||
gnupg \
|
||||
golang \
|
||||
gpgme-devel \
|
||||
iptables \
|
||||
libassuan-devel \
|
||||
libseccomp-devel \
|
||||
libselinux-devel \
|
||||
lsof \
|
||||
make \
|
||||
nmap-ncat \
|
||||
ostree-devel \
|
||||
procps-ng \
|
||||
python \
|
||||
python3-dateutil \
|
||||
python3-psutil \
|
||||
python3-pytoml \
|
||||
python3-varlink \
|
||||
skopeo-containers \
|
||||
slirp4netns \
|
||||
rsync \
|
||||
which \
|
||||
xz \
|
||||
&& dnf clean all
|
||||
|
||||
ENV GOPATH="/go" \
|
||||
PATH="/go/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" \
|
||||
SRCPATH="/usr/src/libpod" \
|
||||
GOSRC="/go/src/github.com/containers/libpod"
|
||||
|
||||
# Only needed for installing build-time dependencies
|
||||
COPY / $GOSRC
|
||||
|
||||
WORKDIR $GOSRC
|
||||
|
||||
# Install dependencies
|
||||
RUN set -x && \
|
||||
go get -u github.com/mailru/easyjson/... && \
|
||||
install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ && \
|
||||
make install.tools && \
|
||||
install -D -m 755 $GOSRC/contrib/gate/entrypoint.sh /usr/local/bin/ && \
|
||||
rm -rf "$GOSRC"
|
||||
|
||||
# Install cni config
|
||||
#RUN make install.cni
|
||||
RUN mkdir -p /etc/cni/net.d/
|
||||
COPY cni/87-podman-bridge.conflist /etc/cni/net.d/87-podman-bridge.conflist
|
||||
|
||||
# Make sure we have some policy for pulling images
|
||||
RUN mkdir -p /etc/containers
|
||||
COPY test/policy.json /etc/containers/policy.json
|
||||
COPY test/redhat_sigstore.yaml /etc/containers/registries.d/registry.access.redhat.com.yaml
|
||||
|
||||
VOLUME ["/usr/src/libpod"]
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
4
contrib/gate/README.md
Normal file
4
contrib/gate/README.md
Normal file
@ -0,0 +1,4 @@
|
||||

|
||||
|
||||
A standard container image for `gofmt` and lint-checking the libpod
|
||||
repository. The [contributors guide contains the documentation for usage.](https://github.com/containers/libpod/blob/master/CONTRIBUTING.md#go-format-and-lint)
|
15
contrib/gate/entrypoint.sh
Executable file
15
contrib/gate/entrypoint.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
[[ -n "$SRCPATH" ]] || \
|
||||
( echo "ERROR: \$SRCPATH must be non-empty" && exit 1 )
|
||||
[[ -n "$GOSRC" ]] || \
|
||||
( echo "ERROR: \$GOSRC must be non-empty" && exit 2 )
|
||||
[[ -r "${SRCPATH}/contrib/gate/Dockerfile" ]] || \
|
||||
( echo "ERROR: Expecting libpod repository root at $SRCPATH" && exit 3 )
|
||||
|
||||
# Working from a copy avoids needing to perturb the actual source files
|
||||
mkdir -p "$GOSRC"
|
||||
/usr/bin/rsync --recursive --links --quiet --safe-links \
|
||||
--perms --times "${SRCPATH}/" "${GOSRC}/"
|
||||
cd "$GOSRC"
|
||||
make "$@"
|
@ -4,6 +4,7 @@ PODMAN_VERSION ?= '0.0.4'
|
||||
|
||||
.PHONY: python-podman
|
||||
python-podman:
|
||||
PODMAN_VERSION=$(PODMAN_VERSION) \
|
||||
$(PYTHON) setup.py sdist bdist
|
||||
|
||||
.PHONY: lint
|
||||
@ -16,6 +17,7 @@ integration:
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
PODMAN_VERSION=$(PODMAN_VERSION) \
|
||||
$(PYTHON) setup.py install --root ${DESTDIR}
|
||||
|
||||
.PHONY: upload
|
||||
|
@ -19,9 +19,13 @@ class Mixin:
|
||||
"""
|
||||
if stdin is None:
|
||||
stdin = sys.stdin.fileno()
|
||||
elif hasattr(stdin, 'fileno'):
|
||||
stdin = stdin.fileno()
|
||||
|
||||
if stdout is None:
|
||||
stdout = sys.stdout.fileno()
|
||||
elif hasattr(stdout, 'fileno'):
|
||||
stdout = stdout.fileno()
|
||||
|
||||
with self._client() as podman:
|
||||
attach = podman.GetAttachSockets(self._id)
|
||||
@ -49,7 +53,7 @@ class Mixin:
|
||||
def resize_handler(self):
|
||||
"""Send the new window size to conmon."""
|
||||
|
||||
def wrapped(signum, frame):
|
||||
def wrapped(signum, frame): # pylint: disable=unused-argument
|
||||
packed = fcntl.ioctl(self.pseudo_tty.stdout, termios.TIOCGWINSZ,
|
||||
struct.pack('HHHH', 0, 0, 0, 0))
|
||||
rows, cols, _, _ = struct.unpack('HHHH', packed)
|
||||
@ -67,7 +71,7 @@ class Mixin:
|
||||
def log_handler(self):
|
||||
"""Send command to reopen log to conmon."""
|
||||
|
||||
def wrapped(signum, frame):
|
||||
def wrapped(signum, frame): # pylint: disable=unused-argument
|
||||
with open(self.pseudo_tty.control_socket, 'w') as skt:
|
||||
# send conmon reopen log message
|
||||
skt.write('2\n')
|
||||
|
@ -1,12 +1,12 @@
|
||||
"""Models for manipulating containers and storage."""
|
||||
import collections
|
||||
import functools
|
||||
import getpass
|
||||
import json
|
||||
import logging
|
||||
import signal
|
||||
import time
|
||||
|
||||
from . import fold_keys
|
||||
from ._containers_attach import Mixin as AttachMixin
|
||||
from ._containers_start import Mixin as StartMixin
|
||||
|
||||
@ -14,25 +14,27 @@ from ._containers_start import Mixin as StartMixin
|
||||
class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
"""Model for a container."""
|
||||
|
||||
def __init__(self, client, id, data):
|
||||
def __init__(self, client, ident, data, refresh=True):
|
||||
"""Construct Container Model."""
|
||||
super(Container, self).__init__(data)
|
||||
|
||||
self._client = client
|
||||
self._id = id
|
||||
self._id = ident
|
||||
|
||||
with client() as podman:
|
||||
self._refresh(podman)
|
||||
if refresh:
|
||||
with client() as podman:
|
||||
self._refresh(podman)
|
||||
else:
|
||||
for k, v in self.data.items():
|
||||
setattr(self, k, v)
|
||||
if 'containerrunning' in self.data:
|
||||
setattr(self, 'running', self.data['containerrunning'])
|
||||
self.data['running'] = self.data['containerrunning']
|
||||
|
||||
assert self._id == data['id'],\
|
||||
'Requested container id({}) does not match store id({})'.format(
|
||||
self._id, data['id']
|
||||
)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Get items from parent dict."""
|
||||
return super().__getitem__(key)
|
||||
|
||||
def _refresh(self, podman, tries=1):
|
||||
try:
|
||||
ctnr = podman.GetContainer(self._id)
|
||||
@ -71,18 +73,18 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
results = podman.ListContainerChanges(self._id)
|
||||
return results['container']
|
||||
|
||||
def kill(self, signal=signal.SIGTERM, wait=25):
|
||||
def kill(self, sig=signal.SIGTERM, wait=25):
|
||||
"""Send signal to container.
|
||||
|
||||
default signal is signal.SIGTERM.
|
||||
wait n of seconds, 0 waits forever.
|
||||
"""
|
||||
with self._client() as podman:
|
||||
podman.KillContainer(self._id, signal)
|
||||
podman.KillContainer(self._id, sig)
|
||||
timeout = time.time() + wait
|
||||
while True:
|
||||
self._refresh(podman)
|
||||
if self.status != 'running':
|
||||
if self.status != 'running': # pylint: disable=no-member
|
||||
return self
|
||||
|
||||
if wait and timeout < time.time():
|
||||
@ -90,20 +92,11 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
def _lower_hook(self):
|
||||
"""Convert all keys to lowercase."""
|
||||
|
||||
@functools.wraps(self._lower_hook)
|
||||
def wrapped(input_):
|
||||
return {k.lower(): v for (k, v) in input_.items()}
|
||||
|
||||
return wrapped
|
||||
|
||||
def inspect(self):
|
||||
"""Retrieve details about containers."""
|
||||
with self._client() as podman:
|
||||
results = podman.InspectContainer(self._id)
|
||||
obj = json.loads(results['container'], object_hook=self._lower_hook())
|
||||
obj = json.loads(results['container'], object_hook=fold_keys())
|
||||
return collections.namedtuple('ContainerInspect', obj.keys())(**obj)
|
||||
|
||||
def export(self, target):
|
||||
@ -115,19 +108,16 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
results = podman.ExportContainer(self._id, target)
|
||||
return results['tarfile']
|
||||
|
||||
def commit(self,
|
||||
image_name,
|
||||
*args,
|
||||
changes=[],
|
||||
message='',
|
||||
pause=True,
|
||||
**kwargs):
|
||||
def commit(self, image_name, **kwargs):
|
||||
"""Create image from container.
|
||||
|
||||
All changes overwrite existing values.
|
||||
See inspect() to obtain current settings.
|
||||
Keyword arguments:
|
||||
author -- change image's author
|
||||
message -- change image's message, docker format only.
|
||||
pause -- pause container during commit
|
||||
change -- Additional properties to change
|
||||
|
||||
Changes:
|
||||
Change examples:
|
||||
CMD=/usr/bin/zsh
|
||||
ENTRYPOINT=/bin/sh date
|
||||
ENV=TEST=test_containers.TestContainers.test_commit
|
||||
@ -136,21 +126,23 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
USER=bozo:circus
|
||||
VOLUME=/data
|
||||
WORKDIR=/data/application
|
||||
"""
|
||||
# TODO: Clean up *args, **kwargs after Commit() is complete
|
||||
try:
|
||||
author = kwargs.get('author', getpass.getuser())
|
||||
except Exception: # pylint: disable=broad-except
|
||||
author = ''
|
||||
|
||||
for c in changes:
|
||||
All changes overwrite existing values.
|
||||
See inspect() to obtain current settings.
|
||||
"""
|
||||
author = kwargs.get('author', None) or getpass.getuser()
|
||||
change = kwargs.get('change', None) or []
|
||||
message = kwargs.get('message', None) or ''
|
||||
pause = kwargs.get('pause', None) or True
|
||||
|
||||
for c in change:
|
||||
if c.startswith('LABEL=') and c.count('=') < 2:
|
||||
raise ValueError(
|
||||
'LABEL should have the format: LABEL=label=value, not {}'.
|
||||
format(c))
|
||||
|
||||
with self._client() as podman:
|
||||
results = podman.Commit(self._id, image_name, changes, author,
|
||||
results = podman.Commit(self._id, image_name, change, author,
|
||||
message, pause)
|
||||
return results['image']
|
||||
|
||||
@ -175,7 +167,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
podman.RestartContainer(self._id, timeout)
|
||||
return self._refresh(podman)
|
||||
|
||||
def rename(self, target):
|
||||
def rename(self, target): # pylint: disable=unused-argument
|
||||
"""Rename container, return id on success."""
|
||||
with self._client() as podman:
|
||||
# TODO: Need arguments
|
||||
@ -183,7 +175,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
# TODO: fixup objects cached information
|
||||
return results['container']
|
||||
|
||||
def resize_tty(self, width, height):
|
||||
def resize_tty(self, width, height): # pylint: disable=unused-argument
|
||||
"""Resize container tty."""
|
||||
with self._client() as podman:
|
||||
# TODO: magic re: attach(), arguments
|
||||
@ -201,7 +193,8 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
podman.UnpauseContainer(self._id)
|
||||
return self._refresh(podman)
|
||||
|
||||
def update_container(self, *args, **kwargs):
|
||||
def update_container(self, *args, **kwargs): \
|
||||
# pylint: disable=unused-argument
|
||||
"""TODO: Update container..., return id on success."""
|
||||
with self._client() as podman:
|
||||
podman.UpdateContainer()
|
||||
@ -220,7 +213,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
|
||||
obj = results['container']
|
||||
return collections.namedtuple('StatDetail', obj.keys())(**obj)
|
||||
|
||||
def logs(self, *args, **kwargs):
|
||||
def logs(self, *args, **kwargs): # pylint: disable=unused-argument
|
||||
"""Retrieve container logs."""
|
||||
with self._client() as podman:
|
||||
results = podman.GetContainerLogs(self._id)
|
||||
@ -239,7 +232,7 @@ class Containers():
|
||||
with self._client() as podman:
|
||||
results = podman.ListContainers()
|
||||
for cntr in results['containers']:
|
||||
yield Container(self._client, cntr['id'], cntr)
|
||||
yield Container(self._client, cntr['id'], cntr, refresh=False)
|
||||
|
||||
def delete_stopped(self):
|
||||
"""Delete all stopped containers."""
|
||||
|
@ -27,9 +27,10 @@ class Image(collections.UserDict):
|
||||
|
||||
@staticmethod
|
||||
def _split_token(values=None, sep='='):
|
||||
if not values:
|
||||
return {}
|
||||
return {
|
||||
k: v1
|
||||
for k, v1 in (v0.split(sep, 1) for v0 in values if values)
|
||||
k: v1 for k, v1 in (v0.split(sep, 1) for v0 in values)
|
||||
}
|
||||
|
||||
def create(self, *args, **kwargs):
|
||||
@ -74,7 +75,7 @@ class Image(collections.UserDict):
|
||||
obj = json.loads(results['image'], object_hook=fold_keys())
|
||||
return collections.namedtuple('ImageInspect', obj.keys())(**obj)
|
||||
|
||||
def push(self, target, tlsverify=False):
|
||||
def push(self, target, tlsverify=True):
|
||||
"""Copy image to target, return id on success."""
|
||||
with self._client() as podman:
|
||||
results = podman.PushImage(self._id, target, tlsverify)
|
||||
@ -137,7 +138,7 @@ class Images():
|
||||
results = podman.DeleteUnusedImages()
|
||||
return results['images']
|
||||
|
||||
def import_image(self, source, reference, message=None, changes=None):
|
||||
def import_image(self, source, reference, message='', changes=None):
|
||||
"""Read image tarball from source and save in image store."""
|
||||
with self._client() as podman:
|
||||
results = podman.ImportImage(source, reference, message, changes)
|
||||
|
@ -152,7 +152,7 @@ class TestContainers(PodmanTestCase):
|
||||
changes.append('WORKDIR=/data/application')
|
||||
|
||||
id = self.alpine_ctnr.commit(
|
||||
'alpine3', author='Bozo the clown', changes=changes, pause=True)
|
||||
'alpine3', author='Bozo the clown', change=changes, pause=True)
|
||||
img = self.pclient.images.get(id)
|
||||
self.assertIsNotNone(img)
|
||||
|
||||
|
@ -102,7 +102,7 @@ class TestImages(PodmanTestCase):
|
||||
def test_push(self):
|
||||
path = '{}/alpine_push'.format(self.tmpdir)
|
||||
target = 'dir:{}'.format(path)
|
||||
self.alpine_image.push(target)
|
||||
self.alpine_image.push(target, tlsverify=False)
|
||||
|
||||
self.assertTrue(os.path.isfile(os.path.join(path, 'manifest.json')))
|
||||
self.assertTrue(os.path.isfile(os.path.join(path, 'version')))
|
||||
|
@ -52,7 +52,8 @@ class TestPodsCtnrs(PodmanTestCase):
|
||||
status = FoldedString(pod.containersinfo[0]['status'])
|
||||
self.assertIn(status, ('stopped', 'exited', 'running'))
|
||||
|
||||
killed = pod.kill()
|
||||
# Pod kill is broken, so use stop for now
|
||||
killed = pod.stop()
|
||||
self.assertEqual(pod, killed)
|
||||
|
||||
def test_999_remove(self):
|
||||
|
@ -4,6 +4,7 @@ PODMAN_VERSION ?= '0.0.4'
|
||||
|
||||
.PHONY: python-pypodman
|
||||
python-pypodman:
|
||||
PODMAN_VERSION=$(PODMAN_VERSION) \
|
||||
$(PYTHON) setup.py sdist bdist
|
||||
|
||||
.PHONY: lint
|
||||
@ -16,6 +17,7 @@ integration:
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
PODMAN_VERSION=$(PODMAN_VERSION) \
|
||||
$(PYTHON) setup.py install --root ${DESTDIR}
|
||||
|
||||
.PHONY: upload
|
||||
|
@ -85,7 +85,7 @@ overwriting earlier. Any missing items are ignored.
|
||||
.IP \[bu] 2
|
||||
From \f[C]\-\-config\-home\f[] command line option + \f[C]pypodman/pypodman.conf\f[]
|
||||
.IP \[bu] 2
|
||||
From environment variable, for example: RUN_DIR
|
||||
From environment variable prefixed with PODMAN_, for example: PODMAN_RUN_DIR
|
||||
.IP \[bu] 2
|
||||
From command line option, for example: \[en]run\-dir
|
||||
.PP
|
||||
|
@ -4,14 +4,15 @@ import sys
|
||||
import podman
|
||||
from pypodman.lib.action_base import AbstractActionBase
|
||||
from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate,
|
||||
PathAction, PositiveIntAction,
|
||||
UnitAction)
|
||||
ChangeAction, PathAction,
|
||||
PositiveIntAction, UnitAction)
|
||||
from pypodman.lib.podman_parser import PodmanArgumentParser
|
||||
from pypodman.lib.report import Report, ReportColumn
|
||||
|
||||
# Silence pylint overlording...
|
||||
assert BooleanAction
|
||||
assert BooleanValidate
|
||||
assert ChangeAction
|
||||
assert PathAction
|
||||
assert PositiveIntAction
|
||||
assert UnitAction
|
||||
|
@ -22,6 +22,8 @@ from pypodman.lib.actions.rm_action import Rm
|
||||
from pypodman.lib.actions.rmi_action import Rmi
|
||||
from pypodman.lib.actions.run_action import Run
|
||||
from pypodman.lib.actions.search_action import Search
|
||||
from pypodman.lib.actions.start_action import Start
|
||||
from pypodman.lib.actions.version_action import Version
|
||||
|
||||
__all__ = [
|
||||
'Attach',
|
||||
@ -47,4 +49,6 @@ __all__ = [
|
||||
'Rmi',
|
||||
'Run',
|
||||
'Search',
|
||||
'Start',
|
||||
'Version',
|
||||
]
|
||||
|
@ -2,7 +2,7 @@
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase, BooleanAction
|
||||
from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction
|
||||
|
||||
|
||||
class Commit(AbstractActionBase):
|
||||
@ -12,7 +12,9 @@ class Commit(AbstractActionBase):
|
||||
def subparser(cls, parent):
|
||||
"""Add Commit command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'commit', help='create image from container')
|
||||
'commit',
|
||||
help='create image from container',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--author',
|
||||
help='Set the author for the committed image',
|
||||
@ -20,11 +22,7 @@ class Commit(AbstractActionBase):
|
||||
parser.add_argument(
|
||||
'--change',
|
||||
'-c',
|
||||
choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
|
||||
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
|
||||
action='append',
|
||||
type=str.upper,
|
||||
help='Apply the following possible changes to the created image',
|
||||
action=ChangeAction,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--format',
|
||||
@ -32,7 +30,8 @@ class Commit(AbstractActionBase):
|
||||
choices=('oci', 'docker'),
|
||||
default='oci',
|
||||
type=str.lower,
|
||||
help='Set the format of the image manifest and metadata',
|
||||
help='Set the format of the image manifest and metadata.'
|
||||
' (Ignored.)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--iidfile',
|
||||
@ -42,7 +41,8 @@ class Commit(AbstractActionBase):
|
||||
parser.add_argument(
|
||||
'--message',
|
||||
'-m',
|
||||
help='Set commit message for committed image',
|
||||
help='Set commit message for committed image'
|
||||
' (Only on docker images.)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--pause',
|
||||
@ -69,27 +69,11 @@ class Commit(AbstractActionBase):
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='commit')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Commit class."""
|
||||
if not args.container:
|
||||
raise ValueError('You must supply one container id'
|
||||
' or name to be used as source.')
|
||||
if not args.image:
|
||||
raise ValueError('You must supply one image id'
|
||||
' or name to be created.')
|
||||
super().__init__(args)
|
||||
|
||||
# used only on client
|
||||
del self.opts['image']
|
||||
del self.opts['container']
|
||||
|
||||
def commit(self):
|
||||
"""Create image from container."""
|
||||
try:
|
||||
try:
|
||||
ctnr = self.client.containers.get(self._args.container[0])
|
||||
ident = ctnr.commit(**self.opts)
|
||||
print(ident)
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
@ -97,6 +81,17 @@ class Commit(AbstractActionBase):
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
else:
|
||||
ident = ctnr.commit(
|
||||
self.opts['image'][0],
|
||||
change=self.opts.get('change', None),
|
||||
message=self.opts.get('message', None),
|
||||
pause=self.opts['pause'],
|
||||
author=self.opts.get('author', None),
|
||||
)
|
||||
|
||||
if not self.opts['quiet']:
|
||||
print(ident)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
@ -104,3 +99,4 @@ class Commit(AbstractActionBase):
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
return 0
|
||||
|
@ -21,7 +21,7 @@ class Create(AbstractActionBase):
|
||||
parser.add_argument('image', nargs=1, help='source image id')
|
||||
parser.add_argument(
|
||||
'command',
|
||||
nargs='*',
|
||||
nargs=parent.REMAINDER,
|
||||
help='command and args to run.',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='create')
|
||||
|
@ -12,13 +12,16 @@ class Export(AbstractActionBase):
|
||||
def subparser(cls, parent):
|
||||
"""Add Export command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'export', help='export container to tarball')
|
||||
'export',
|
||||
help='export container to tarball',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
'-o',
|
||||
metavar='PATH',
|
||||
nargs=1,
|
||||
help='Write to a file',
|
||||
required=True,
|
||||
help='Write to this file on host',
|
||||
)
|
||||
parser.add_argument(
|
||||
'container',
|
||||
@ -27,23 +30,11 @@ class Export(AbstractActionBase):
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='export')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Export class."""
|
||||
if not args.container:
|
||||
raise ValueError('You must supply one container id'
|
||||
' or name to be used as source.')
|
||||
|
||||
if not args.output:
|
||||
raise ValueError('You must supply one filename'
|
||||
' to be created as tarball using --output.')
|
||||
super().__init__(args)
|
||||
|
||||
def export(self):
|
||||
"""Create tarball from container filesystem."""
|
||||
try:
|
||||
try:
|
||||
ctnr = self.client.containers.get(self._args.container[0])
|
||||
ctnr.export(self._args.output[0])
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
@ -51,6 +42,8 @@ class Export(AbstractActionBase):
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
else:
|
||||
ctnr.export(self._args.output[0])
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
@ -58,3 +51,4 @@ class Export(AbstractActionBase):
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
return 0
|
||||
|
@ -60,7 +60,7 @@ class History(AbstractActionBase):
|
||||
if self._args.human:
|
||||
fields.update({
|
||||
'size':
|
||||
humanize.naturalsize(details.size, binary=True),
|
||||
humanize.naturalsize(details.size),
|
||||
'created':
|
||||
humanize.naturaldate(
|
||||
podman.datetime_parse(details.created)),
|
||||
|
@ -37,7 +37,7 @@ class Images(AbstractActionBase):
|
||||
|
||||
self.columns = OrderedDict({
|
||||
'name':
|
||||
ReportColumn('name', 'REPOSITORY', 40),
|
||||
ReportColumn('name', 'REPOSITORY', 0),
|
||||
'tag':
|
||||
ReportColumn('tag', 'TAG', 10),
|
||||
'id':
|
||||
@ -65,18 +65,18 @@ class Images(AbstractActionBase):
|
||||
'created':
|
||||
humanize.naturaldate(podman.datetime_parse(image.created)),
|
||||
'size':
|
||||
humanize.naturalsize(int(image.size), binary=True),
|
||||
humanize.naturalsize(int(image.size)),
|
||||
'repoDigests':
|
||||
' '.join(image.repoDigests),
|
||||
})
|
||||
|
||||
for r in image.repoTags:
|
||||
name, tag = r.split(':', 1)
|
||||
name, tag = r.rsplit(':', 1)
|
||||
fields.update({
|
||||
'name': name,
|
||||
'tag': tag,
|
||||
})
|
||||
rows.append(fields)
|
||||
rows.append(fields)
|
||||
|
||||
if not self._args.digests:
|
||||
del self.columns['repoDigests']
|
||||
|
@ -2,7 +2,7 @@
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase
|
||||
from pypodman.lib import AbstractActionBase, ChangeAction
|
||||
|
||||
|
||||
class Import(AbstractActionBase):
|
||||
@ -12,18 +12,19 @@ class Import(AbstractActionBase):
|
||||
def subparser(cls, parent):
|
||||
"""Add Import command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'import', help='import tarball as image filesystem')
|
||||
'import',
|
||||
help='import tarball as image filesystem',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--change',
|
||||
'-c',
|
||||
action='append',
|
||||
choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL',
|
||||
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
|
||||
type=str.upper,
|
||||
help='Apply the following possible instructions',
|
||||
action=ChangeAction,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--message', '-m', help='Set commit message for imported image.')
|
||||
'--message',
|
||||
'-m',
|
||||
help='Set commit message for imported image.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'source',
|
||||
metavar='PATH',
|
||||
@ -38,18 +39,25 @@ class Import(AbstractActionBase):
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='import_')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Import class."""
|
||||
super().__init__(args)
|
||||
|
||||
def import_(self):
|
||||
"""Import tarball as image filesystem."""
|
||||
# ImportImage() validates it's parameters therefore we need to create
|
||||
# pristine dict() for keywords
|
||||
options = {}
|
||||
if 'message' in self.opts:
|
||||
options['message'] = self.opts['message']
|
||||
if 'change' in self.opts and self.opts['change']:
|
||||
options['changes'] = self.opts['change']
|
||||
|
||||
reference = self.opts['reference'][0] if 'reference' in self.opts\
|
||||
else None
|
||||
|
||||
try:
|
||||
ident = self.client.images.import_image(
|
||||
self.opts.source,
|
||||
self.opts.reference,
|
||||
message=self.opts.message,
|
||||
changes=self.opts.change)
|
||||
self.opts['source'][0],
|
||||
reference,
|
||||
**options,
|
||||
)
|
||||
print(ident)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
@ -58,3 +66,4 @@ class Import(AbstractActionBase):
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
return 0
|
||||
|
@ -23,9 +23,9 @@ class Inspect(AbstractActionBase):
|
||||
help='Type of object to inspect',
|
||||
)
|
||||
parser.add_argument(
|
||||
'size',
|
||||
'--size',
|
||||
action='store_true',
|
||||
default=True,
|
||||
default=False,
|
||||
help='Display the total file size if the type is a container.'
|
||||
' Always True.')
|
||||
parser.add_argument(
|
||||
@ -59,7 +59,7 @@ class Inspect(AbstractActionBase):
|
||||
|
||||
def inspect(self):
|
||||
"""Inspect provided podman objects."""
|
||||
output = {}
|
||||
output = []
|
||||
try:
|
||||
for ident in self._args.objects:
|
||||
obj = None
|
||||
@ -78,7 +78,13 @@ class Inspect(AbstractActionBase):
|
||||
msg = 'Object "{}" not found'.format(ident)
|
||||
print(msg, file=sys.stderr, flush=True)
|
||||
else:
|
||||
output.update(obj._asdict())
|
||||
fields = obj._asdict()
|
||||
if not self._args.size:
|
||||
try:
|
||||
del fields['sizerootfs']
|
||||
except KeyError:
|
||||
pass
|
||||
output.append(fields)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
|
@ -16,6 +16,7 @@ class Ps(AbstractActionBase):
|
||||
"""Add Images command to parent parser."""
|
||||
parser = parent.add_parser('ps', help='list containers')
|
||||
super().subparser(parser)
|
||||
|
||||
parser.add_argument(
|
||||
'--sort',
|
||||
choices=('createdat', 'id', 'image', 'names', 'runningfor', 'size',
|
||||
@ -32,9 +33,9 @@ class Ps(AbstractActionBase):
|
||||
|
||||
self.columns = OrderedDict({
|
||||
'id':
|
||||
ReportColumn('id', 'CONTAINER ID', 14),
|
||||
ReportColumn('id', 'CONTAINER ID', 12),
|
||||
'image':
|
||||
ReportColumn('image', 'IMAGE', 30),
|
||||
ReportColumn('image', 'IMAGE', 31),
|
||||
'command':
|
||||
ReportColumn('column', 'COMMAND', 20),
|
||||
'createdat':
|
||||
@ -49,10 +50,15 @@ class Ps(AbstractActionBase):
|
||||
|
||||
def list(self):
|
||||
"""List containers."""
|
||||
if self._args.all:
|
||||
ictnrs = self.client.containers.list()
|
||||
else:
|
||||
ictnrs = filter(
|
||||
lambda c: podman.FoldedString(c['status']) == 'running',
|
||||
self.client.containers.list())
|
||||
|
||||
# TODO: Verify sorting on dates and size
|
||||
ctnrs = sorted(
|
||||
self.client.containers.list(),
|
||||
key=operator.attrgetter(self._args.sort))
|
||||
ctnrs = sorted(ictnrs, key=operator.attrgetter(self._args.sort))
|
||||
if not ctnrs:
|
||||
return
|
||||
|
||||
@ -65,9 +71,6 @@ class Ps(AbstractActionBase):
|
||||
'createdat':
|
||||
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
|
||||
})
|
||||
|
||||
if self._args.truncate:
|
||||
fields.update({'image': ctnr.image[-30:]})
|
||||
rows.append(fields)
|
||||
|
||||
with Report(self.columns, heading=self._args.heading) as report:
|
||||
|
@ -21,7 +21,7 @@ class Run(AbstractActionBase):
|
||||
parser.add_argument('image', nargs=1, help='source image id.')
|
||||
parser.add_argument(
|
||||
'command',
|
||||
nargs='*',
|
||||
nargs=parent.REMAINDER,
|
||||
help='command and args to run.',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='run')
|
||||
|
76
contrib/python/pypodman/pypodman/lib/actions/start_action.py
Normal file
76
contrib/python/pypodman/pypodman/lib/actions/start_action.py
Normal file
@ -0,0 +1,76 @@
|
||||
"""Remote client command for starting containers."""
|
||||
import sys
|
||||
|
||||
import podman
|
||||
from pypodman.lib import AbstractActionBase, BooleanAction
|
||||
|
||||
|
||||
class Start(AbstractActionBase):
|
||||
"""Class for starting container."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Start command to parent parser."""
|
||||
parser = parent.add_parser('start', help='start container')
|
||||
parser.add_argument(
|
||||
'--attach',
|
||||
'-a',
|
||||
action=BooleanAction,
|
||||
default=False,
|
||||
help="Attach container's STDOUT and STDERR (default: %(default)s)")
|
||||
parser.add_argument(
|
||||
'--detach-keys',
|
||||
metavar='KEY(s)',
|
||||
default=4,
|
||||
help='Override the key sequence for detaching a container.'
|
||||
' (format: a single character [a-Z] or ctrl-<value> where'
|
||||
' <value> is one of: a-z, @, ^, [, , or _) (default: ^D)')
|
||||
parser.add_argument(
|
||||
'--interactive',
|
||||
'-i',
|
||||
action=BooleanAction,
|
||||
default=False,
|
||||
help="Attach container's STDIN (default: %(default)s)")
|
||||
# TODO: Implement sig-proxy
|
||||
parser.add_argument(
|
||||
'--sig-proxy',
|
||||
action=BooleanAction,
|
||||
default=False,
|
||||
help="Proxy received signals to the process (default: %(default)s)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'containers',
|
||||
nargs='+',
|
||||
help='containers to start',
|
||||
)
|
||||
parser.set_defaults(class_=cls, method='start')
|
||||
|
||||
def start(self):
|
||||
"""Start provided containers."""
|
||||
stdin = sys.stdin if self.opts['interactive'] else None
|
||||
stdout = sys.stdout if self.opts['attach'] else None
|
||||
|
||||
try:
|
||||
for ident in self._args.containers:
|
||||
try:
|
||||
ctnr = self.client.containers.get(ident)
|
||||
ctnr.attach(
|
||||
eot=self.opts['detach_keys'],
|
||||
stdin=stdin,
|
||||
stdout=stdout)
|
||||
ctnr.start()
|
||||
except podman.ContainerNotFound as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'Container "{}" not found'.format(e.name),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
else:
|
||||
print(ident)
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
@ -0,0 +1,41 @@
|
||||
"""Remote client command for reporting on Podman service."""
|
||||
import json
|
||||
import sys
|
||||
|
||||
import podman
|
||||
import yaml
|
||||
from pypodman.lib import AbstractActionBase
|
||||
|
||||
|
||||
class Version(AbstractActionBase):
|
||||
"""Class for reporting on Podman Service."""
|
||||
|
||||
@classmethod
|
||||
def subparser(cls, parent):
|
||||
"""Add Version command to parent parser."""
|
||||
parser = parent.add_parser(
|
||||
'version', help='report version on podman service')
|
||||
parser.set_defaults(class_=cls, method='version')
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct Version class."""
|
||||
super().__init__(args)
|
||||
|
||||
def version(self):
|
||||
"""Report on Podman Service."""
|
||||
try:
|
||||
info = self.client.system.info()
|
||||
except podman.ErrorOccurred as e:
|
||||
sys.stdout.flush()
|
||||
print(
|
||||
'{}'.format(e.reason).capitalize(),
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
return 1
|
||||
else:
|
||||
version = info._asdict()['podman']
|
||||
host = info._asdict()['host']
|
||||
print("Version {}".format(version['podman_version']))
|
||||
print("Go Version {}".format(version['go_version']))
|
||||
print("Git Commit {}".format(version['git_commit']))
|
||||
print("OS/Arch {}/{}".format(host["os"], host["arch"]))
|
@ -4,9 +4,10 @@ Supplimental argparse.Action converters and validaters.
|
||||
The constructors are very verbose but remain for IDE support.
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import os
|
||||
|
||||
# API defined by argparse.Action shut up pylint
|
||||
# API defined by argparse.Action therefore shut up pylint
|
||||
# pragma pylint: disable=redefined-builtin
|
||||
# pragma pylint: disable=too-few-public-methods
|
||||
# pragma pylint: disable=too-many-arguments
|
||||
@ -36,7 +37,7 @@ class BooleanAction(argparse.Action):
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=('True', 'False'),
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar='{True,False}'):
|
||||
@ -58,11 +59,58 @@ class BooleanAction(argparse.Action):
|
||||
try:
|
||||
val = BooleanValidate()(values)
|
||||
except ValueError:
|
||||
parser.error('{} must be True or False.'.format(self.dest))
|
||||
parser.error('"{}" must be True or False.'.format(option_string))
|
||||
else:
|
||||
setattr(namespace, self.dest, val)
|
||||
|
||||
|
||||
class ChangeAction(argparse.Action):
|
||||
"""Convert and validate change argument."""
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=[],
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar='OPT=VALUE'):
|
||||
"""Create ChangeAction object."""
|
||||
help = (help or '') + ('Apply change(s) to the new image.'
|
||||
' May be given multiple times.')
|
||||
|
||||
super().__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
nargs=nargs,
|
||||
const=const,
|
||||
default=default,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
"""Convert and Validate input."""
|
||||
items = getattr(namespace, self.dest, None) or []
|
||||
items = copy.copy(items)
|
||||
|
||||
choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
|
||||
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR')
|
||||
|
||||
opt, val = values.split('=', 1)
|
||||
if opt not in choices:
|
||||
parser.error('Option "{}" is not supported by argument "{}",'
|
||||
' valid options are: {}'.format(
|
||||
opt, option_string, ', '.join(choices)))
|
||||
items.append(values)
|
||||
setattr(namespace, self.dest, items)
|
||||
|
||||
|
||||
class UnitAction(argparse.Action):
|
||||
"""Validate number given is positive integer, with optional suffix."""
|
||||
|
||||
@ -78,8 +126,8 @@ class UnitAction(argparse.Action):
|
||||
help=None,
|
||||
metavar='UNIT'):
|
||||
"""Create UnitAction object."""
|
||||
help = (help or metavar or dest
|
||||
) + ' (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||
help = (help or metavar or dest)\
|
||||
+ ' (format: <number>[<unit>], where unit = b, k, m or g)'
|
||||
super().__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
@ -99,15 +147,15 @@ class UnitAction(argparse.Action):
|
||||
except ValueError:
|
||||
if not values[:-1].isdigit():
|
||||
msg = ('{} must be a positive integer,'
|
||||
' with optional suffix').format(self.dest)
|
||||
' with optional suffix').format(option_string)
|
||||
parser.error(msg)
|
||||
if not values[-1] in ('b', 'k', 'm', 'g'):
|
||||
msg = '{} only supports suffices of: b, k, m, g'.format(
|
||||
self.dest)
|
||||
option_string)
|
||||
parser.error(msg)
|
||||
else:
|
||||
if val <= 0:
|
||||
msg = '{} must be a positive integer'.format(self.dest)
|
||||
msg = '{} must be a positive integer'.format(option_string)
|
||||
parser.error(msg)
|
||||
|
||||
setattr(namespace, self.dest, values)
|
||||
@ -125,19 +173,16 @@ class PositiveIntAction(argparse.Action):
|
||||
type=int,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
help='Must be a positive integer.',
|
||||
metavar=None):
|
||||
"""Create PositiveIntAction object."""
|
||||
self.message = '{} must be a positive integer'.format(dest)
|
||||
help = help or self.message
|
||||
|
||||
super().__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
nargs=nargs,
|
||||
const=const,
|
||||
default=default,
|
||||
type=int,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
help=help,
|
||||
@ -149,7 +194,8 @@ class PositiveIntAction(argparse.Action):
|
||||
setattr(namespace, self.dest, values)
|
||||
return
|
||||
|
||||
parser.error(self.message)
|
||||
msg = '{} must be a positive integer'.format(option_string)
|
||||
parser.error(msg)
|
||||
|
||||
|
||||
class PathAction(argparse.Action):
|
||||
|
@ -97,6 +97,8 @@ class PodmanArgumentParser(argparse.ArgumentParser):
|
||||
|
||||
actions_parser = self.add_subparsers(
|
||||
dest='subparser_name', help='commands')
|
||||
# For create/exec/run: don't process options intended for subcommand
|
||||
actions_parser.REMAINDER = argparse.REMAINDER
|
||||
|
||||
# import buried here to prevent import loops
|
||||
import pypodman.lib.actions # pylint: disable=cyclic-import
|
||||
@ -152,7 +154,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
|
||||
reqattr(
|
||||
'run_dir',
|
||||
getattr(args, 'run_dir')
|
||||
or os.environ.get('RUN_DIR')
|
||||
or os.environ.get('PODMAN_RUN_DIR')
|
||||
or config['default'].get('run_dir')
|
||||
or str(Path(args.xdg_runtime_dir, 'pypodman'))
|
||||
) # yapf: disable
|
||||
@ -161,23 +163,24 @@ class PodmanArgumentParser(argparse.ArgumentParser):
|
||||
args,
|
||||
'host',
|
||||
getattr(args, 'host')
|
||||
or os.environ.get('HOST')
|
||||
or os.environ.get('PODMAN_HOST')
|
||||
or config['default'].get('host')
|
||||
) # yapf:disable
|
||||
|
||||
reqattr(
|
||||
'username',
|
||||
getattr(args, 'username')
|
||||
or os.environ.get('PODMAN_USER')
|
||||
or config['default'].get('username')
|
||||
or os.environ.get('USER')
|
||||
or os.environ.get('LOGNAME')
|
||||
or config['default'].get('username')
|
||||
or getpass.getuser()
|
||||
) # yapf:disable
|
||||
|
||||
reqattr(
|
||||
'port',
|
||||
getattr(args, 'port')
|
||||
or os.environ.get('PORT')
|
||||
or os.environ.get('PODMAN_PORT')
|
||||
or config['default'].get('port', None)
|
||||
or 22
|
||||
) # yapf:disable
|
||||
@ -185,7 +188,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
|
||||
reqattr(
|
||||
'remote_socket_path',
|
||||
getattr(args, 'remote_socket_path')
|
||||
or os.environ.get('REMOTE_SOCKET_PATH')
|
||||
or os.environ.get('PODMAN_REMOTE_SOCKET_PATH')
|
||||
or config['default'].get('remote_socket_path')
|
||||
or '/run/podman/io.podman'
|
||||
) # yapf:disable
|
||||
@ -193,7 +196,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
|
||||
reqattr(
|
||||
'log_level',
|
||||
getattr(args, 'log_level')
|
||||
or os.environ.get('LOG_LEVEL')
|
||||
or os.environ.get('PODMAN_LOG_LEVEL')
|
||||
or config['default'].get('log_level')
|
||||
or logging.WARNING
|
||||
) # yapf:disable
|
||||
@ -202,7 +205,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
|
||||
args,
|
||||
'identity_file',
|
||||
getattr(args, 'identity_file')
|
||||
or os.environ.get('IDENTITY_FILE')
|
||||
or os.environ.get('PODMAN_IDENTITY_FILE')
|
||||
or config['default'].get('identity_file')
|
||||
or os.path.expanduser('~{}/.ssh/id_dsa'.format(args.username))
|
||||
) # yapf:disable
|
||||
|
@ -1,8 +1,23 @@
|
||||
"""Report Manager."""
|
||||
import string
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
class ReportFormatter(string.Formatter):
|
||||
"""Custom formatter to default missing keys to '<none>'."""
|
||||
|
||||
def get_value(self, key, args, kwargs):
|
||||
"""Map missing key to value '<none>'."""
|
||||
try:
|
||||
if isinstance(key, int):
|
||||
return args[key]
|
||||
else:
|
||||
return kwargs[key]
|
||||
except KeyError:
|
||||
return '<none>'
|
||||
|
||||
|
||||
class ReportColumn(namedtuple('ReportColumn', 'key display width default')):
|
||||
"""Hold attributes of output column."""
|
||||
|
||||
@ -26,18 +41,24 @@ class Report():
|
||||
"""
|
||||
self._columns = columns
|
||||
self._file = file
|
||||
self._format_string = None
|
||||
self._formatter = ReportFormatter()
|
||||
self._heading = heading
|
||||
self.epilog = epilog
|
||||
self._format = None
|
||||
|
||||
def row(self, **fields):
|
||||
"""Print row for report."""
|
||||
if self._heading:
|
||||
hdrs = {k: v.display for (k, v) in self._columns.items()}
|
||||
print(self._format.format(**hdrs), flush=True, file=self._file)
|
||||
print(
|
||||
self._formatter.format(self._format_string, **hdrs),
|
||||
flush=True,
|
||||
file=self._file,
|
||||
)
|
||||
self._heading = False
|
||||
|
||||
fields = {k: str(v) for k, v in fields.items()}
|
||||
print(self._format.format(**fields))
|
||||
print(self._formatter.format(self._format_string, **fields))
|
||||
|
||||
def __enter__(self):
|
||||
"""Return `self` upon entering the runtime context."""
|
||||
@ -63,4 +84,4 @@ class Report():
|
||||
display_len = info.width
|
||||
|
||||
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
|
||||
self._format = ' '.join(fmt)
|
||||
self._format_string = ' '.join(fmt)
|
||||
|
@ -39,7 +39,7 @@
|
||||
%global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7})
|
||||
|
||||
Name: podman
|
||||
Version: 0.10.2
|
||||
Version: 0.11.2
|
||||
Release: #COMMITDATE#.git%{shortcommit0}%{?dist}
|
||||
Summary: Manage Pods, Containers and Container Images
|
||||
License: ASL 2.0
|
||||
@ -378,10 +378,6 @@ providing packages with %{import_path} prefix.
|
||||
|
||||
%prep
|
||||
%autosetup -Sgit -n %{repo}-%{shortcommit0}
|
||||
sed -i '/\/bin\/env/d' completions/bash/%{name}
|
||||
sed -i 's/0.0.0/%{version}/' contrib/python/%{name}/setup.py
|
||||
sed -i 's/0.0.0/%{version}/' contrib/python/py%{name}/setup.py
|
||||
mv pkg/hooks/README.md pkg/hooks/README-hooks.md
|
||||
|
||||
# untar cri-o
|
||||
tar zxf %{SOURCE1}
|
||||
@ -416,15 +412,17 @@ popd
|
||||
|
||||
%install
|
||||
install -dp %{buildroot}%{_unitdir}
|
||||
%{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \
|
||||
PODMAN_VERSION=%{version} %{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \
|
||||
install.bin \
|
||||
install.man \
|
||||
install.cni \
|
||||
install.systemd \
|
||||
install.completions
|
||||
|
||||
mv pkg/hooks/README.md pkg/hooks/README-hooks.md
|
||||
|
||||
%if %{with varlink}
|
||||
%{__make} DESTDIR=%{buildroot} install.python
|
||||
PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} install.python
|
||||
%endif # varlink
|
||||
|
||||
# install libpod.conf
|
||||
|
@ -24,6 +24,18 @@ libpod to manage containers.
|
||||
**cgroup_manager**=""
|
||||
Specify the CGroup Manager to use; valid values are "systemd" and "cgroupfs"
|
||||
|
||||
**hooks_dir**=["*path*", ...]
|
||||
|
||||
Each `*.json` file in the path configures a hook for Podman containers. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated.
|
||||
|
||||
Paths listed later in the array higher precedence (`oci-hooks(5)` discusses directory precedence).
|
||||
|
||||
For the annotation conditions, libpod uses any annotations set in the generated OCI configuration.
|
||||
|
||||
For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered.
|
||||
|
||||
If `hooks_dir` is unset for root callers, Podman and libpod will currently default to `/usr/share/containers/oci/hooks.d` and `/etc/containers/oci/hooks.d` in order of increasing precedence. Using these defaults is deprecated, and callers should migrate to explicitly setting `hooks_dir`.
|
||||
|
||||
**static_dir**=""
|
||||
Directory for persistent libpod files (database, etc)
|
||||
By default this will be configured relative to where containers/storage
|
||||
|
@ -171,7 +171,7 @@ value can be entered. The password is entered without echo.
|
||||
**--disable-content-trust**
|
||||
|
||||
This is a Docker specific option to disable image verification to a Docker
|
||||
registry and is not supported by Buildah. This flag is a NOOP and provided
|
||||
registry and is not supported by Podman. This flag is a NOOP and provided
|
||||
soley for scripting compatibility.
|
||||
|
||||
**--file, -f** *Dockerfile*
|
||||
|
@ -17,6 +17,25 @@ are not deleted if checkpointing fails for further debugging. If checkpointing s
|
||||
files are theoretically not needed, but if these files are needed Podman can keep the files
|
||||
for further analysis.
|
||||
|
||||
**--all, -a**
|
||||
|
||||
Checkpoint all running containers.
|
||||
|
||||
**--latest, -l**
|
||||
|
||||
Instead of providing the container name or ID, checkpoint the last created container.
|
||||
|
||||
**--leave-running, -R**
|
||||
|
||||
Leave the container running after checkpointing instead of stopping it.
|
||||
|
||||
**--tcp-established**
|
||||
|
||||
Checkpoint a container with established TCP connections. If the checkpoint
|
||||
image contains established TCP connections, this options is required during
|
||||
restore. Defaults to not checkpointing containers with established TCP
|
||||
connections.
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
podman container checkpoint mywebserver
|
||||
|
40
docs/podman-container-exists.1.md
Normal file
40
docs/podman-container-exists.1.md
Normal file
@ -0,0 +1,40 @@
|
||||
% PODMAN(1) Podman Man Pages
|
||||
% Brent Baude
|
||||
% November 2018
|
||||
# NAME
|
||||
podman-container-exists- Check if a container exists in local storage
|
||||
|
||||
# SYNOPSIS
|
||||
**podman container exists**
|
||||
[**-h**|**--help**]
|
||||
CONTAINER
|
||||
|
||||
# DESCRIPTION
|
||||
**podman container exists** checks if a container exists in local storage. The **ID** or **Name**
|
||||
of the container may be used as input. Podman will return an exit code
|
||||
of `0` when the container is found. A `1` will be returned otherwise. An exit code of `125` indicates there
|
||||
was an issue accessing the local storage.
|
||||
|
||||
## Examples ##
|
||||
|
||||
Check if an container called `webclient` exists in local storage (the container does actually exist).
|
||||
```
|
||||
$ sudo podman container exists webclient
|
||||
$ echo $?
|
||||
0
|
||||
$
|
||||
```
|
||||
|
||||
Check if an container called `webbackend` exists in local storage (the container does not actually exist).
|
||||
```
|
||||
$ sudo podman container exists webbackend
|
||||
$ echo $?
|
||||
1
|
||||
$
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1)
|
||||
|
||||
# HISTORY
|
||||
November 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
|
@ -24,6 +24,22 @@ processes in the checkpointed container.
|
||||
Without the **-k**, **--keep** option the checkpoint will be consumed and cannot be used
|
||||
again.
|
||||
|
||||
**--all, -a**
|
||||
|
||||
Restore all checkpointed containers.
|
||||
|
||||
**--latest, -l**
|
||||
|
||||
Instead of providing the container name or ID, restore the last created container.
|
||||
|
||||
**--tcp-established**
|
||||
|
||||
Restore a container with established TCP connections. If the checkpoint image
|
||||
contains established TCP connections, this option is required during restore.
|
||||
If the checkpoint image does not contain established TCP connections this
|
||||
option is ignored. Defaults to not restoring containers with established TCP
|
||||
connections.
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
podman container restore mywebserver
|
||||
|
@ -20,6 +20,7 @@ The container command allows you to manage containers
|
||||
| create | [podman-create(1)](podman-create.1.md) | Create a new container. |
|
||||
| diff | [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. |
|
||||
| exec | [podman-exec(1)](podman-exec.1.md) | Execute a command in a running container. |
|
||||
| exists | [podman-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage |
|
||||
| export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. |
|
||||
| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. |
|
||||
| kill | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. |
|
||||
@ -38,7 +39,6 @@ The container command allows you to manage containers
|
||||
| stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
|
||||
| top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
|
||||
| umount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
|
||||
| unmount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
|
||||
| unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
|
||||
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
|
||||
|
||||
|
@ -66,7 +66,7 @@ Write the container ID to the file
|
||||
|
||||
**--conmon-pidfile**=""
|
||||
|
||||
Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers.
|
||||
Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers.
|
||||
|
||||
**--cpu-count**=*0*
|
||||
|
||||
@ -321,13 +321,13 @@ Not implemented
|
||||
|
||||
**--log-driver**="*json-file*"
|
||||
|
||||
Logging driver for the container. Default is defined by daemon `--log-driver` flag.
|
||||
**Warning**: the `podman logs` command works only for the `json-file` and
|
||||
`journald` logging drivers.
|
||||
Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility.
|
||||
|
||||
**--log-opt**=[]
|
||||
|
||||
Logging driver specific options.
|
||||
Logging driver specific options. Used to set the path to the container log file. For example:
|
||||
|
||||
`--log-opt path=/var/log/container/mycontainer.json`
|
||||
|
||||
**--mac-address**=""
|
||||
|
||||
@ -414,7 +414,7 @@ UUID short identifier (“f78375b1c487”)
|
||||
Name (“jonah”)
|
||||
|
||||
podman generates a UUID for each container, and if a name is not assigned
|
||||
to the container with **--name** then the daemon will also generate a random
|
||||
to the container with **--name** then it will generate a random
|
||||
string name. The name is useful any place you need to identify a container.
|
||||
This works for both background and foreground containers.
|
||||
|
||||
@ -426,7 +426,8 @@ Set the Network mode for the container
|
||||
'container:<name|id>': reuse another container's network stack
|
||||
'host': use the podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
|
||||
'<network-name>|<network-id>': connect to a user-defined network
|
||||
'ns:<path>' path to a network namespace to join
|
||||
'ns:<path>': path to a network namespace to join
|
||||
'slirp4netns': use slirp4netns to create a user network stack. This is the default for rootless containers
|
||||
|
||||
**--network-alias**=[]
|
||||
|
||||
@ -454,7 +455,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain
|
||||
|
||||
**--pod**=""
|
||||
|
||||
Run container in an existing pod
|
||||
Run container in an existing pod. If you want podman to make the pod for you, preference the pod name with `new:`.
|
||||
To make a pod with more granular options, use the `podman pod create` command before creating a container.
|
||||
|
||||
**--privileged**=*true*|*false*
|
||||
|
||||
@ -465,9 +467,10 @@ By default, podman containers are
|
||||
This is because by default a container is not allowed to access any devices.
|
||||
A “privileged” container is given access to all devices.
|
||||
|
||||
When the operator executes **podman run --privileged**, podman enables access
|
||||
to all devices on the host as well as set turn off most of the security measures
|
||||
protecting the host from the container.
|
||||
When the operator executes a privileged container, podman enables access
|
||||
to all devices on the host, turns off graphdriver mount options, as well as
|
||||
turning off most of the security measures protecting the host from the
|
||||
container.
|
||||
|
||||
**-p**, **--publish**=[]
|
||||
|
||||
|
40
docs/podman-image-exists.1.md
Normal file
40
docs/podman-image-exists.1.md
Normal file
@ -0,0 +1,40 @@
|
||||
% PODMAN(1) Podman Man Pages
|
||||
% Brent Baude
|
||||
% November 2018
|
||||
# NAME
|
||||
podman-image-exists- Check if an image exists in local storage
|
||||
|
||||
# SYNOPSIS
|
||||
**podman image exists**
|
||||
[**-h**|**--help**]
|
||||
IMAGE
|
||||
|
||||
# DESCRIPTION
|
||||
**podman image exists** checks if an image exists in local storage. The **ID** or **Name**
|
||||
of the image may be used as input. Podman will return an exit code
|
||||
of `0` when the image is found. A `1` will be returned otherwise. An exit code of `125` indicates there
|
||||
was an issue accessing the local storage.
|
||||
|
||||
## Examples ##
|
||||
|
||||
Check if an image called `webclient` exists in local storage (the image does actually exist).
|
||||
```
|
||||
$ sudo podman image exists webclient
|
||||
$ echo $?
|
||||
0
|
||||
$
|
||||
```
|
||||
|
||||
Check if an image called `webbackend` exists in local storage (the image does not actually exist).
|
||||
```
|
||||
$ sudo podman image exists webbackend
|
||||
$ echo $?
|
||||
1
|
||||
$
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1)
|
||||
|
||||
# HISTORY
|
||||
November 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
|
@ -14,6 +14,7 @@ The image command allows you to manage images
|
||||
| Command | Man Page | Description |
|
||||
| -------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. |
|
||||
| exists | [podman-exists(1)](podman-image-exists.1.md) | Check if a image exists in local storage |
|
||||
| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. |
|
||||
| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. |
|
||||
| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a image or image's configuration. |
|
||||
|
@ -49,9 +49,9 @@ Sort by created, id, repository, size or tag (default: created)
|
||||
```
|
||||
# podman images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251kB
|
||||
<none> <none> ebb91b73692b 4 weeks ago 27.2MB
|
||||
docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126MB
|
||||
docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251 kB
|
||||
<none> <none> ebb91b73692b 4 weeks ago 27.2 MB
|
||||
docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126 MB
|
||||
```
|
||||
|
||||
```
|
||||
@ -63,17 +63,17 @@ ebb91b73692b
|
||||
|
||||
```
|
||||
# podman images --noheading
|
||||
docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251kB
|
||||
<none> <none> ebb91b73692b 4 weeks ago 27.2MB
|
||||
docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126MB
|
||||
docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251 kB
|
||||
<none> <none> ebb91b73692b 4 weeks ago 27.2 MB
|
||||
docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126 MB
|
||||
```
|
||||
|
||||
```
|
||||
# podman images --no-trunc
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
docker.io/kubernetes/pause latest sha256:e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27 3 years ago 251kB
|
||||
<none> <none> sha256:ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9 4 weeks ago 27.2MB
|
||||
docker.io/library/ubuntu latest sha256:4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a 6 weeks ago 126MB
|
||||
docker.io/kubernetes/pause latest sha256:e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27 3 years ago 251 kB
|
||||
<none> <none> sha256:ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9 4 weeks ago 27.2 MB
|
||||
docker.io/library/ubuntu latest sha256:4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a 6 weeks ago 126 MB
|
||||
```
|
||||
|
||||
```
|
||||
@ -87,7 +87,7 @@ ebb91b73692b <none> <none>
|
||||
```
|
||||
# podman images --filter dangling=true
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
<none> <none> ebb91b73692b 4 weeks ago 27.2MB
|
||||
<none> <none> ebb91b73692b 4 weeks ago 27.2 MB
|
||||
```
|
||||
|
||||
```
|
||||
@ -126,25 +126,25 @@ REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
```
|
||||
# podman images --sort repository
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
<none> <none> 2460217d76fc About a minute ago 4.41MB
|
||||
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB
|
||||
localhost/myapp latest b2e0ad03474a About a minute ago 4.41MB
|
||||
registry.access.redhat.com/rhel7 latest 7a840db7f020 2 weeks ago 211MB
|
||||
registry.fedoraproject.org/fedora 27 801894bc0e43 6 weeks ago 246MB
|
||||
<none> <none> 2460217d76fc About a minute ago 4.41 MB
|
||||
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
|
||||
localhost/myapp latest b2e0ad03474a About a minute ago 4.41 MB
|
||||
registry.access.redhat.com/rhel7 latest 7a840db7f020 2 weeks ago 211 MB
|
||||
registry.fedoraproject.org/fedora 27 801894bc0e43 6 weeks ago 246 MB
|
||||
```
|
||||
|
||||
```
|
||||
# podman images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
localhost/test latest 18f0c080cd72 4 seconds ago 4.42MB
|
||||
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB
|
||||
localhost/test latest 18f0c080cd72 4 seconds ago 4.42 MB
|
||||
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
|
||||
# podman images -a
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
localhost/test latest 18f0c080cd72 6 seconds ago 4.42MB
|
||||
<none> <none> 270e70dc54c0 7 seconds ago 4.42MB
|
||||
<none> <none> 4ed6fbe43414 8 seconds ago 4.41MB
|
||||
<none> <none> 6b0df8e71508 8 seconds ago 4.41MB
|
||||
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB
|
||||
localhost/test latest 18f0c080cd72 6 seconds ago 4.42 MB
|
||||
<none> <none> 270e70dc54c0 7 seconds ago 4.42 MB
|
||||
<none> <none> 4ed6fbe43414 8 seconds ago 4.41 MB
|
||||
<none> <none> 6b0df8e71508 8 seconds ago 4.41 MB
|
||||
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
|
@ -4,7 +4,7 @@
|
||||
podman\-kill - Kills one or more containers with a signal
|
||||
|
||||
## SYNOPSIS
|
||||
**podman kill** [*options*] *container* ...
|
||||
**podman kill** [*options*] [*container* ...]
|
||||
|
||||
## DESCRIPTION
|
||||
The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user