Merge branch 'master' of github.com:containers/libpod into vendor

This commit is contained in:
Daniel J Walsh
2018-12-06 03:20:16 -05:00
337 changed files with 8940 additions and 3081 deletions

View File

@ -18,9 +18,9 @@ gce_instance:
env: env:
FEDORA_CNI_COMMIT: "412b6d31280682bb4fab4446f113c22ff1886554" FEDORA_CNI_COMMIT: "412b6d31280682bb4fab4446f113c22ff1886554"
CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9" CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9"
CRIO_COMMIT: "662dbb31b5d4f5ed54511a47cde7190c61c28677" CRIO_COMMIT: "7a283c391abb7bd25086a8ff91dbb36ebdd24466"
CRIU_COMMIT: "584cbe4643c3fc7dc901ff08bf923ca0fe7326f9" CRIU_COMMIT: "c74b83cd49c00589c0c0468ba5fe685b67fdbd0a"
RUNC_COMMIT: "78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d" RUNC_COMMIT: "96ec2177ae841256168fcf76954f7177af9446eb"
# File to update in home-dir with task-specific env. var values # File to update in home-dir with task-specific env. var values
ENVLIB: ".bash_profile" ENVLIB: ".bash_profile"
# Overrides default location (/tmp/cirrus) for repo clone # Overrides default location (/tmp/cirrus) for repo clone
@ -42,11 +42,12 @@ full_vm_testing_task:
# 'matrix' combinations. All run in parallel. # 'matrix' combinations. All run in parallel.
matrix: matrix:
# Images are generated separetly, from build_images_task (below) # 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) # TODO: Make these work (also build_images_task below)
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe" #image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
#image_name: "centos-7-v20180911-libpod-fce09afe" #image_name: "centos-7-v20180911-libpod-fce09afe"
#image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe" #image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe"
timeout_in: 120m timeout_in: 120m
# Every *_script runs in sequence, for each task. The name prefix is for # 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 success_script: $SCRIPT_BASE/success.sh
# This task build new images for future PR testing, but only after a PR merge. # Because system tests are stored within the repository, it is sometimes
# These images save needing to install/setup the same environment to test every # necessary to execute them within a PR to validate changes.
# 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 optional_system_testing_task:
# be automated (see comment at end).
# 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: build_vm_images_task:
# Only produce new images after a PR merge # Only produce new cache-images after a PR merge, and if a magic string
only_if: $CIRRUS_BRANCH == 'master' # 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. # Require tests to pass first.
depends_on: depends_on:
- test # i.e. 'test_task' - full_vm_testing # i.e. 'full_vm_testing_task'
env: env:
# CSV of packer builder names to enable (see $PACKER_BASE/libpod_images.json) # 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 # Version of packer to use
PACKER_VER: "1.3.1" PACKER_VER: "1.3.1"
# VMs created by packer are not cleaned up by cirrus
auto_cancellation: $CI != "true"
gce_instance: gce_instance:
image_name: "image-builder-image" # Simply CentOS 7 + packer dependencies image_name: "image-builder-image" # Simply CentOS 7 + packer dependencies

View File

@ -139,6 +139,3 @@ if [ $integrationtest -eq 1 ]; then
fi fi
make ginkgo GOPATH=/go $INTEGRATION_TEST_ENVS make ginkgo GOPATH=/go $INTEGRATION_TEST_ENVS
fi fi
exit 0

View File

@ -122,7 +122,7 @@ packages:
- python3-varlink - python3-varlink
- python3-dateutil - python3-dateutil
- python3-psutil - 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: tests:
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf - sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf
@ -163,6 +163,7 @@ packages:
- python3-dateutil - python3-dateutil
- python3-psutil - python3-psutil
- container-selinux - 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: tests:
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf - sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf

View File

@ -10,6 +10,13 @@ if [[ ${DIST} != "Fedora" ]]; then
PYTHON=python PYTHON=python
fi 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 # Build the test image
${CONTAINER_RUNTIME} build -t ${IMAGE} -f Dockerfile.${DIST} . 2>build.log ${CONTAINER_RUNTIME} build -t ${IMAGE} -f Dockerfile.${DIST} . 2>build.log

View File

@ -40,6 +40,8 @@ ${LINTER} \
--exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$'\ --exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$'\
--exclude='duplicate of.*_test.go.*\(dupl\)$'\ --exclude='duplicate of.*_test.go.*\(dupl\)$'\
--exclude='cmd\/client\/.*\.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='vendor\/.*'\
--exclude='podman\/.*'\ --exclude='podman\/.*'\
--exclude='server\/seccomp\/.*\.go.*$'\ --exclude='server\/seccomp\/.*\.go.*$'\

103
API.md
View File

@ -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 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 CreateContainer(create: Create) string](#CreateContainer)
[func CreateImage() NotImplemented](#CreateImage) [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 HistoryImage(name: string) ImageHistory](#HistoryImage)
[func ImageExists(name: string) int](#ImageExists)
[func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage) [func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage)
[func InspectContainer(name: string) string](#InspectContainer) [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 ListContainerChanges(name: string) ContainerChanges](#ListContainerChanges)
[func ListContainerMounts() []string](#ListContainerMounts)
[func ListContainerPorts(name: string) NotImplemented](#ListContainerPorts)
[func ListContainerProcesses(name: string, opts: []string) []string](#ListContainerProcesses) [func ListContainerProcesses(name: string, opts: []string) []string](#ListContainerProcesses)
[func ListContainers() ListContainerData](#ListContainers) [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 ListPods() ListPodData](#ListPods)
[func MountContainer(name: string) string](#MountContainer)
[func PauseContainer(name: string) string](#PauseContainer) [func PauseContainer(name: string) string](#PauseContainer)
[func PausePod(name: string) string](#PausePod) [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 TopPod() NotImplemented](#TopPod)
[func UnmountContainer(name: string, force: bool) ](#UnmountContainer)
[func UnpauseContainer(name: string) string](#UnpauseContainer) [func UnpauseContainer(name: string) string](#UnpauseContainer)
[func UnpausePod(name: string) string](#UnpausePod) [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 PodmanInfo](#PodmanInfo)
[type Runlabel](#Runlabel)
[type Sockets](#Sockets) [type Sockets](#Sockets)
[type StringResponse](#StringResponse) [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 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, 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. 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 ### <a name="CreateContainer"></a>func CreateContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <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 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 history is in the form of an array of ImageHistory structures. If the image cannot be found, an
[ImageNotFound](#ImageNotFound) error is returned. [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 ### <a name="ImportImage"></a>func ImportImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <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> 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 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. 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 ### <a name="ListContainerProcesses"></a>func ListContainerProcesses
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <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> method ListPods() [ListPodData](#ListPodData)</div>
ListPods returns a list of pods in no particular order. They are ListPods returns a list of pods in no particular order. They are
returned as an array of ListPodData structs. See also [GetPod](#GetPod). 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 ### <a name="PauseContainer"></a>func PauseContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <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> method TopPod() [NotImplemented](#NotImplemented)</div>
This method has not been implemented yet. 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 ### <a name="UnpauseContainer"></a>func UnpauseContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -1293,6 +1367,33 @@ insecure_registries [[]string](#[]string)
store [InfoStore](#InfoStore) store [InfoStore](#InfoStore)
podman [InfoPodmanBinary](#InfoPodmanBinary) 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 ### <a name="Sockets"></a>type Sockets
Sockets describes sockets location for a container 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. NoContainerRunning means none of the containers requested are running in a command that requires a running container.
### <a name="NoContainersInPod"></a>type NoContainersInPod ### <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. the pod ID.
### <a name="PodContainerError"></a>type PodContainerError ### <a name="PodContainerError"></a>type PodContainerError

View File

@ -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 If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`. 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 ### Integration Tests
Our primary means of performing integration testing for libpod is with the Our primary means of performing integration testing for libpod is with the

View File

@ -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 RUN set -x && cd /go/src/github.com/containers/libpod && make install.libseccomp.sudo
# Install runc # Install runc
ENV RUNC_COMMIT 78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d ENV RUNC_COMMIT 96ec2177ae841256168fcf76954f7177af9446eb
RUN set -x \ RUN set -x \
&& export GOPATH="$(mktemp -d)" \ && export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \ && git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
@ -64,7 +64,7 @@ RUN set -x \
&& rm -rf "$GOPATH" && rm -rf "$GOPATH"
# Install conmon # Install conmon
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677 ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \ RUN set -x \
&& export GOPATH="$(mktemp -d)" \ && export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ && 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/... \ && go get -u github.com/mailru/easyjson/... \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ && install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
# Install criu # Install latest stable criu version
ENV CRIU_COMMIT 584cbe4643c3fc7dc901ff08bf923ca0fe7326f9
RUN set -x \ RUN set -x \
&& cd /tmp \ && cd /tmp \
&& git clone https://github.com/checkpoint-restore/criu.git \ && git clone https://github.com/checkpoint-restore/criu.git \

View File

@ -68,7 +68,7 @@ RUN set -x \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ && install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
# Install conmon # Install conmon
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677 ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \ RUN set -x \
&& export GOPATH="$(mktemp -d)" \ && export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ && git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \

View File

@ -17,7 +17,7 @@ RUN dnf -y install btrfs-progs-devel \
libseccomp-devel \ libseccomp-devel \
libselinux-devel \ libselinux-devel \
skopeo-containers \ 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 \ make \
ostree-devel \ ostree-devel \
python \ python \
@ -72,7 +72,7 @@ RUN set -x \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ && install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
# Install conmon # Install conmon
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677 ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \ RUN set -x \
&& export GOPATH="$(mktemp -d)" \ && export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \ && git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \

View File

@ -1,6 +1,6 @@
GO ?= go GO ?= go
DESTDIR ?= / DESTDIR ?= /
EPOCH_TEST_COMMIT ?= 733cfe96819e1dc044e982b5321b3c902d1a47c6 EPOCH_TEST_COMMIT ?= 1b52843cfd2ae254a6e52c74e564730f1c875c4c
HEAD ?= HEAD HEAD ?= HEAD
CHANGELOG_BASE ?= HEAD~ CHANGELOG_BASE ?= HEAD~
CHANGELOG_TARGET ?= HEAD CHANGELOG_TARGET ?= HEAD
@ -23,7 +23,7 @@ BUILDTAGS_CROSS ?= containers_image_openpgp containers_image_ostree_stub exclude
ifneq (,$(findstring varlink,$(BUILDTAGS))) ifneq (,$(findstring varlink,$(BUILDTAGS)))
PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go
endif 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) 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 OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z) 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) 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}") GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
BUILD_INFO ?= $(shell date +%s) 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) ISODATE ?= $(shell date --iso-8601)
LIBSECCOMP_COMMIT := release-2.3 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) test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
$(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp $(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) podman: .gopathok $(PODMAN_VARLINK_DEPENDENCIES)
$(GO) build -i -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman $(GO) build -i -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman
@ -129,6 +133,7 @@ clean:
test/bin2img/bin2img \ test/bin2img/bin2img \
test/checkseccomp/checkseccomp \ test/checkseccomp/checkseccomp \
test/copyimg/copyimg \ test/copyimg/copyimg \
test/goecho/goecho \
test/testdata/redis-image \ test/testdata/redis-image \
cmd/podman/varlink/iopodman.go \ cmd/podman/varlink/iopodman.go \
libpod/container_ffjson.go \ libpod/container_ffjson.go \
@ -165,7 +170,7 @@ shell: libpodimage
testunit: 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 ${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) $(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
ginkgo: ginkgo:
@ -173,6 +178,12 @@ ginkgo:
localintegration: varlink_generate test-binaries clientintegration 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: clientintegration:
$(MAKE) -C contrib/python/podman integration $(MAKE) -C contrib/python/podman integration
$(MAKE) -C contrib/python/pypodman integration $(MAKE) -C contrib/python/pypodman integration
@ -182,7 +193,7 @@ vagrant-check:
binaries: varlink_generate easyjson_generate podman 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_MD ?= $(wildcard docs/*.md pkg/*/docs/*.md)
MANPAGES ?= $(MANPAGES_MD:%.md=%) MANPAGES ?= $(MANPAGES_MD:%.md=%)
@ -282,7 +293,7 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man .ins
if [ ! -x "$(GOBIN)/gometalinter" ]; then \ if [ ! -x "$(GOBIN)/gometalinter" ]; then \
$(GO) get -u github.com/alecthomas/gometalinter; \ $(GO) get -u github.com/alecthomas/gometalinter; \
cd $(FIRST_GOPATH)/src/github.com/alecthomas/gometalinter; \ cd $(FIRST_GOPATH)/src/github.com/alecthomas/gometalinter; \
git checkout 23261fa046586808612c61da7a81d75a658e0814; \ git checkout e8d801238da6f0dfd14078d68f9b53fa50a7eeb5; \
$(GO) install github.com/alecthomas/gometalinter; \ $(GO) install github.com/alecthomas/gometalinter; \
$(GOBIN)/gometalinter --install; \ $(GOBIN)/gometalinter --install; \
fi fi

5
OWNERS
View File

@ -3,9 +3,14 @@ approvers:
- baude - baude
- mrunalp - mrunalp
- rhatdan - rhatdan
- TomSweeneyRedHat
- umohnani8
- giuseppe
- vrothberg
reviewers: reviewers:
- mheon - mheon
- baude - baude
- mrunalp
- rhatdan - rhatdan
- TomSweeneyRedHat - TomSweeneyRedHat
- umohnani8 - umohnani8

View File

@ -1,9 +1,11 @@
![PODMAN logo](logo/podman-logo-source.svg) ![PODMAN logo](logo/podman-logo-source.svg)
# libpod - library for running OCI-based containers in Pods # libpod - library for running OCI-based containers in Pods
### Latest Version: 0.10.1.3 ### Latest Version: 0.11.1.1
### Status: Active Development ### Status: Active Development
### Continuous Integration: [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod)
## What is the scope of this project? ## What is the scope of this project?
libpod provides a library for applications looking to use the Container Pod concept popularized by Kubernetes. libpod provides a library for applications looking to use the Container Pod concept popularized by Kubernetes.

View File

@ -1,5 +1,53 @@
# Release Notes # 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 ## 0.10.1.3
### Bugfixes ### Bugfixes
- Fixed a bug where `podman build` would not work while any containers were running - Fixed a bug where `podman build` would not work while any containers were running

View File

@ -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) - Changelog for v0.10.1.3 (2018-10-17)
* Update release notes for 0.10.1.3 * Update release notes for 0.10.1.3
* Vendor in new new buildah/ci * Vendor in new new buildah/ci

View File

@ -1,6 +1,11 @@
package main package main
import ( import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/containers/buildah" "github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah" "github.com/containers/buildah/imagebuildah"
buildahcli "github.com/containers/buildah/pkg/cli" buildahcli "github.com/containers/buildah/pkg/cli"
@ -10,14 +15,14 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
"io/ioutil"
"os"
"path/filepath"
"strings"
) )
var ( var (
layerFlags = []cli.Flag{ 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{ cli.BoolTFlag{
Name: "layers", Name: "layers",
Usage: "cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override. ", 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, Layers: layers,
NoCache: c.Bool("no-cache"), NoCache: c.Bool("no-cache"),
RemoveIntermediateCtrs: c.BoolT("rm"), RemoveIntermediateCtrs: c.BoolT("rm"),
ForceRmIntermediateCtrs: c.Bool("force-rm"), ForceRmIntermediateCtrs: c.BoolT("force-rm"),
} }
if c.Bool("quiet") { if c.Bool("quiet") {

View File

@ -23,6 +23,14 @@ var (
Name: "keep, k", Name: "keep, k",
Usage: "keep all temporary checkpoint files", 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{ cli.BoolFlag{
Name: "all, a", Name: "all, a",
Usage: "checkpoint all running containers", Usage: "checkpoint all running containers",
@ -50,7 +58,11 @@ func checkpointCmd(c *cli.Context) error {
} }
defer runtime.Shutdown(false) 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 { if err := checkAllAndLatest(c); err != nil {
return err return err
@ -59,7 +71,7 @@ func checkpointCmd(c *cli.Context) error {
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
for _, ctr := range containers { 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 { if lastError != nil {
fmt.Fprintln(os.Stderr, lastError) fmt.Fprintln(os.Stderr, lastError)
} }

View File

@ -95,7 +95,7 @@ func commitCmd(c *cli.Context) error {
for _, change := range c.StringSlice("change") { for _, change := range c.StringSlice("change") {
splitChange := strings.Split(strings.ToUpper(change), "=") splitChange := strings.Split(strings.ToUpper(change), "=")
if !util.StringInSlice(splitChange[0], libpod.ChangeCmds) { if !util.StringInSlice(splitChange[0], libpod.ChangeCmds) {
return errors.Errorf("invalid syntax for --change ", change) return errors.Errorf("invalid syntax for --change: %s", change)
} }
} }
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/containers/buildah" "github.com/containers/buildah"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/fatih/camelcase" "github.com/fatih/camelcase"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -161,6 +162,13 @@ func getContext() context.Context {
return context.TODO() return context.TODO()
} }
func getDefaultNetwork() string {
if rootless.IsRootless() {
return "slirp4netns"
}
return "bridge"
}
// Common flags shared between commands // Common flags shared between commands
var createFlags = []cli.Flag{ var createFlags = []cli.Flag{
cli.StringSliceFlag{ cli.StringSliceFlag{
@ -372,7 +380,7 @@ var createFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "net, network", Name: "net, network",
Usage: "Connect a container to a network", Usage: "Connect a container to a network",
Value: "bridge", Value: getDefaultNetwork(),
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "oom-kill-disable", Name: "oom-kill-disable",

View File

@ -9,6 +9,7 @@ var (
attachCommand, attachCommand,
checkpointCommand, checkpointCommand,
cleanupCommand, cleanupCommand,
containerExistsCommand,
commitCommand, commitCommand,
createCommand, createCommand,
diffCommand, diffCommand,

View File

@ -11,6 +11,7 @@ import (
"syscall" "syscall"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations" ann "github.com/containers/libpod/pkg/annotations"
@ -66,7 +67,7 @@ func createCmd(c *cli.Context) error {
rootless.SetSkipStorageSetup(true) rootless.SetSkipStorageSetup(true)
} }
runtime, err := libpodruntime.GetContainerRuntime(c) runtime, err := libpodruntime.GetRuntime(c)
if err != nil { if err != nil {
return errors.Wrapf(err, "error creating libpod runtime") return errors.Wrapf(err, "error creating libpod runtime")
} }
@ -375,8 +376,8 @@ func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string {
return entrypoint return entrypoint
} }
func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string) (map[string]string, error) { func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) {
pod, err := runtime.LookupPod(c.String("pod")) pod, err := runtime.LookupPod(podName)
if err != nil { if err != nil {
return namespaces, err return namespaces, err
} }
@ -409,6 +410,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
inputCommand, command []string inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64 memoryLimit, memoryReservation, memorySwap, memoryKernel int64
blkioWeight uint16 blkioWeight uint16
namespaces map[string]string
) )
idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname")) idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
if err != nil { 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") 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 // Kernel Namespaces
// TODO Fix handling of namespace from pod // TODO Fix handling of namespace from pod
// Instead of integrating here, should be done in libpod // Instead of integrating here, should be done in libpod
// However, that also involves setting up security opts // However, that also involves setting up security opts
// when the pod's namespace is integrated // when the pod's namespace is integrated
namespaces := map[string]string{ namespaces = map[string]string{
"pid": c.String("pid"), "pid": c.String("pid"),
"net": c.String("net"), "net": c.String("net"),
"ipc": c.String("ipc"), "ipc": c.String("ipc"),
@ -505,8 +516,41 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
"uts": c.String("uts"), "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") { 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 { if err != nil {
return nil, err 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 // Make sure if network is set to container namespace, port binding is not also being asked for
netMode := ns.NetworkMode(namespaces["net"]) netMode := ns.NetworkMode(namespaces["net"])
if netMode.IsContainer() { 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") 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") 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 // SHM Size
shmSize, err := units.FromHumanSize(c.String("shm-size")) shmSize, err := units.FromHumanSize(c.String("shm-size"))
if err != nil { 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 { 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 '.'") 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 // Validate domains are good
for _, dom := range c.StringSlice("dns-search") { for _, dom := range c.StringSlice("dns-search") {
@ -741,7 +781,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
NetMode: netMode, NetMode: netMode,
UtsMode: utsMode, UtsMode: utsMode,
PidMode: pidMode, PidMode: pidMode,
Pod: c.String("pod"), Pod: podName,
Privileged: c.Bool("privileged"), Privileged: c.Bool("privileged"),
Publish: c.StringSlice("publish"), Publish: c.StringSlice("publish"),
PublishAll: c.Bool("publish-all"), PublishAll: c.Bool("publish-all"),

120
cmd/podman/exists.go Normal file
View 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
}

View File

@ -9,6 +9,7 @@ var (
buildCommand, buildCommand,
historyCommand, historyCommand,
importCommand, importCommand,
imageExistsCommand,
inspectCommand, inspectCommand,
loadCommand, loadCommand,
lsImagesCommand, lsImagesCommand,

View File

@ -6,8 +6,7 @@ import (
"sort" "sort"
"strings" "strings"
"time" "time"
"unicode"
"github.com/sirupsen/logrus"
"github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
@ -16,6 +15,7 @@ import (
"github.com/docker/go-units" "github.com/docker/go-units"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -289,6 +289,8 @@ func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, image
sizeStr = err.Error() sizeStr = err.Error()
} else { } else {
sizeStr = units.HumanSizeWithPrecision(float64(*size), 3) sizeStr = units.HumanSizeWithPrecision(float64(*size), 3)
lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber)
sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:]
} }
params := imagesTemplateParams{ params := imagesTemplateParams{
Repository: repo, Repository: repo,
@ -374,13 +376,13 @@ func CreateFilterFuncs(ctx context.Context, r *libpod.Runtime, c *cli.Context, i
case "before": case "before":
before, err := r.ImageRuntime().NewFromLocal(splitFilter[1]) before, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
if err != nil { 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())) filterFuncs = append(filterFuncs, image.CreatedBeforeFilter(before.Created()))
case "after": case "after":
after, err := r.ImageRuntime().NewFromLocal(splitFilter[1]) after, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
if err != nil { 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())) filterFuncs = append(filterFuncs, image.CreatedAfterFilter(after.Created()))
case "dangling": case "dangling":

View File

@ -139,7 +139,7 @@ func downloadFromURL(source string) (string, error) {
_, err = io.Copy(outFile, response.Body) _, err = io.Copy(outFile, response.Body)
if err != nil { 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 return outFile.Name(), nil

View File

@ -81,6 +81,7 @@ func debugInfo(c *cli.Context) map[string]interface{} {
info["compiler"] = runtime.Compiler info["compiler"] = runtime.Compiler
info["go version"] = runtime.Version() info["go version"] = runtime.Version()
info["podman version"] = c.App.Version info["podman version"] = c.App.Version
info["git commit"] = libpod.GitCommit version, _ := libpod.GetVersion()
info["git commit"] = version.GitCommit
return info return info
} }

View File

@ -119,7 +119,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
} }
libpodInspectData, err := ctr.Inspect(c.Bool("size")) libpodInspectData, err := ctr.Inspect(c.Bool("size"))
if err != nil { 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 break
} }
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData) data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
@ -154,12 +154,12 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
} else { } else {
libpodInspectData, err := ctr.Inspect(c.Bool("size")) libpodInspectData, err := ctr.Inspect(c.Bool("size"))
if err != nil { 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 break
} }
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData) data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
if err != nil { 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 break
} }
} }

View File

@ -1,15 +1,16 @@
package main package main
import ( import (
"os" "fmt"
"syscall" "syscall"
"fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/rootless"
"github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/signal"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -41,6 +42,11 @@ var (
// killCmd kills one or more containers with a signal // killCmd kills one or more containers with a signal
func killCmd(c *cli.Context) error { func killCmd(c *cli.Context) error {
var (
killFuncs []shared.ParallelWorkerInput
killSignal uint = uint(syscall.SIGTERM)
)
if err := checkAllAndLatest(c); err != nil { if err := checkAllAndLatest(c); err != nil {
return err return err
} }
@ -56,7 +62,6 @@ func killCmd(c *cli.Context) error {
} }
defer runtime.Shutdown(false) defer runtime.Shutdown(false)
var killSignal uint = uint(syscall.SIGTERM)
if c.String("signal") != "" { if c.String("signal") != "" {
// Check if the signalString provided by the user is valid // Check if the signalString provided by the user is valid
// Invalid signals will return err // Invalid signals will return err
@ -67,17 +72,32 @@ func killCmd(c *cli.Context) error {
killSignal = uint(sysSignal) 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 { for _, ctr := range containers {
if err := ctr.Kill(killSignal); err != nil { con := ctr
if lastError != nil { f := func() error {
fmt.Fprintln(os.Stderr, lastError) return con.Kill(killSignal)
}
lastError = errors.Wrapf(err, "unable to find container %v", ctr.ID())
} else {
fmt.Println(ctr.ID())
} }
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
View 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,
}
)

View 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
}

View File

@ -5,43 +5,33 @@ import (
"github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util" "github.com/containers/libpod/pkg/util"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// GetRuntime generates a new libpod runtime configured by command line options // GetRuntime generates a new libpod runtime configured by command line options
func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
storageOpts, err := util.GetDefaultStoreOptions() storageOpts := new(storage.StoreOptions)
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) {
options := []libpod.RuntimeOption{} 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") { if c.GlobalIsSet("root") {
storageOpts.GraphRoot = c.GlobalString("root") storageOpts.GraphRoot = c.GlobalString("root")
} }
if c.GlobalIsSet("runroot") { if c.GlobalIsSet("runroot") {
storageOpts.RunRoot = c.GlobalString("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") { if c.GlobalIsSet("storage-driver") {
storageOpts.GraphDriverName = c.GlobalString("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") { if c.GlobalIsSet("default-mounts-file") {
options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file"))) options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file")))
} }
if c.GlobalIsSet("hooks-dir-path") { if c.GlobalIsSet("hooks-dir") {
options = append(options, libpod.WithHooksDir(c.GlobalString("hooks-dir-path"))) options = append(options, libpod.WithHooksDir(c.GlobalStringSlice("hooks-dir")...))
} }
// TODO flag to set CNI plugins dir? // TODO flag to set CNI plugins dir?

View File

@ -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" + 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" "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{ logsCommand = cli.Command{
Name: "logs", Name: "logs",
Usage: "Fetch the logs of a container", Usage: "Fetch the logs of a container",
Description: logsDescription, Description: logsDescription,
Flags: sortFlags(logsFlags), Flags: sortFlags(logsFlags),
Action: logsCmd, Action: logsCmd,
ArgsUsage: "CONTAINER", ArgsUsage: "CONTAINER",
SkipArgReorder: true, SkipArgReorder: true,
OnUsageError: usageErrorHandler, OnUsageError: usageErrorHandler,
UseShortOptionHandling: true,
} }
) )

View File

@ -8,7 +8,6 @@ import (
"syscall" "syscall"
"github.com/containers/libpod/libpod" "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/hooks/0.1.0"
"github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/version" "github.com/containers/libpod/version"
@ -77,6 +76,7 @@ func main() {
infoCommand, infoCommand,
inspectCommand, inspectCommand,
killCommand, killCommand,
kubeCommand,
loadCommand, loadCommand,
loginCommand, loginCommand,
logoutCommand, logoutCommand,
@ -205,11 +205,9 @@ func main() {
Usage: "path to default mounts file", Usage: "path to default mounts file",
Hidden: true, Hidden: true,
}, },
cli.StringFlag{ cli.StringSliceFlag{
Name: "hooks-dir-path", Name: "hooks-dir",
Usage: "set the OCI hooks directory path", Usage: "set the OCI hooks directory path (may be set multiple times)",
Value: hooks.DefaultDir,
Hidden: true,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "max-workers", Name: "max-workers",

View File

@ -1,15 +1,23 @@
package main package main
import ( import (
"fmt"
"os" "os"
"github.com/containers/libpod/cmd/podman/libpodruntime" "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/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var ( var (
pauseFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "pause all running containers",
},
}
pauseDescription = ` pauseDescription = `
podman pause podman pause
@ -19,6 +27,7 @@ var (
Name: "pause", Name: "pause",
Usage: "Pauses all the processes in one or more containers", Usage: "Pauses all the processes in one or more containers",
Description: pauseDescription, Description: pauseDescription,
Flags: pauseFlags,
Action: pauseCmd, Action: pauseCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
OnUsageError: usageErrorHandler, OnUsageError: usageErrorHandler,
@ -26,6 +35,10 @@ var (
) )
func pauseCmd(c *cli.Context) error { func pauseCmd(c *cli.Context) error {
var (
pauseContainers []*libpod.Container
pauseFuncs []shared.ParallelWorkerInput
)
if os.Geteuid() != 0 { if os.Geteuid() != 0 {
return errors.New("pause is not supported for rootless containers") return errors.New("pause is not supported for rootless containers")
} }
@ -37,28 +50,44 @@ func pauseCmd(c *cli.Context) error {
defer runtime.Shutdown(false) defer runtime.Shutdown(false)
args := c.Args() 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") return errors.Errorf("you must provide at least one container name or id")
} }
if c.Bool("all") {
var lastError error containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
for _, arg := range args {
ctr, err := runtime.LookupContainer(arg)
if err != nil { if err != nil {
if lastError != nil { return err
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error looking up container %q", arg)
continue
} }
if err = ctr.Pause(); err != nil { pauseContainers = append(pauseContainers, containers...)
if lastError != nil { } else {
fmt.Fprintln(os.Stderr, lastError) 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()) pauseContainers = append(pauseContainers, ctr)
} else {
fmt.Println(ctr.ID())
} }
} }
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)
} }

View File

@ -11,6 +11,7 @@ Pods are a group of one or more containers sharing the same network, pid and ipc
` `
podSubCommands = []cli.Command{ podSubCommands = []cli.Command{
podCreateCommand, podCreateCommand,
podExistsCommand,
podInspectCommand, podInspectCommand,
podKillCommand, podKillCommand,
podPauseCommand, podPauseCommand,

View File

@ -3,11 +3,15 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod" "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/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -58,6 +62,10 @@ var podCreateFlags = []cli.Flag{
Name: "pod-id-file", Name: "pod-id-file",
Usage: "Write the pod ID to the 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{ cli.StringFlag{
Name: "share", Name: "share",
Usage: "A comma delimited list of kernel namespaces the pod will 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.Close()
defer podIdFile.Sync() 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") != "" { 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") 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...) 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 // always have containers use pod cgroups
// User Opt out is not yet supported // User Opt out is not yet supported
options = append(options, libpod.WithPodCgroups()) options = append(options, libpod.WithPodCgroups())
@ -152,3 +178,36 @@ func podCreateCmd(c *cli.Context) error {
return nil 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
}

View File

@ -184,7 +184,7 @@ var (
Usage: "Display the extended information", Usage: "Display the extended information",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "pod", Name: "pod, p",
Usage: "Print the ID and name of the pod the containers are associated with", Usage: "Print the ID and name of the pod the containers are associated with",
}, },
cli.BoolFlag{ cli.BoolFlag{

View File

@ -1,18 +1,24 @@
package main package main
import ( import (
"context"
"fmt"
"os"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var ( var (
restartFlags = []cli.Flag{ 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{ cli.UintFlag{
Name: "timeout, time, t", Name: "timeout, time, t",
Usage: "Seconds to wait for stop before killing the container", Usage: "Seconds to wait for stop before killing the container",
@ -35,11 +41,18 @@ var (
) )
func restartCmd(c *cli.Context) error { func restartCmd(c *cli.Context) error {
var (
restartFuncs []shared.ParallelWorkerInput
containers []*libpod.Container
restartContainers []*libpod.Container
)
args := c.Args() 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") return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID")
} }
if err := validateFlags(c, restartFlags); err != nil { if err := validateFlags(c, restartFlags); err != nil {
return err return err
} }
@ -50,8 +63,6 @@ func restartCmd(c *cli.Context) error {
} }
defer runtime.Shutdown(false) defer runtime.Shutdown(false)
var lastError error
timeout := c.Uint("timeout") timeout := c.Uint("timeout")
useTimeout := c.IsSet("timeout") useTimeout := c.IsSet("timeout")
@ -59,39 +70,57 @@ func restartCmd(c *cli.Context) error {
if c.Bool("latest") { if c.Bool("latest") {
lastCtr, err := runtime.GetLatestContainer() lastCtr, err := runtime.GetLatestContainer()
if err != nil { if err != nil {
lastError = errors.Wrapf(err, "unable to get latest container") return errors.Wrapf(err, "unable to get latest container")
} else { }
ctrTimeout := lastCtr.StopTimeout() restartContainers = append(restartContainers, lastCtr)
if useTimeout { } else if runOnly {
ctrTimeout = timeout 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
} }
restartContainers = append(restartContainers, ctr)
lastError = lastCtr.RestartWithTimeout(context.TODO(), ctrTimeout)
} }
} }
for _, id := range args { // We now have a slice of all the containers to be restarted. Iterate them to
ctr, err := runtime.LookupContainer(id) // create restart Funcs with a timeout as needed
if err != nil { for _, ctr := range restartContainers {
if lastError != nil { con := ctr
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "unable to find container %s", id)
continue
}
ctrTimeout := ctr.StopTimeout() ctrTimeout := ctr.StopTimeout()
if useTimeout { if useTimeout {
ctrTimeout = timeout ctrTimeout = timeout
} }
if err := ctr.RestartWithTimeout(context.TODO(), ctrTimeout); err != nil { f := func() error {
if lastError != nil { return con.RestartWithTimeout(getContext(), ctrTimeout)
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error restarting container %s", ctr.ID())
} }
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)
} }

View File

@ -26,6 +26,10 @@ var (
// restore --all would make more sense if there would be // restore --all would make more sense if there would be
// dedicated state for container which are checkpointed. // dedicated state for container which are checkpointed.
// TODO: add ContainerStateCheckpointed // TODO: add ContainerStateCheckpointed
cli.BoolFlag{
Name: "tcp-established",
Usage: "checkpoint a container with established TCP connections",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "all, a", Name: "all, a",
Usage: "restore all checkpointed containers", Usage: "restore all checkpointed containers",
@ -53,16 +57,19 @@ func restoreCmd(c *cli.Context) error {
} }
defer runtime.Shutdown(false) 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 { if err := checkAllAndLatest(c); err != nil {
return err return err
} }
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "checkpointed") containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateExited, "checkpointed")
for _, ctr := range containers { 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 { if lastError != nil {
fmt.Fprintln(os.Stderr, lastError) fmt.Fprintln(os.Stderr, lastError)
} }

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "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 // saveCmd saves the image to either docker-archive or oci
func rmCmd(c *cli.Context) error { func rmCmd(c *cli.Context) error {
var ( var (
delContainers []*libpod.Container deleteFuncs []shared.ParallelWorkerInput
lastError error
deleteFuncs []shared.ParallelWorkerInput
) )
ctx := getContext() ctx := getContext()
@ -65,7 +62,13 @@ func rmCmd(c *cli.Context) error {
return err 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 { for _, container := range delContainers {
con := container con := container
@ -84,14 +87,7 @@ func rmCmd(c *cli.Context) error {
} }
logrus.Debugf("Setting maximum workers to %d", maxWorkers) logrus.Debugf("Setting maximum workers to %d", maxWorkers)
deleteErrors := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs) // Run the parallel funcs
for cid, result := range deleteErrors { deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
if result != nil { return printParallelOutput(deleteErrors, errCount)
fmt.Println(result.Error())
lastError = result
continue
}
fmt.Println(cid)
}
return lastError
} }

View File

@ -91,8 +91,23 @@ func rmiCmd(c *cli.Context) error {
if err != nil { if err != nil {
return errors.Wrapf(err, "unable to query local images") return errors.Wrapf(err, "unable to query local images")
} }
for _, i := range imagesToDelete { lastNumberofImages := 0
removeImage(i) 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 { } else {
// Create image.image objects for deletion from user input. // Create image.image objects for deletion from user input.

View File

@ -44,7 +44,7 @@ func runCmd(c *cli.Context) error {
rootless.SetSkipStorageSetup(true) rootless.SetSkipStorageSetup(true)
} }
runtime, err := libpodruntime.GetContainerRuntime(c) runtime, err := libpodruntime.GetRuntime(c)
if err != nil { if err != nil {
return errors.Wrapf(err, "error creating libpod runtime") return errors.Wrapf(err, "error creating libpod runtime")
} }
@ -96,8 +96,6 @@ func runCmd(c *cli.Context) error {
inputStream = nil inputStream = nil
} }
inputStream = nil
attachTo := c.StringSlice("attach") attachTo := c.StringSlice("attach")
for _, stream := range attachTo { for _, stream := range attachTo {
switch strings.ToLower(stream) { switch strings.ToLower(stream) {

View File

@ -6,11 +6,9 @@ import (
"os" "os"
"strings" "strings"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils" "github.com/containers/libpod/utils"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -94,7 +92,7 @@ func runlabelCmd(c *cli.Context) error {
imageName string imageName string
stdErr, stdOut io.Writer stdErr, stdOut io.Writer
stdIn io.Reader stdIn io.Reader
newImage *image.Image extraArgs []string
) )
// Evil images could trick into recursively executing the runlabel // 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.") return errors.Errorf("the display and quiet flags cannot be used together.")
} }
if len(args) > 2 {
extraArgs = args[2:]
}
pull := c.Bool("pull") pull := c.Bool("pull")
label := args[0] label := args[0]
@ -151,75 +152,24 @@ func runlabelCmd(c *cli.Context) error {
stdIn = nil stdIn = nil
} }
if pull { dockerRegistryOptions := image.DockerRegistryOptions{
var registryCreds *types.DockerAuthConfig DockerCertPath: c.String("cert-dir"),
if c.IsSet("creds") { DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
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")
} }
if len(newImage.Names()) < 1 { authfile := getAuthFile(c.String("authfile"))
imageName = newImage.ID() runLabel, imageName, err := shared.GetRunlabel(label, runlabelImage, ctx, runtime, pull, c.String("creds"), dockerRegistryOptions, authfile, c.String("signature-policy"), stdOut)
} else {
imageName = newImage.Names()[0]
}
runLabel, err := newImage.GetLabel(ctx, label)
if err != nil { if err != nil {
return err return err
} }
// If no label to execute, we return
if runLabel == "" { if runLabel == "" {
return nil return nil
} }
// The user provided extra arguments that need to be tacked onto the label's command cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, c.String("name"), opts, extraArgs)
if len(args) > 2 {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " "))
}
cmd, err := shared.GenerateCommand(runLabel, imageName, c.String("name"))
if err != nil { 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") { if !c.Bool("quiet") {
fmt.Printf("Command: %s\n", strings.Join(cmd, " ")) fmt.Printf("Command: %s\n", strings.Join(cmd, " "))
if c.Bool("display") { 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:]...) 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
}

View File

@ -1,10 +1,15 @@
package shared package shared
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "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/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units" "github.com/docker/go-units"
"io"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -589,3 +594,79 @@ func portsToString(ports []ocicni.PortMapping) string {
} }
return strings.Join(portDisplay, ", ") 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
}

View File

@ -5,6 +5,8 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/google/shlex"
) )
func substituteCommand(cmd string) (string, error) { func substituteCommand(cmd string) (string, error) {
@ -42,7 +44,11 @@ func GenerateCommand(command, imageName, name string) ([]string, error) {
if name == "" { if name == "" {
name = imageName name = imageName
} }
cmd := strings.Split(command, " ")
cmd, err := shlex.Split(command)
if err != nil {
return nil, err
}
prog, err := substituteCommand(cmd[0]) prog, err := substituteCommand(cmd[0])
if err != nil { if err != nil {

View File

@ -18,10 +18,11 @@ var (
) )
func TestGenerateCommand(t *testing.T) { func TestGenerateCommand(t *testing.T) {
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE 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 install" correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
newCommand, err := GenerateCommand(inputCommand, "foo", "bar") newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "hello world", newCommand[11])
assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
} }
@ -108,8 +109,8 @@ func TestGenerateCommandNoSetName(t *testing.T) {
} }
func TestGenerateCommandNoName(t *testing.T) { func TestGenerateCommandNoName(t *testing.T) {
inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install" inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install" correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
newCommand, err := GenerateCommand(inputCommand, "foo", "") newCommand, err := GenerateCommand(inputCommand, "foo", "")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) assert.Equal(t, correctCommand, strings.Join(newCommand, " "))

View File

@ -30,9 +30,10 @@ func ParallelWorker(wg *sync.WaitGroup, jobs <-chan ParallelWorkerInput, results
// ParallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker // ParallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker
// int determines how many workers/threads should be premade. // 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 ( var (
wg sync.WaitGroup wg sync.WaitGroup
errorCount int
) )
resultChan := make(chan containerError, len(functions)) resultChan := make(chan containerError, len(functions))
@ -62,9 +63,12 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
close(resultChan) close(resultChan)
for ctrError := range resultChan { for ctrError := range resultChan {
results[ctrError.ContainerID] = ctrError.Err 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 // 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 { func Parallelize(job string) int {
numCpus := runtime.NumCPU() numCpus := runtime.NumCPU()
switch job { switch job {
case "stop": case "kill":
if numCpus <= 2 { if numCpus <= 3 {
return 4
} else {
return 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": case "rm":
if numCpus <= 3 { if numCpus <= 3 {
return numCpus * 3 return numCpus * 3
} else { } else {
return numCpus * 4 return numCpus * 4
} }
case "ps": case "stop":
return 8 if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
case "unpause":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
} }
return 3 return 3
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
@ -59,7 +60,13 @@ func stopCmd(c *cli.Context) error {
} }
defer runtime.Shutdown(false) 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 var stopFuncs []shared.ParallelWorkerInput
for _, ctr := range containers { for _, ctr := range containers {
@ -71,7 +78,11 @@ func stopCmd(c *cli.Context) error {
stopTimeout = ctr.StopTimeout() stopTimeout = ctr.StopTimeout()
} }
f := func() error { 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{ stopFuncs = append(stopFuncs, shared.ParallelWorkerInput{
ContainerID: con.ID(), ContainerID: con.ID(),
@ -85,15 +96,6 @@ func stopCmd(c *cli.Context) error {
} }
logrus.Debugf("Setting maximum workers to %d", maxWorkers) logrus.Debugf("Setting maximum workers to %d", maxWorkers)
stopErrors := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs) stopErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs)
return printParallelOutput(stopErrors, errCount)
for cid, result := range stopErrors {
if result != nil && result != libpod.ErrCtrStopped {
fmt.Println(result.Error())
lastError = result
continue
}
fmt.Println(cid)
}
return lastError
} }

View File

@ -1,15 +1,23 @@
package main package main
import ( import (
"fmt"
"os" "os"
"github.com/containers/libpod/cmd/podman/libpodruntime" "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/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var ( var (
unpauseFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "unpause all paused containers",
},
}
unpauseDescription = ` unpauseDescription = `
podman unpause podman unpause
@ -19,6 +27,7 @@ var (
Name: "unpause", Name: "unpause",
Usage: "Unpause the processes in one or more containers", Usage: "Unpause the processes in one or more containers",
Description: unpauseDescription, Description: unpauseDescription,
Flags: unpauseFlags,
Action: unpauseCmd, Action: unpauseCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
OnUsageError: usageErrorHandler, OnUsageError: usageErrorHandler,
@ -26,6 +35,10 @@ var (
) )
func unpauseCmd(c *cli.Context) error { func unpauseCmd(c *cli.Context) error {
var (
unpauseContainers []*libpod.Container
unpauseFuncs []shared.ParallelWorkerInput
)
if os.Geteuid() != 0 { if os.Geteuid() != 0 {
return errors.New("unpause is not supported for rootless containers") return errors.New("unpause is not supported for rootless containers")
} }
@ -37,28 +50,44 @@ func unpauseCmd(c *cli.Context) error {
defer runtime.Shutdown(false) defer runtime.Shutdown(false)
args := c.Args() 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") return errors.Errorf("you must provide at least one container name or id")
} }
if c.Bool("all") {
var lastError error cs, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStatePaused, "paused")
for _, arg := range args {
ctr, err := runtime.LookupContainer(arg)
if err != nil { if err != nil {
if lastError != nil { return err
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error looking up container %q", arg)
continue
} }
if err = ctr.Unpause(); err != nil { unpauseContainers = append(unpauseContainers, cs...)
if lastError != nil { } else {
fmt.Fprintln(os.Stderr, lastError) 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()) unpauseContainers = append(unpauseContainers, ctr)
} else {
fmt.Println(ctr.ID())
} }
} }
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)
} }

View File

@ -207,3 +207,20 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error
} }
return pods, lastError 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
}

View File

@ -371,6 +371,22 @@ type PodContainerErrorData (
reason: string 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. # Ping provides a response for developers to ensure their varlink setup is working.
# #### Example # #### Example
# ~~~ # ~~~
@ -804,6 +820,42 @@ method TopPod() -> (notimplemented: NotImplemented)
# ~~~ # ~~~
method GetPodStats(name: string) -> (pod: string, containers: []ContainerStats) 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. # ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (name: string) error ImageNotFound (name: string)

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -15,13 +16,26 @@ func versionCmd(c *cli.Context) error {
if err != nil { if err != nil {
errors.Wrapf(err, "unable to determine version") 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("Version: ", output.Version)
fmt.Println("Go Version: ", output.GoVersion) fmt.Println("Go Version: ", output.GoVersion)
if output.GitCommit != "" { if output.GitCommit != "" {
fmt.Println("Git Commit: ", output.GitCommit) fmt.Println("Git Commit: ", output.GitCommit)
} }
// Prints out the build time in readable format // 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)) 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 // Cli command to print out the full version of podman
var versionCommand = cli.Command{ var (
Name: "version", versionCommand = cli.Command{
Usage: "Display the PODMAN Version Information", Name: "version",
Action: versionCmd, 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
View File

@ -0,0 +1,82 @@
![PODMAN logo](../../logo/podman-logo-source.svg)
# 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.

View File

@ -22,10 +22,6 @@ SCRIPT_BASE $SCRIPT_BASE
PACKER_BASE $PACKER_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 show_env_vars
# Everything here is running on the 'image-builder-image' GCE image # Everything here is running on the 'image-builder-image' GCE image

View File

@ -106,7 +106,10 @@ ircmsg() {
SCRIPT="$GOSRC/$SCRIPT_BASE/podbot.py" SCRIPT="$GOSRC/$SCRIPT_BASE/podbot.py"
NICK="podbot_$CIRRUS_TASK_ID" NICK="podbot_$CIRRUS_TASK_ID"
NICK="${NICK:0:15}" # Any longer will break things NICK="${NICK:0:15}" # Any longer will break things
set +e
$SCRIPT $NICK $1 $SCRIPT $NICK $1
echo "Ignoring exit($?)"
set -e
} }
# Run sudo in directory with GOPATH set # Run sudo in directory with GOPATH set
@ -117,7 +120,6 @@ cdsudo() {
sudo --preserve-env=GOPATH --non-interactive bash -c "$CMD" sudo --preserve-env=GOPATH --non-interactive bash -c "$CMD"
} }
# Helper/wrapper script to only show stderr/stdout on non-zero exit # Helper/wrapper script to only show stderr/stdout on non-zero exit
install_ooe() { install_ooe() {
req_env_var "SCRIPT_BASE $SCRIPT_BASE" req_env_var "SCRIPT_BASE $SCRIPT_BASE"
@ -142,8 +144,8 @@ EOF
install_cni_plugins() { install_cni_plugins() {
echo "Installing CNI Plugins from commit $CNI_COMMIT" echo "Installing CNI Plugins from commit $CNI_COMMIT"
req_env_var " req_env_var "
GOPATH $GOPATH GOPATH $GOPATH
CNI_COMMIT $CNI_COMMIT CNI_COMMIT $CNI_COMMIT
" "
DEST="$GOPATH/src/github.com/containernetworking/plugins" DEST="$GOPATH/src/github.com/containernetworking/plugins"
rm -rf "$DEST" rm -rf "$DEST"
@ -155,14 +157,27 @@ install_cni_plugins() {
sudo cp bin/* /usr/libexec/cni 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(){ install_runc(){
OS_RELEASE_ID=$(os_release_id) OS_RELEASE_ID=$(os_release_id)
echo "Installing RunC from commit $RUNC_COMMIT" echo "Installing RunC from commit $RUNC_COMMIT"
echo "Platform is $OS_RELEASE_ID" echo "Platform is $OS_RELEASE_ID"
req_env_var " req_env_var "
GOPATH $GOPATH GOPATH $GOPATH
RUNC_COMMIT $RUNC_COMMIT RUNC_COMMIT $RUNC_COMMIT
OS_RELEASE_ID $OS_RELEASE_ID OS_RELEASE_ID $OS_RELEASE_ID
" "
if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]; then if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]]; then
echo "Running make install.libseccomp.sudo for ubuntu" echo "Running make install.libseccomp.sudo for ubuntu"
@ -177,14 +192,7 @@ install_runc(){
cd "$GOPATH/src/github.com/containers/libpod" cd "$GOPATH/src/github.com/containers/libpod"
ooe.sh sudo make install.libseccomp.sudo ooe.sh sudo make install.libseccomp.sudo
fi fi
DEST="$GOPATH/src/github.com/opencontainers/runc" install_runc_from_git
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_buildah() { install_buildah() {
@ -202,8 +210,8 @@ install_buildah() {
install_conmon(){ install_conmon(){
echo "Installing conmon from commit $CRIO_COMMIT" echo "Installing conmon from commit $CRIO_COMMIT"
req_env_var " req_env_var "
GOPATH $GOPATH GOPATH $GOPATH
CRIO_COMMIT $CRIO_COMMIT CRIO_COMMIT $CRIO_COMMIT
" "
DEST="$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" DEST="$GOPATH/src/github.com/kubernetes-sigs/cri-o.git"
rm -rf "$DEST" rm -rf "$DEST"
@ -234,8 +242,8 @@ install_criu(){
install_testing_dependencies() { install_testing_dependencies() {
echo "Installing ginkgo, gomega, and easyjson into \$GOPATH=$GOPATH" echo "Installing ginkgo, gomega, and easyjson into \$GOPATH=$GOPATH"
req_env_var " req_env_var "
GOPATH $GOPATH GOPATH $GOPATH
GOSRC $GOSRC GOSRC $GOSRC
" "
cd "$GOSRC" cd "$GOSRC"
ooe.sh go get -u github.com/onsi/ginkgo/ginkgo ooe.sh go get -u github.com/onsi/ginkgo/ginkgo
@ -263,7 +271,7 @@ install_varlink(){
_finalize(){ _finalize(){
echo "Removing leftover giblets from cloud-init" echo "Removing leftover giblets from cloud-init"
cd / cd /
sudo rm -rf /var/lib/cloud sudo rm -rf /var/lib/cloud/instance?
sudo rm -rf /root/.ssh/* sudo rm -rf /root/.ssh/*
sudo rm -rf /home/* sudo rm -rf /home/*
} }

View File

@ -21,8 +21,7 @@ install_ooe
export GOPATH="$(mktemp -d)" export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT 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 \ ooe.sh sudo dnf install -y \
atomic-registries \ atomic-registries \

View File

@ -21,10 +21,13 @@ install_ooe
export GOPATH="$(mktemp -d)" export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT trap "sudo rm -rf $GOPATH" EXIT
ooe.sh sudo apt-get -qq update export DEBIAN_FRONTEND=noninteractive
ooe.sh sudo apt-get -qq update # sometimes it needs to get it twice :S
ooe.sh sudo apt-get -qq upgrade # Try twice as workaround for minor networking problems
ooe.sh sudo apt-get -qq install --no-install-recommends \ 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 \ apparmor \
autoconf \ autoconf \
automake \ automake \

View File

@ -53,6 +53,8 @@ then
# Some setup needs to vary between distros # Some setup needs to vary between distros
case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
ubuntu-18) 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"' 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 fedora-28) ;& # Continue to the next item

33
contrib/cirrus/system_test.sh Executable file
View 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
View 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
View File

@ -0,0 +1,4 @@
![PODMAN logo](../../logo/podman-logo-source.svg)
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
View 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 "$@"

View File

@ -4,6 +4,7 @@ PODMAN_VERSION ?= '0.0.4'
.PHONY: python-podman .PHONY: python-podman
python-podman: python-podman:
PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py sdist bdist $(PYTHON) setup.py sdist bdist
.PHONY: lint .PHONY: lint
@ -16,6 +17,7 @@ integration:
.PHONY: install .PHONY: install
install: install:
PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py install --root ${DESTDIR} $(PYTHON) setup.py install --root ${DESTDIR}
.PHONY: upload .PHONY: upload

View File

@ -19,9 +19,13 @@ class Mixin:
""" """
if stdin is None: if stdin is None:
stdin = sys.stdin.fileno() stdin = sys.stdin.fileno()
elif hasattr(stdin, 'fileno'):
stdin = stdin.fileno()
if stdout is None: if stdout is None:
stdout = sys.stdout.fileno() stdout = sys.stdout.fileno()
elif hasattr(stdout, 'fileno'):
stdout = stdout.fileno()
with self._client() as podman: with self._client() as podman:
attach = podman.GetAttachSockets(self._id) attach = podman.GetAttachSockets(self._id)
@ -49,7 +53,7 @@ class Mixin:
def resize_handler(self): def resize_handler(self):
"""Send the new window size to conmon.""" """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, packed = fcntl.ioctl(self.pseudo_tty.stdout, termios.TIOCGWINSZ,
struct.pack('HHHH', 0, 0, 0, 0)) struct.pack('HHHH', 0, 0, 0, 0))
rows, cols, _, _ = struct.unpack('HHHH', packed) rows, cols, _, _ = struct.unpack('HHHH', packed)
@ -67,7 +71,7 @@ class Mixin:
def log_handler(self): def log_handler(self):
"""Send command to reopen log to conmon.""" """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: with open(self.pseudo_tty.control_socket, 'w') as skt:
# send conmon reopen log message # send conmon reopen log message
skt.write('2\n') skt.write('2\n')

View File

@ -1,12 +1,12 @@
"""Models for manipulating containers and storage.""" """Models for manipulating containers and storage."""
import collections import collections
import functools
import getpass import getpass
import json import json
import logging import logging
import signal import signal
import time import time
from . import fold_keys
from ._containers_attach import Mixin as AttachMixin from ._containers_attach import Mixin as AttachMixin
from ._containers_start import Mixin as StartMixin from ._containers_start import Mixin as StartMixin
@ -14,25 +14,27 @@ from ._containers_start import Mixin as StartMixin
class Container(AttachMixin, StartMixin, collections.UserDict): class Container(AttachMixin, StartMixin, collections.UserDict):
"""Model for a container.""" """Model for a container."""
def __init__(self, client, id, data): def __init__(self, client, ident, data, refresh=True):
"""Construct Container Model.""" """Construct Container Model."""
super(Container, self).__init__(data) super(Container, self).__init__(data)
self._client = client self._client = client
self._id = id self._id = ident
with client() as podman: if refresh:
self._refresh(podman) 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'],\ assert self._id == data['id'],\
'Requested container id({}) does not match store id({})'.format( 'Requested container id({}) does not match store id({})'.format(
self._id, data['id'] self._id, data['id']
) )
def __getitem__(self, key):
"""Get items from parent dict."""
return super().__getitem__(key)
def _refresh(self, podman, tries=1): def _refresh(self, podman, tries=1):
try: try:
ctnr = podman.GetContainer(self._id) ctnr = podman.GetContainer(self._id)
@ -71,18 +73,18 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
results = podman.ListContainerChanges(self._id) results = podman.ListContainerChanges(self._id)
return results['container'] return results['container']
def kill(self, signal=signal.SIGTERM, wait=25): def kill(self, sig=signal.SIGTERM, wait=25):
"""Send signal to container. """Send signal to container.
default signal is signal.SIGTERM. default signal is signal.SIGTERM.
wait n of seconds, 0 waits forever. wait n of seconds, 0 waits forever.
""" """
with self._client() as podman: with self._client() as podman:
podman.KillContainer(self._id, signal) podman.KillContainer(self._id, sig)
timeout = time.time() + wait timeout = time.time() + wait
while True: while True:
self._refresh(podman) self._refresh(podman)
if self.status != 'running': if self.status != 'running': # pylint: disable=no-member
return self return self
if wait and timeout < time.time(): if wait and timeout < time.time():
@ -90,20 +92,11 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
time.sleep(0.5) 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): def inspect(self):
"""Retrieve details about containers.""" """Retrieve details about containers."""
with self._client() as podman: with self._client() as podman:
results = podman.InspectContainer(self._id) 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) return collections.namedtuple('ContainerInspect', obj.keys())(**obj)
def export(self, target): def export(self, target):
@ -115,19 +108,16 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
results = podman.ExportContainer(self._id, target) results = podman.ExportContainer(self._id, target)
return results['tarfile'] return results['tarfile']
def commit(self, def commit(self, image_name, **kwargs):
image_name,
*args,
changes=[],
message='',
pause=True,
**kwargs):
"""Create image from container. """Create image from container.
All changes overwrite existing values. Keyword arguments:
See inspect() to obtain current settings. 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 CMD=/usr/bin/zsh
ENTRYPOINT=/bin/sh date ENTRYPOINT=/bin/sh date
ENV=TEST=test_containers.TestContainers.test_commit ENV=TEST=test_containers.TestContainers.test_commit
@ -136,21 +126,23 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
USER=bozo:circus USER=bozo:circus
VOLUME=/data VOLUME=/data
WORKDIR=/data/application 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: if c.startswith('LABEL=') and c.count('=') < 2:
raise ValueError( raise ValueError(
'LABEL should have the format: LABEL=label=value, not {}'. 'LABEL should have the format: LABEL=label=value, not {}'.
format(c)) format(c))
with self._client() as podman: 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) message, pause)
return results['image'] return results['image']
@ -175,7 +167,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
podman.RestartContainer(self._id, timeout) podman.RestartContainer(self._id, timeout)
return self._refresh(podman) return self._refresh(podman)
def rename(self, target): def rename(self, target): # pylint: disable=unused-argument
"""Rename container, return id on success.""" """Rename container, return id on success."""
with self._client() as podman: with self._client() as podman:
# TODO: Need arguments # TODO: Need arguments
@ -183,7 +175,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
# TODO: fixup objects cached information # TODO: fixup objects cached information
return results['container'] return results['container']
def resize_tty(self, width, height): def resize_tty(self, width, height): # pylint: disable=unused-argument
"""Resize container tty.""" """Resize container tty."""
with self._client() as podman: with self._client() as podman:
# TODO: magic re: attach(), arguments # TODO: magic re: attach(), arguments
@ -201,7 +193,8 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
podman.UnpauseContainer(self._id) podman.UnpauseContainer(self._id)
return self._refresh(podman) 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.""" """TODO: Update container..., return id on success."""
with self._client() as podman: with self._client() as podman:
podman.UpdateContainer() podman.UpdateContainer()
@ -220,7 +213,7 @@ class Container(AttachMixin, StartMixin, collections.UserDict):
obj = results['container'] obj = results['container']
return collections.namedtuple('StatDetail', obj.keys())(**obj) return collections.namedtuple('StatDetail', obj.keys())(**obj)
def logs(self, *args, **kwargs): def logs(self, *args, **kwargs): # pylint: disable=unused-argument
"""Retrieve container logs.""" """Retrieve container logs."""
with self._client() as podman: with self._client() as podman:
results = podman.GetContainerLogs(self._id) results = podman.GetContainerLogs(self._id)
@ -239,7 +232,7 @@ class Containers():
with self._client() as podman: with self._client() as podman:
results = podman.ListContainers() results = podman.ListContainers()
for cntr in results['containers']: 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): def delete_stopped(self):
"""Delete all stopped containers.""" """Delete all stopped containers."""

View File

@ -27,9 +27,10 @@ class Image(collections.UserDict):
@staticmethod @staticmethod
def _split_token(values=None, sep='='): def _split_token(values=None, sep='='):
if not values:
return {}
return { return {
k: v1 k: v1 for k, v1 in (v0.split(sep, 1) for v0 in values)
for k, v1 in (v0.split(sep, 1) for v0 in values if values)
} }
def create(self, *args, **kwargs): def create(self, *args, **kwargs):
@ -74,7 +75,7 @@ class Image(collections.UserDict):
obj = json.loads(results['image'], object_hook=fold_keys()) obj = json.loads(results['image'], object_hook=fold_keys())
return collections.namedtuple('ImageInspect', obj.keys())(**obj) 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.""" """Copy image to target, return id on success."""
with self._client() as podman: with self._client() as podman:
results = podman.PushImage(self._id, target, tlsverify) results = podman.PushImage(self._id, target, tlsverify)
@ -137,7 +138,7 @@ class Images():
results = podman.DeleteUnusedImages() results = podman.DeleteUnusedImages()
return results['images'] 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.""" """Read image tarball from source and save in image store."""
with self._client() as podman: with self._client() as podman:
results = podman.ImportImage(source, reference, message, changes) results = podman.ImportImage(source, reference, message, changes)

View File

@ -152,7 +152,7 @@ class TestContainers(PodmanTestCase):
changes.append('WORKDIR=/data/application') changes.append('WORKDIR=/data/application')
id = self.alpine_ctnr.commit( 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) img = self.pclient.images.get(id)
self.assertIsNotNone(img) self.assertIsNotNone(img)

View File

@ -102,7 +102,7 @@ class TestImages(PodmanTestCase):
def test_push(self): def test_push(self):
path = '{}/alpine_push'.format(self.tmpdir) path = '{}/alpine_push'.format(self.tmpdir)
target = 'dir:{}'.format(path) 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, 'manifest.json')))
self.assertTrue(os.path.isfile(os.path.join(path, 'version'))) self.assertTrue(os.path.isfile(os.path.join(path, 'version')))

View File

@ -52,7 +52,8 @@ class TestPodsCtnrs(PodmanTestCase):
status = FoldedString(pod.containersinfo[0]['status']) status = FoldedString(pod.containersinfo[0]['status'])
self.assertIn(status, ('stopped', 'exited', 'running')) 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) self.assertEqual(pod, killed)
def test_999_remove(self): def test_999_remove(self):

View File

@ -4,6 +4,7 @@ PODMAN_VERSION ?= '0.0.4'
.PHONY: python-pypodman .PHONY: python-pypodman
python-pypodman: python-pypodman:
PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py sdist bdist $(PYTHON) setup.py sdist bdist
.PHONY: lint .PHONY: lint
@ -16,6 +17,7 @@ integration:
.PHONY: install .PHONY: install
install: install:
PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py install --root ${DESTDIR} $(PYTHON) setup.py install --root ${DESTDIR}
.PHONY: upload .PHONY: upload

View File

@ -85,7 +85,7 @@ overwriting earlier. Any missing items are ignored.
.IP \[bu] 2 .IP \[bu] 2
From \f[C]\-\-config\-home\f[] command line option + \f[C]pypodman/pypodman.conf\f[] From \f[C]\-\-config\-home\f[] command line option + \f[C]pypodman/pypodman.conf\f[]
.IP \[bu] 2 .IP \[bu] 2
From environment variable, for example: RUN_DIR From environment variable prefixed with PODMAN_, for example: PODMAN_RUN_DIR
.IP \[bu] 2 .IP \[bu] 2
From command line option, for example: \[en]run\-dir From command line option, for example: \[en]run\-dir
.PP .PP

View File

@ -4,14 +4,15 @@ import sys
import podman import podman
from pypodman.lib.action_base import AbstractActionBase from pypodman.lib.action_base import AbstractActionBase
from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate, from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate,
PathAction, PositiveIntAction, ChangeAction, PathAction,
UnitAction) PositiveIntAction, UnitAction)
from pypodman.lib.podman_parser import PodmanArgumentParser from pypodman.lib.podman_parser import PodmanArgumentParser
from pypodman.lib.report import Report, ReportColumn from pypodman.lib.report import Report, ReportColumn
# Silence pylint overlording... # Silence pylint overlording...
assert BooleanAction assert BooleanAction
assert BooleanValidate assert BooleanValidate
assert ChangeAction
assert PathAction assert PathAction
assert PositiveIntAction assert PositiveIntAction
assert UnitAction assert UnitAction

View File

@ -22,6 +22,8 @@ from pypodman.lib.actions.rm_action import Rm
from pypodman.lib.actions.rmi_action import Rmi from pypodman.lib.actions.rmi_action import Rmi
from pypodman.lib.actions.run_action import Run from pypodman.lib.actions.run_action import Run
from pypodman.lib.actions.search_action import Search 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__ = [ __all__ = [
'Attach', 'Attach',
@ -47,4 +49,6 @@ __all__ = [
'Rmi', 'Rmi',
'Run', 'Run',
'Search', 'Search',
'Start',
'Version',
] ]

View File

@ -2,7 +2,7 @@
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase, BooleanAction from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction
class Commit(AbstractActionBase): class Commit(AbstractActionBase):
@ -12,7 +12,9 @@ class Commit(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Commit command to parent parser.""" """Add Commit command to parent parser."""
parser = parent.add_parser( parser = parent.add_parser(
'commit', help='create image from container') 'commit',
help='create image from container',
)
parser.add_argument( parser.add_argument(
'--author', '--author',
help='Set the author for the committed image', help='Set the author for the committed image',
@ -20,11 +22,7 @@ class Commit(AbstractActionBase):
parser.add_argument( parser.add_argument(
'--change', '--change',
'-c', '-c',
choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD', action=ChangeAction,
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
action='append',
type=str.upper,
help='Apply the following possible changes to the created image',
) )
parser.add_argument( parser.add_argument(
'--format', '--format',
@ -32,7 +30,8 @@ class Commit(AbstractActionBase):
choices=('oci', 'docker'), choices=('oci', 'docker'),
default='oci', default='oci',
type=str.lower, 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( parser.add_argument(
'--iidfile', '--iidfile',
@ -42,7 +41,8 @@ class Commit(AbstractActionBase):
parser.add_argument( parser.add_argument(
'--message', '--message',
'-m', '-m',
help='Set commit message for committed image', help='Set commit message for committed image'
' (Only on docker images.)',
) )
parser.add_argument( parser.add_argument(
'--pause', '--pause',
@ -69,27 +69,11 @@ class Commit(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='commit') 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): def commit(self):
"""Create image from container.""" """Create image from container."""
try: try:
try: try:
ctnr = self.client.containers.get(self._args.container[0]) ctnr = self.client.containers.get(self._args.container[0])
ident = ctnr.commit(**self.opts)
print(ident)
except podman.ContainerNotFound as e: except podman.ContainerNotFound as e:
sys.stdout.flush() sys.stdout.flush()
print( print(
@ -97,6 +81,17 @@ class Commit(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 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: except podman.ErrorOccurred as e:
sys.stdout.flush() sys.stdout.flush()
print( print(
@ -104,3 +99,4 @@ class Commit(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 return 1
return 0

View File

@ -21,7 +21,7 @@ class Create(AbstractActionBase):
parser.add_argument('image', nargs=1, help='source image id') parser.add_argument('image', nargs=1, help='source image id')
parser.add_argument( parser.add_argument(
'command', 'command',
nargs='*', nargs=parent.REMAINDER,
help='command and args to run.', help='command and args to run.',
) )
parser.set_defaults(class_=cls, method='create') parser.set_defaults(class_=cls, method='create')

View File

@ -12,13 +12,16 @@ class Export(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Export command to parent parser.""" """Add Export command to parent parser."""
parser = parent.add_parser( parser = parent.add_parser(
'export', help='export container to tarball') 'export',
help='export container to tarball',
)
parser.add_argument( parser.add_argument(
'--output', '--output',
'-o', '-o',
metavar='PATH', metavar='PATH',
nargs=1, nargs=1,
help='Write to a file', required=True,
help='Write to this file on host',
) )
parser.add_argument( parser.add_argument(
'container', 'container',
@ -27,23 +30,11 @@ class Export(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='export') 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): def export(self):
"""Create tarball from container filesystem.""" """Create tarball from container filesystem."""
try: try:
try: try:
ctnr = self.client.containers.get(self._args.container[0]) ctnr = self.client.containers.get(self._args.container[0])
ctnr.export(self._args.output[0])
except podman.ContainerNotFound as e: except podman.ContainerNotFound as e:
sys.stdout.flush() sys.stdout.flush()
print( print(
@ -51,6 +42,8 @@ class Export(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 return 1
else:
ctnr.export(self._args.output[0])
except podman.ErrorOccurred as e: except podman.ErrorOccurred as e:
sys.stdout.flush() sys.stdout.flush()
print( print(
@ -58,3 +51,4 @@ class Export(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 return 1
return 0

View File

@ -60,7 +60,7 @@ class History(AbstractActionBase):
if self._args.human: if self._args.human:
fields.update({ fields.update({
'size': 'size':
humanize.naturalsize(details.size, binary=True), humanize.naturalsize(details.size),
'created': 'created':
humanize.naturaldate( humanize.naturaldate(
podman.datetime_parse(details.created)), podman.datetime_parse(details.created)),

View File

@ -37,7 +37,7 @@ class Images(AbstractActionBase):
self.columns = OrderedDict({ self.columns = OrderedDict({
'name': 'name':
ReportColumn('name', 'REPOSITORY', 40), ReportColumn('name', 'REPOSITORY', 0),
'tag': 'tag':
ReportColumn('tag', 'TAG', 10), ReportColumn('tag', 'TAG', 10),
'id': 'id':
@ -65,18 +65,18 @@ class Images(AbstractActionBase):
'created': 'created':
humanize.naturaldate(podman.datetime_parse(image.created)), humanize.naturaldate(podman.datetime_parse(image.created)),
'size': 'size':
humanize.naturalsize(int(image.size), binary=True), humanize.naturalsize(int(image.size)),
'repoDigests': 'repoDigests':
' '.join(image.repoDigests), ' '.join(image.repoDigests),
}) })
for r in image.repoTags: for r in image.repoTags:
name, tag = r.split(':', 1) name, tag = r.rsplit(':', 1)
fields.update({ fields.update({
'name': name, 'name': name,
'tag': tag, 'tag': tag,
}) })
rows.append(fields) rows.append(fields)
if not self._args.digests: if not self._args.digests:
del self.columns['repoDigests'] del self.columns['repoDigests']

View File

@ -2,7 +2,7 @@
import sys import sys
import podman import podman
from pypodman.lib import AbstractActionBase from pypodman.lib import AbstractActionBase, ChangeAction
class Import(AbstractActionBase): class Import(AbstractActionBase):
@ -12,18 +12,19 @@ class Import(AbstractActionBase):
def subparser(cls, parent): def subparser(cls, parent):
"""Add Import command to parent parser.""" """Add Import command to parent parser."""
parser = parent.add_parser( parser = parent.add_parser(
'import', help='import tarball as image filesystem') 'import',
help='import tarball as image filesystem',
)
parser.add_argument( parser.add_argument(
'--change', '--change',
'-c', '-c',
action='append', action=ChangeAction,
choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL',
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
type=str.upper,
help='Apply the following possible instructions',
) )
parser.add_argument( parser.add_argument(
'--message', '-m', help='Set commit message for imported image.') '--message',
'-m',
help='Set commit message for imported image.',
)
parser.add_argument( parser.add_argument(
'source', 'source',
metavar='PATH', metavar='PATH',
@ -38,18 +39,25 @@ class Import(AbstractActionBase):
) )
parser.set_defaults(class_=cls, method='import_') parser.set_defaults(class_=cls, method='import_')
def __init__(self, args):
"""Construct Import class."""
super().__init__(args)
def import_(self): def import_(self):
"""Import tarball as image filesystem.""" """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: try:
ident = self.client.images.import_image( ident = self.client.images.import_image(
self.opts.source, self.opts['source'][0],
self.opts.reference, reference,
message=self.opts.message, **options,
changes=self.opts.change) )
print(ident) print(ident)
except podman.ErrorOccurred as e: except podman.ErrorOccurred as e:
sys.stdout.flush() sys.stdout.flush()
@ -58,3 +66,4 @@ class Import(AbstractActionBase):
file=sys.stderr, file=sys.stderr,
flush=True) flush=True)
return 1 return 1
return 0

View File

@ -23,9 +23,9 @@ class Inspect(AbstractActionBase):
help='Type of object to inspect', help='Type of object to inspect',
) )
parser.add_argument( parser.add_argument(
'size', '--size',
action='store_true', action='store_true',
default=True, default=False,
help='Display the total file size if the type is a container.' help='Display the total file size if the type is a container.'
' Always True.') ' Always True.')
parser.add_argument( parser.add_argument(
@ -59,7 +59,7 @@ class Inspect(AbstractActionBase):
def inspect(self): def inspect(self):
"""Inspect provided podman objects.""" """Inspect provided podman objects."""
output = {} output = []
try: try:
for ident in self._args.objects: for ident in self._args.objects:
obj = None obj = None
@ -78,7 +78,13 @@ class Inspect(AbstractActionBase):
msg = 'Object "{}" not found'.format(ident) msg = 'Object "{}" not found'.format(ident)
print(msg, file=sys.stderr, flush=True) print(msg, file=sys.stderr, flush=True)
else: 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: except podman.ErrorOccurred as e:
sys.stdout.flush() sys.stdout.flush()
print( print(

View File

@ -16,6 +16,7 @@ class Ps(AbstractActionBase):
"""Add Images command to parent parser.""" """Add Images command to parent parser."""
parser = parent.add_parser('ps', help='list containers') parser = parent.add_parser('ps', help='list containers')
super().subparser(parser) super().subparser(parser)
parser.add_argument( parser.add_argument(
'--sort', '--sort',
choices=('createdat', 'id', 'image', 'names', 'runningfor', 'size', choices=('createdat', 'id', 'image', 'names', 'runningfor', 'size',
@ -32,9 +33,9 @@ class Ps(AbstractActionBase):
self.columns = OrderedDict({ self.columns = OrderedDict({
'id': 'id':
ReportColumn('id', 'CONTAINER ID', 14), ReportColumn('id', 'CONTAINER ID', 12),
'image': 'image':
ReportColumn('image', 'IMAGE', 30), ReportColumn('image', 'IMAGE', 31),
'command': 'command':
ReportColumn('column', 'COMMAND', 20), ReportColumn('column', 'COMMAND', 20),
'createdat': 'createdat':
@ -49,10 +50,15 @@ class Ps(AbstractActionBase):
def list(self): def list(self):
"""List containers.""" """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 # TODO: Verify sorting on dates and size
ctnrs = sorted( ctnrs = sorted(ictnrs, key=operator.attrgetter(self._args.sort))
self.client.containers.list(),
key=operator.attrgetter(self._args.sort))
if not ctnrs: if not ctnrs:
return return
@ -65,9 +71,6 @@ class Ps(AbstractActionBase):
'createdat': 'createdat':
humanize.naturaldate(podman.datetime_parse(ctnr.createdat)), humanize.naturaldate(podman.datetime_parse(ctnr.createdat)),
}) })
if self._args.truncate:
fields.update({'image': ctnr.image[-30:]})
rows.append(fields) rows.append(fields)
with Report(self.columns, heading=self._args.heading) as report: with Report(self.columns, heading=self._args.heading) as report:

View File

@ -21,7 +21,7 @@ class Run(AbstractActionBase):
parser.add_argument('image', nargs=1, help='source image id.') parser.add_argument('image', nargs=1, help='source image id.')
parser.add_argument( parser.add_argument(
'command', 'command',
nargs='*', nargs=parent.REMAINDER,
help='command and args to run.', help='command and args to run.',
) )
parser.set_defaults(class_=cls, method='run') parser.set_defaults(class_=cls, method='run')

View 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

View File

@ -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"]))

View File

@ -4,9 +4,10 @@ Supplimental argparse.Action converters and validaters.
The constructors are very verbose but remain for IDE support. The constructors are very verbose but remain for IDE support.
""" """
import argparse import argparse
import copy
import os 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=redefined-builtin
# pragma pylint: disable=too-few-public-methods # pragma pylint: disable=too-few-public-methods
# pragma pylint: disable=too-many-arguments # pragma pylint: disable=too-many-arguments
@ -36,7 +37,7 @@ class BooleanAction(argparse.Action):
const=None, const=None,
default=None, default=None,
type=None, type=None,
choices=('True', 'False'), choices=None,
required=False, required=False,
help=None, help=None,
metavar='{True,False}'): metavar='{True,False}'):
@ -58,11 +59,58 @@ class BooleanAction(argparse.Action):
try: try:
val = BooleanValidate()(values) val = BooleanValidate()(values)
except ValueError: except ValueError:
parser.error('{} must be True or False.'.format(self.dest)) parser.error('"{}" must be True or False.'.format(option_string))
else: else:
setattr(namespace, self.dest, val) 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): class UnitAction(argparse.Action):
"""Validate number given is positive integer, with optional suffix.""" """Validate number given is positive integer, with optional suffix."""
@ -78,8 +126,8 @@ class UnitAction(argparse.Action):
help=None, help=None,
metavar='UNIT'): metavar='UNIT'):
"""Create UnitAction object.""" """Create UnitAction object."""
help = (help or metavar or dest help = (help or metavar or dest)\
) + ' (format: <number>[<unit>], where unit = b, k, m or g)' + ' (format: <number>[<unit>], where unit = b, k, m or g)'
super().__init__( super().__init__(
option_strings=option_strings, option_strings=option_strings,
dest=dest, dest=dest,
@ -99,15 +147,15 @@ class UnitAction(argparse.Action):
except ValueError: except ValueError:
if not values[:-1].isdigit(): if not values[:-1].isdigit():
msg = ('{} must be a positive integer,' msg = ('{} must be a positive integer,'
' with optional suffix').format(self.dest) ' with optional suffix').format(option_string)
parser.error(msg) parser.error(msg)
if not values[-1] in ('b', 'k', 'm', 'g'): if not values[-1] in ('b', 'k', 'm', 'g'):
msg = '{} only supports suffices of: b, k, m, g'.format( msg = '{} only supports suffices of: b, k, m, g'.format(
self.dest) option_string)
parser.error(msg) parser.error(msg)
else: else:
if val <= 0: if val <= 0:
msg = '{} must be a positive integer'.format(self.dest) msg = '{} must be a positive integer'.format(option_string)
parser.error(msg) parser.error(msg)
setattr(namespace, self.dest, values) setattr(namespace, self.dest, values)
@ -125,19 +173,16 @@ class PositiveIntAction(argparse.Action):
type=int, type=int,
choices=None, choices=None,
required=False, required=False,
help=None, help='Must be a positive integer.',
metavar=None): metavar=None):
"""Create PositiveIntAction object.""" """Create PositiveIntAction object."""
self.message = '{} must be a positive integer'.format(dest)
help = help or self.message
super().__init__( super().__init__(
option_strings=option_strings, option_strings=option_strings,
dest=dest, dest=dest,
nargs=nargs, nargs=nargs,
const=const, const=const,
default=default, default=default,
type=int, type=type,
choices=choices, choices=choices,
required=required, required=required,
help=help, help=help,
@ -149,7 +194,8 @@ class PositiveIntAction(argparse.Action):
setattr(namespace, self.dest, values) setattr(namespace, self.dest, values)
return return
parser.error(self.message) msg = '{} must be a positive integer'.format(option_string)
parser.error(msg)
class PathAction(argparse.Action): class PathAction(argparse.Action):

View File

@ -97,6 +97,8 @@ class PodmanArgumentParser(argparse.ArgumentParser):
actions_parser = self.add_subparsers( actions_parser = self.add_subparsers(
dest='subparser_name', help='commands') 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 buried here to prevent import loops
import pypodman.lib.actions # pylint: disable=cyclic-import import pypodman.lib.actions # pylint: disable=cyclic-import
@ -152,7 +154,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr( reqattr(
'run_dir', 'run_dir',
getattr(args, '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 config['default'].get('run_dir')
or str(Path(args.xdg_runtime_dir, 'pypodman')) or str(Path(args.xdg_runtime_dir, 'pypodman'))
) # yapf: disable ) # yapf: disable
@ -161,23 +163,24 @@ class PodmanArgumentParser(argparse.ArgumentParser):
args, args,
'host', 'host',
getattr(args, 'host') getattr(args, 'host')
or os.environ.get('HOST') or os.environ.get('PODMAN_HOST')
or config['default'].get('host') or config['default'].get('host')
) # yapf:disable ) # yapf:disable
reqattr( reqattr(
'username', 'username',
getattr(args, 'username') getattr(args, 'username')
or os.environ.get('PODMAN_USER')
or config['default'].get('username')
or os.environ.get('USER') or os.environ.get('USER')
or os.environ.get('LOGNAME') or os.environ.get('LOGNAME')
or config['default'].get('username')
or getpass.getuser() or getpass.getuser()
) # yapf:disable ) # yapf:disable
reqattr( reqattr(
'port', 'port',
getattr(args, 'port') getattr(args, 'port')
or os.environ.get('PORT') or os.environ.get('PODMAN_PORT')
or config['default'].get('port', None) or config['default'].get('port', None)
or 22 or 22
) # yapf:disable ) # yapf:disable
@ -185,7 +188,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr( reqattr(
'remote_socket_path', 'remote_socket_path',
getattr(args, '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 config['default'].get('remote_socket_path')
or '/run/podman/io.podman' or '/run/podman/io.podman'
) # yapf:disable ) # yapf:disable
@ -193,7 +196,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr( reqattr(
'log_level', 'log_level',
getattr(args, '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 config['default'].get('log_level')
or logging.WARNING or logging.WARNING
) # yapf:disable ) # yapf:disable
@ -202,7 +205,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
args, args,
'identity_file', 'identity_file',
getattr(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 config['default'].get('identity_file')
or os.path.expanduser('~{}/.ssh/id_dsa'.format(args.username)) or os.path.expanduser('~{}/.ssh/id_dsa'.format(args.username))
) # yapf:disable ) # yapf:disable

View File

@ -1,8 +1,23 @@
"""Report Manager.""" """Report Manager."""
import string
import sys import sys
from collections import namedtuple 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')): class ReportColumn(namedtuple('ReportColumn', 'key display width default')):
"""Hold attributes of output column.""" """Hold attributes of output column."""
@ -26,18 +41,24 @@ class Report():
""" """
self._columns = columns self._columns = columns
self._file = file self._file = file
self._format_string = None
self._formatter = ReportFormatter()
self._heading = heading self._heading = heading
self.epilog = epilog self.epilog = epilog
self._format = None
def row(self, **fields): def row(self, **fields):
"""Print row for report.""" """Print row for report."""
if self._heading: if self._heading:
hdrs = {k: v.display for (k, v) in self._columns.items()} 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 self._heading = False
fields = {k: str(v) for k, v in fields.items()} 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): def __enter__(self):
"""Return `self` upon entering the runtime context.""" """Return `self` upon entering the runtime context."""
@ -63,4 +84,4 @@ class Report():
display_len = info.width display_len = info.width
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len)) fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
self._format = ' '.join(fmt) self._format_string = ' '.join(fmt)

View File

@ -39,7 +39,7 @@
%global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7}) %global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7})
Name: podman Name: podman
Version: 0.10.2 Version: 0.11.2
Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Release: #COMMITDATE#.git%{shortcommit0}%{?dist}
Summary: Manage Pods, Containers and Container Images Summary: Manage Pods, Containers and Container Images
License: ASL 2.0 License: ASL 2.0
@ -378,10 +378,6 @@ providing packages with %{import_path} prefix.
%prep %prep
%autosetup -Sgit -n %{repo}-%{shortcommit0} %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 # untar cri-o
tar zxf %{SOURCE1} tar zxf %{SOURCE1}
@ -416,15 +412,17 @@ popd
%install %install
install -dp %{buildroot}%{_unitdir} install -dp %{buildroot}%{_unitdir}
%{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \ PODMAN_VERSION=%{version} %{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \
install.bin \ install.bin \
install.man \ install.man \
install.cni \ install.cni \
install.systemd \ install.systemd \
install.completions install.completions
mv pkg/hooks/README.md pkg/hooks/README-hooks.md
%if %{with varlink} %if %{with varlink}
%{__make} DESTDIR=%{buildroot} install.python PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} install.python
%endif # varlink %endif # varlink
# install libpod.conf # install libpod.conf

View File

@ -24,6 +24,18 @@ libpod to manage containers.
**cgroup_manager**="" **cgroup_manager**=""
Specify the CGroup Manager to use; valid values are "systemd" and "cgroupfs" 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**="" **static_dir**=""
Directory for persistent libpod files (database, etc) Directory for persistent libpod files (database, etc)
By default this will be configured relative to where containers/storage By default this will be configured relative to where containers/storage

View File

@ -171,7 +171,7 @@ value can be entered. The password is entered without echo.
**--disable-content-trust** **--disable-content-trust**
This is a Docker specific option to disable image verification to a Docker 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. soley for scripting compatibility.
**--file, -f** *Dockerfile* **--file, -f** *Dockerfile*

View File

@ -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 files are theoretically not needed, but if these files are needed Podman can keep the files
for further analysis. 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 ## EXAMPLE
podman container checkpoint mywebserver podman container checkpoint mywebserver

View 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)

View File

@ -24,6 +24,22 @@ processes in the checkpointed container.
Without the **-k**, **--keep** option the checkpoint will be consumed and cannot be used Without the **-k**, **--keep** option the checkpoint will be consumed and cannot be used
again. 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 ## EXAMPLE
podman container restore mywebserver podman container restore mywebserver

View File

@ -20,6 +20,7 @@ The container command allows you to manage containers
| create | [podman-create(1)](podman-create.1.md) | Create a new container. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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. | | wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |

View File

@ -66,7 +66,7 @@ Write the container ID to the file
**--conmon-pidfile**="" **--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* **--cpu-count**=*0*
@ -321,13 +321,13 @@ Not implemented
**--log-driver**="*json-file*" **--log-driver**="*json-file*"
Logging driver for the container. Default is defined by daemon `--log-driver` flag. Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility.
**Warning**: the `podman logs` command works only for the `json-file` and
`journald` logging drivers.
**--log-opt**=[] **--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**="" **--mac-address**=""
@ -414,7 +414,7 @@ UUID short identifier (“f78375b1c487”)
Name (“jonah”) Name (“jonah”)
podman generates a UUID for each container, and if a name is not assigned 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. string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers. 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 '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. '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 '<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**=[] **--network-alias**=[]
@ -454,7 +455,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain
**--pod**="" **--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* **--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. This is because by default a container is not allowed to access any devices.
A “privileged” container is given access to all devices. A “privileged” container is given access to all devices.
When the operator executes **podman run --privileged**, podman enables access When the operator executes a privileged container, podman enables access
to all devices on the host as well as set turn off most of the security measures to all devices on the host, turns off graphdriver mount options, as well as
protecting the host from the container. turning off most of the security measures protecting the host from the
container.
**-p**, **--publish**=[] **-p**, **--publish**=[]

View 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)

View File

@ -14,6 +14,7 @@ The image command allows you to manage images
| Command | Man Page | Description | | Command | Man Page | Description |
| -------- | ----------------------------------------- | ------------------------------------------------------------------------------ | | -------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. | | 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. | | 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. | | 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. | | inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a image or image's configuration. |

View File

@ -49,9 +49,9 @@ Sort by created, id, repository, size or tag (default: created)
``` ```
# podman images # podman images
REPOSITORY TAG IMAGE ID CREATED SIZE REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251kB docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251 kB
<none> <none> ebb91b73692b 4 weeks ago 27.2MB <none> <none> ebb91b73692b 4 weeks ago 27.2 MB
docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126MB docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126 MB
``` ```
``` ```
@ -63,17 +63,17 @@ ebb91b73692b
``` ```
# podman images --noheading # podman images --noheading
docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251kB docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251 kB
<none> <none> ebb91b73692b 4 weeks ago 27.2MB <none> <none> ebb91b73692b 4 weeks ago 27.2 MB
docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126MB docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126 MB
``` ```
``` ```
# podman images --no-trunc # podman images --no-trunc
REPOSITORY TAG IMAGE ID CREATED SIZE REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/kubernetes/pause latest sha256:e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27 3 years ago 251kB docker.io/kubernetes/pause latest sha256:e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27 3 years ago 251 kB
<none> <none> sha256:ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9 4 weeks ago 27.2MB <none> <none> sha256:ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9 4 weeks ago 27.2 MB
docker.io/library/ubuntu latest sha256:4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a 6 weeks ago 126MB docker.io/library/ubuntu latest sha256:4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a 6 weeks ago 126 MB
``` ```
``` ```
@ -87,7 +87,7 @@ ebb91b73692b <none> <none>
``` ```
# podman images --filter dangling=true # podman images --filter dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE 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 # podman images --sort repository
REPOSITORY TAG IMAGE ID CREATED SIZE REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 2460217d76fc About a minute ago 4.41MB <none> <none> 2460217d76fc About a minute ago 4.41 MB
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
localhost/myapp latest b2e0ad03474a About a minute ago 4.41MB localhost/myapp latest b2e0ad03474a About a minute ago 4.41 MB
registry.access.redhat.com/rhel7 latest 7a840db7f020 2 weeks ago 211MB registry.access.redhat.com/rhel7 latest 7a840db7f020 2 weeks ago 211 MB
registry.fedoraproject.org/fedora 27 801894bc0e43 6 weeks ago 246MB registry.fedoraproject.org/fedora 27 801894bc0e43 6 weeks ago 246 MB
``` ```
``` ```
# podman images # podman images
REPOSITORY TAG IMAGE ID CREATED SIZE REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/test latest 18f0c080cd72 4 seconds ago 4.42MB localhost/test latest 18f0c080cd72 4 seconds ago 4.42 MB
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
# podman images -a # podman images -a
REPOSITORY TAG IMAGE ID CREATED SIZE REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/test latest 18f0c080cd72 6 seconds ago 4.42MB localhost/test latest 18f0c080cd72 6 seconds ago 4.42 MB
<none> <none> 270e70dc54c0 7 seconds ago 4.42MB <none> <none> 270e70dc54c0 7 seconds ago 4.42 MB
<none> <none> 4ed6fbe43414 8 seconds ago 4.41MB <none> <none> 4ed6fbe43414 8 seconds ago 4.41 MB
<none> <none> 6b0df8e71508 8 seconds ago 4.41MB <none> <none> 6b0df8e71508 8 seconds ago 4.41 MB
docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
``` ```
## SEE ALSO ## SEE ALSO

View File

@ -4,7 +4,7 @@
podman\-kill - Kills one or more containers with a signal podman\-kill - Kills one or more containers with a signal
## SYNOPSIS ## SYNOPSIS
**podman kill** [*options*] *container* ... **podman kill** [*options*] [*container* ...]
## DESCRIPTION ## DESCRIPTION
The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal. 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