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:
FEDORA_CNI_COMMIT: "412b6d31280682bb4fab4446f113c22ff1886554"
CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9"
CRIO_COMMIT: "662dbb31b5d4f5ed54511a47cde7190c61c28677"
CRIU_COMMIT: "584cbe4643c3fc7dc901ff08bf923ca0fe7326f9"
RUNC_COMMIT: "78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d"
CRIO_COMMIT: "7a283c391abb7bd25086a8ff91dbb36ebdd24466"
CRIU_COMMIT: "c74b83cd49c00589c0c0468ba5fe685b67fdbd0a"
RUNC_COMMIT: "96ec2177ae841256168fcf76954f7177af9446eb"
# File to update in home-dir with task-specific env. var values
ENVLIB: ".bash_profile"
# Overrides default location (/tmp/cirrus) for repo clone
@ -42,11 +42,12 @@ full_vm_testing_task:
# 'matrix' combinations. All run in parallel.
matrix:
# Images are generated separetly, from build_images_task (below)
image_name: "ubuntu-1804-bionic-v20180911-libpod-63a86a18"
image_name: "ubuntu-18-libpod-0c954a67"
# TODO: Make these work (also build_images_task below)
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
#image_name: "centos-7-v20180911-libpod-fce09afe"
#image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe"
timeout_in: 120m
# Every *_script runs in sequence, for each task. The name prefix is for
@ -65,19 +66,49 @@ full_vm_testing_task:
success_script: $SCRIPT_BASE/success.sh
# This task build new images for future PR testing, but only after a PR merge.
# These images save needing to install/setup the same environment to test every
# PR. The 'active' image for testing is selected by the 'image_name' items in
# task above. Currently this requires manually updating them, but this could
# be automated (see comment at end).
# Because system tests are stored within the repository, it is sometimes
# necessary to execute them within a PR to validate changes.
optional_system_testing_task:
# Only run system tests in PRs (not on merge) if magic string is present
# in the PR description. Post-merge system testing is assumed to happen
# later from OS distribution's build systems.
only_if: >-
$CIRRUS_BRANCH != 'master' &&
$CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*.*'
gce_instance:
matrix:
image_name: "ubuntu-1804-bionic-v20180911-libpod-e8d18305"
# TODO: Make these work (also build_images_task below)
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
#image_name: "centos-7-v20180911-libpod-fce09afe"
#image_name: "fedora-cloud-base-28-1-1-7-libpod-fce09afe"
timeout_in: 60m
setup_environment_script: $SCRIPT_BASE/setup_environment.sh
system_test_script: $SCRIPT_BASE/system_test.sh
success_script: $SCRIPT_BASE/success.sh
# This task builds new cache-images for future PR testing. These images save
# time installing/setting up the environment while an engineer is waiting.
# The 'active' cache-images for full_vm_testing are selected by the
# 'image_name' keys. Updating those items requires manually modification,
# but this could be automated (see comment at end of build_vm_images_task).
build_vm_images_task:
# Only produce new images after a PR merge
only_if: $CIRRUS_BRANCH == 'master'
# Only produce new cache-images after a PR merge, and if a magic string
# is present in the most recent commit-message.
only_if: >-
$CIRRUS_BRANCH == 'master' &&
$CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*REBUILD\s*IMAGES\s*\*\*\*.*'
# Require tests to pass first.
depends_on:
- test # i.e. 'test_task'
- full_vm_testing # i.e. 'full_vm_testing_task'
env:
# CSV of packer builder names to enable (see $PACKER_BASE/libpod_images.json)
@ -99,6 +130,8 @@ build_vm_images_task:
# Version of packer to use
PACKER_VER: "1.3.1"
# VMs created by packer are not cleaned up by cirrus
auto_cancellation: $CI != "true"
gce_instance:
image_name: "image-builder-image" # Simply CentOS 7 + packer dependencies

View File

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

View File

@ -122,7 +122,7 @@ packages:
- python3-varlink
- python3-dateutil
- python3-psutil
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/55.dev.git578fe65.fc28/x86_64/runc-1.0.0-55.dev.git578fe65.fc28.x86_64.rpm
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/54.dev.git00dc700.fc28/x86_64/runc-1.0.0-54.dev.git00dc700.fc28.x86_64.rpm
tests:
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf
@ -163,6 +163,7 @@ packages:
- python3-dateutil
- python3-psutil
- container-selinux
- https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/54.dev.git00dc700.fc28/x86_64/runc-1.0.0-54.dev.git00dc700.fc28.x86_64.rpm
tests:
- sed 's/^expand-check.*/expand-check=0/g' -i /etc/selinux/semanage.conf

View File

@ -10,6 +10,13 @@ if [[ ${DIST} != "Fedora" ]]; then
PYTHON=python
fi
# Since CRIU 3.11 has been pushed to Fedora 28 the checkpoint/restore
# test cases are actually run. As CRIU uses iptables to lock and unlock
# the network during checkpoint and restore it needs the following two
# modules loaded.
modprobe ip6table_nat || :
modprobe iptable_nat || :
# Build the test image
${CONTAINER_RUNTIME} build -t ${IMAGE} -f Dockerfile.${DIST} . 2>build.log

View File

@ -40,6 +40,8 @@ ${LINTER} \
--exclude='.*_test\.go:.*error return value not checked.*\(errcheck\)$'\
--exclude='duplicate of.*_test.go.*\(dupl\)$'\
--exclude='cmd\/client\/.*\.go.*\(dupl\)$'\
--exclude='libpod\/.*_easyjson.go:.*'\
--exclude='.* other occurrence\(s\) of "(container|host|tmpfs|unknown)" found in: .*\(goconst\)$'\
--exclude='vendor\/.*'\
--exclude='podman\/.*'\
--exclude='server\/seccomp\/.*\.go.*$'\

103
API.md
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 ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) string](#ContainerCheckpoint)
[func ContainerExists(name: string) int](#ContainerExists)
[func ContainerRestore(name: string, keep: bool, tcpEstablished: bool) string](#ContainerRestore)
[func ContainerRunlabel(runlabel: Runlabel) ](#ContainerRunlabel)
[func CreateContainer(create: Create) string](#CreateContainer)
[func CreateImage() NotImplemented](#CreateImage)
@ -43,6 +51,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func HistoryImage(name: string) ImageHistory](#HistoryImage)
[func ImageExists(name: string) int](#ImageExists)
[func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage)
[func InspectContainer(name: string) string](#InspectContainer)
@ -57,6 +67,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ListContainerChanges(name: string) ContainerChanges](#ListContainerChanges)
[func ListContainerMounts() []string](#ListContainerMounts)
[func ListContainerPorts(name: string) NotImplemented](#ListContainerPorts)
[func ListContainerProcesses(name: string, opts: []string) []string](#ListContainerProcesses)
[func ListContainers() ListContainerData](#ListContainers)
@ -65,6 +79,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ListPods() ListPodData](#ListPods)
[func MountContainer(name: string) string](#MountContainer)
[func PauseContainer(name: string) string](#PauseContainer)
[func PausePod(name: string) string](#PausePod)
@ -103,6 +119,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func TopPod() NotImplemented](#TopPod)
[func UnmountContainer(name: string, force: bool) ](#UnmountContainer)
[func UnpauseContainer(name: string) string](#UnpauseContainer)
[func UnpausePod(name: string) string](#UnpausePod)
@ -165,6 +183,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type PodmanInfo](#PodmanInfo)
[type Runlabel](#Runlabel)
[type Sockets](#Sockets)
[type StringResponse](#StringResponse)
@ -211,6 +231,31 @@ attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOL
container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot
be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise,
the resulting image's ID will be returned as a string.
### <a name="ContainerCheckpoint"></a>func ContainerCheckpoint
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ContainerCheckpoint(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), leaveRunning: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container
ID. On successful checkpoint, the id of the checkpointed container is returned.
### <a name="ContainerExists"></a>func ContainerExists
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ContainerExists(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div>
ContainerExists takes a full or partial container ID or name and returns an int as to
whether the container exists in local storage. A result of 0 means the container does
exists; whereas a result of 1 means it could not be found.
### <a name="ContainerRestore"></a>func ContainerRestore
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ContainerRestore(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
ContainerRestore restores a container that has been checkpointed. The container to be restored can
be identified by its name or full/partial container ID. A successful restore will result in the return
of the container's ID.
### <a name="ContainerRunlabel"></a>func ContainerRunlabel
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ContainerRunlabel(runlabel: [Runlabel](#Runlabel)) </div>
ContainerRunlabel runs executes a command as described by a given container image label.
### <a name="CreateContainer"></a>func CreateContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -403,6 +448,13 @@ method HistoryImage(name: [string](https://godoc.org/builtin#string)) [ImageHist
HistoryImage takes the name or ID of an image and returns information about its history and layers. The returned
history is in the form of an array of ImageHistory structures. If the image cannot be found, an
[ImageNotFound](#ImageNotFound) error is returned.
### <a name="ImageExists"></a>func ImageExists
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ImageExists(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div>
ImageExists talks a full or partial image ID or name and returns an int as to whether
the image exists in local storage. An int result of 0 means the image does exist in
local storage; whereas 1 indicates the image does not exists in local storage.
### <a name="ImportImage"></a>func ImportImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -453,6 +505,17 @@ See also [StopPod](StopPod).
method ListContainerChanges(name: [string](https://godoc.org/builtin#string)) [ContainerChanges](#ContainerChanges)</div>
ListContainerChanges takes a name or ID of a container and returns changes between the container and
its base image. It returns a struct of changed, deleted, and added path names.
### <a name="ListContainerMounts"></a>func ListContainerMounts
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ListContainerMounts() [[]string](#[]string)</div>
ListContainerMounts gathers all the mounted container mount points and returns them as an array
of strings
### <a name="ListContainerPorts"></a>func ListContainerPorts
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ListContainerPorts(name: [string](https://godoc.org/builtin#string)) [NotImplemented](#NotImplemented)</div>
This function is not implemented yet.
### <a name="ListContainerProcesses"></a>func ListContainerProcesses
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -491,6 +554,12 @@ an image currently in storage. See also [InspectImage](InspectImage).
method ListPods() [ListPodData](#ListPodData)</div>
ListPods returns a list of pods in no particular order. They are
returned as an array of ListPodData structs. See also [GetPod](#GetPod).
### <a name="MountContainer"></a>func MountContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method MountContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination
mount is returned as a string.
### <a name="PauseContainer"></a>func PauseContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -696,6 +765,11 @@ be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise,
method TopPod() [NotImplemented](#NotImplemented)</div>
This method has not been implemented yet.
### <a name="UnmountContainer"></a>func UnmountContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method UnmountContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) </div>
UnmountContainer umounts a container by its name or full/partial container ID.
### <a name="UnpauseContainer"></a>func UnpauseContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@ -1293,6 +1367,33 @@ insecure_registries [[]string](#[]string)
store [InfoStore](#InfoStore)
podman [InfoPodmanBinary](#InfoPodmanBinary)
### <a name="Runlabel"></a>type Runlabel
Runlabel describes the required input for container runlabel
image [string](https://godoc.org/builtin#string)
authfile [string](https://godoc.org/builtin#string)
certDir [string](https://godoc.org/builtin#string)
creds [string](https://godoc.org/builtin#string)
display [bool](https://godoc.org/builtin#bool)
name [string](https://godoc.org/builtin#string)
pull [bool](https://godoc.org/builtin#bool)
signaturePolicyPath [string](https://godoc.org/builtin#string)
tlsVerify [bool](https://godoc.org/builtin#bool)
label [string](https://godoc.org/builtin#string)
extraArgs [[]string](#[]string)
opts [map[string]](#map[string])
### <a name="Sockets"></a>type Sockets
Sockets describes sockets location for a container
@ -1336,7 +1437,7 @@ ImageNotFound means the image could not be found by the provided name or ID in l
NoContainerRunning means none of the containers requested are running in a command that requires a running container.
### <a name="NoContainersInPod"></a>type NoContainersInPod
NoContainersInPod means a pod has no containers on which to perform operation. It contains
NoContainersInPod means a pod has no containers on which to perform the operation. It contains
the pod ID.
### <a name="PodContainerError"></a>type PodContainerError

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
commit automatically with `git commit -s`.
### Go Format and lint
All code changes must pass ``make validate`` and ``make lint``, as
executed in a standard container. The container image for this
purpose is provided at: ``quay.io/libpod/gate:latest``. However,
for changes to the image itself, it may also be built locally
from the repository root, with the command:
```
sudo podman build -t quay.io/libpod/gate:latest -f contrib/gate/Dockerfile .
```
The container executes 'make' by default, on a copy of the repository.
This avoids changing or leaving build artifacts in your working directory.
Execution does not require any special permissions from the host. However,
the repository root must be bind-mounted into the container at
'/usr/src/libpod'. For example, running `make lint` is done (from
the repository root) with the command:
``sudo podman run -it --rm -v $PWD:/usr/src/libpod:z quay.io/libpod/gate:latest lint``
### Integration Tests
Our primary means of performing integration testing for libpod is with the

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

View File

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

View File

@ -17,7 +17,7 @@ RUN dnf -y install btrfs-progs-devel \
libseccomp-devel \
libselinux-devel \
skopeo-containers \
https://kojipkgs.fedoraproject.org//packages/runc/1.0.0/55.dev.git578fe65.fc28/x86_64/runc-1.0.0-55.dev.git578fe65.fc28.x86_64.rpm \
runc \
make \
ostree-devel \
python \
@ -72,7 +72,7 @@ RUN set -x \
&& install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/
# Install conmon
ENV CRIO_COMMIT 662dbb31b5d4f5ed54511a47cde7190c61c28677
ENV CRIO_COMMIT 7a283c391abb7bd25086a8ff91dbb36ebdd24466
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/kubernetes-sigs/cri-o.git "$GOPATH/src/github.com/kubernetes-sigs/cri-o.git" \

View File

@ -1,6 +1,6 @@
GO ?= go
DESTDIR ?= /
EPOCH_TEST_COMMIT ?= 733cfe96819e1dc044e982b5321b3c902d1a47c6
EPOCH_TEST_COMMIT ?= 1b52843cfd2ae254a6e52c74e564730f1c875c4c
HEAD ?= HEAD
CHANGELOG_BASE ?= HEAD~
CHANGELOG_TARGET ?= HEAD
@ -23,7 +23,7 @@ BUILDTAGS_CROSS ?= containers_image_openpgp containers_image_ostree_stub exclude
ifneq (,$(findstring varlink,$(BUILDTAGS)))
PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go
endif
CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null | echo docker)
CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker)
HAS_PYTHON3 := $(shell command -v python3 2>/dev/null)
@ -31,12 +31,13 @@ BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e)
PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e | grep -v system )
COMMIT_NO ?= $(shell git rev-parse HEAD 2> /dev/null || true)
GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
BUILD_INFO ?= $(shell date +%s)
LDFLAGS_PODMAN ?= $(LDFLAGS) -X main.gitCommit=$(GIT_COMMIT) -X main.buildInfo=$(BUILD_INFO)
LIBPOD := ${PROJECT}/libpod
LDFLAGS_PODMAN ?= $(LDFLAGS) -X $(LIBPOD).gitCommit=$(GIT_COMMIT) -X $(LIBPOD).buildInfo=$(BUILD_INFO)
ISODATE ?= $(shell date --iso-8601)
LIBSECCOMP_COMMIT := release-2.3
@ -103,6 +104,9 @@ test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go)
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
$(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go)
$(GO) build -ldflags '$(LDFLAGS)' -o $@ $(PROJECT)/test/goecho
podman: .gopathok $(PODMAN_VARLINK_DEPENDENCIES)
$(GO) build -i -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman
@ -129,6 +133,7 @@ clean:
test/bin2img/bin2img \
test/checkseccomp/checkseccomp \
test/copyimg/copyimg \
test/goecho/goecho \
test/testdata/redis-image \
cmd/podman/varlink/iopodman.go \
libpod/container_ffjson.go \
@ -165,7 +170,7 @@ shell: libpodimage
testunit: libpodimage
${CONTAINER_RUNTIME} run -e STORAGE_OPTIONS="--storage-driver=vfs" -e TESTFLAGS -e CGROUP_MANAGER=cgroupfs -e TRAVIS -t --privileged --rm -v ${CURDIR}:/go/src/${PROJECT} ${LIBPOD_IMAGE} make localunit
localunit: varlink_generate
localunit: test/goecho/goecho varlink_generate
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
ginkgo:
@ -173,6 +178,12 @@ ginkgo:
localintegration: varlink_generate test-binaries clientintegration ginkgo
localsystem: .install.ginkgo .install.gomega
ginkgo -v -noColor test/system/
system.test-binary: .install.ginkgo .install.gomega
$(GO) test -c ./test/system
clientintegration:
$(MAKE) -C contrib/python/podman integration
$(MAKE) -C contrib/python/pypodman integration
@ -182,7 +193,7 @@ vagrant-check:
binaries: varlink_generate easyjson_generate podman
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp test/goecho/goecho
MANPAGES_MD ?= $(wildcard docs/*.md pkg/*/docs/*.md)
MANPAGES ?= $(MANPAGES_MD:%.md=%)
@ -282,7 +293,7 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man .ins
if [ ! -x "$(GOBIN)/gometalinter" ]; then \
$(GO) get -u github.com/alecthomas/gometalinter; \
cd $(FIRST_GOPATH)/src/github.com/alecthomas/gometalinter; \
git checkout 23261fa046586808612c61da7a81d75a658e0814; \
git checkout e8d801238da6f0dfd14078d68f9b53fa50a7eeb5; \
$(GO) install github.com/alecthomas/gometalinter; \
$(GOBIN)/gometalinter --install; \
fi

5
OWNERS
View File

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

View File

@ -1,9 +1,11 @@
![PODMAN logo](logo/podman-logo-source.svg)
# libpod - library for running OCI-based containers in Pods
### Latest Version: 0.10.1.3
### Latest Version: 0.11.1.1
### Status: Active Development
### Continuous Integration: [![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?
libpod provides a library for applications looking to use the Container Pod concept popularized by Kubernetes.

View File

@ -1,5 +1,53 @@
# Release Notes
## 0.11.1.1
### Bugfixes
- Fixed a bug where Podman was not correctly adding firewall rules for containers, preventing them from accessing the network
- Fixed a bug where full error messages were being lost when creating containers with user namespaces
- Fixed a bug where container state was not properly updated if a failure occurred during network setup, which could cause mounts to be left behind when the container was removed
- Fixed a bug where `podman exec` could time out on slower systems by increasing the relevant timeout
### Misc
- `podman rm -f` now removes paused containers. As such, `podman rm -af` completing successfully guarantees all Podman containers have been removed
- Added a field to `podman info` to show if Podman is being run as rootless
- Made a small output format change to `podman images` - image sizes now feature a space between number and unit (e.g. `123 MB` now instead of `123MB`)
- Vendored an updated version of `containers/storage` to fix several bugs reported upstream
## 0.11.1
### Features
- Added `--all` and `--latest` flags to `podman checkpoint` and `podman restore`
- Added `--max-workers` flag to all Podman commands that support operating in parallel, allowing the maximum number of parallel workers used to be specified
- Added `--all` flag to `podman restart`
### Bugfixes
- Fixed a bug where `podman port -l` would segfault if no containers were present
- Fixed a bug where `podman stats -a` would error if containers were present but not running
- Fixed a bug where container status checks would sometimes leave zombie OCI runtime processes
- Fixed checkpoint and restore code to verify an appropriate version of `criu` is being used
- Fixed a bug where environment variables with no specified value (e.g. `-e FOO`) caused errors (they are now added as empty)
- Fixed a bug where rootless Podman would attempt to configure the system firewall, causing errors on some systems where iptables is not in the user's PATH
- Fixed a bug where rootless Podman was unable to successfully write the container ID to a file when `--cid-file` was specified to `podman run`
- Fixed a bug where `podman unmount` would refuse to unmount a container if it was running (the unmount will now be deferred until the container stops)
- Fixed a bug where rootless `podman attach` would fail to attach due to a too-long path name
- Fixed a bug where `podman info` was not properly reporting the Git commit Podman was built from
- Fixed a bug where `podman run --interactive` was not holding STDIN open when `-a` flag was specified
- Fixed a bug where Podman with the `cgroupfs` CGroup driver was sometimes not successfully removing pod CGroups
- Fixed a bug where rootless Podman was unable to run systemd containers (note that this also requires an update to systemd)
- Fixed a bug where `podman run` with the `--user` flag would fail if the container image did not contain `/etc/passwd` or `/etc/group`
### Misc
- `podman rm`, `podman restart`, `podman kill`, `podman pause`, and `podman unpause` now operate in parallel, greatly improving speed when multiple containers are specified
- `podman create`, `podman run`, and `podman ps` have a number of improvements which should greatly increase their speed
- Greatly improved performance and reduced memory utilization of container status checks, which should improve the speed of most Podman commands
- Improve ability of `podman runlabel` to run commands that are not Podman
- Podman containers with an IP address now add their hostnames to `/etc/hosts`
- Changed default location of temporary libpod files in rootless Podman
- Updated the default Podman seccomp profile
### Compatability
Several paths related to rootless Podman had their default values changed in this release.
If paths were not hardcoded in libpod.conf, your system may lose track of running containers and believe they are newly-created.
## 0.10.1.3
### Bugfixes
- Fixed a bug where `podman build` would not work while any containers were running

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)
* Update release notes for 0.10.1.3
* Vendor in new new buildah/ci

View File

@ -1,6 +1,11 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
buildahcli "github.com/containers/buildah/pkg/cli"
@ -10,14 +15,14 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var (
layerFlags = []cli.Flag{
cli.BoolTFlag{
Name: "force-rm",
Usage: "Always remove intermediate containers after a build, even if the build is unsuccessful. (default true)",
},
cli.BoolTFlag{
Name: "layers",
Usage: "cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override. ",
@ -230,7 +235,7 @@ func buildCmd(c *cli.Context) error {
Layers: layers,
NoCache: c.Bool("no-cache"),
RemoveIntermediateCtrs: c.BoolT("rm"),
ForceRmIntermediateCtrs: c.Bool("force-rm"),
ForceRmIntermediateCtrs: c.BoolT("force-rm"),
}
if c.Bool("quiet") {

View File

@ -23,6 +23,14 @@ var (
Name: "keep, k",
Usage: "keep all temporary checkpoint files",
},
cli.BoolFlag{
Name: "leave-running, R",
Usage: "leave the container running after writing checkpoint to disk",
},
cli.BoolFlag{
Name: "tcp-established",
Usage: "checkpoint a container with established TCP connections",
},
cli.BoolFlag{
Name: "all, a",
Usage: "checkpoint all running containers",
@ -50,7 +58,11 @@ func checkpointCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
keep := c.Bool("keep")
options := libpod.ContainerCheckpointOptions{
Keep: c.Bool("keep"),
KeepRunning: c.Bool("leave-running"),
TCPEstablished: c.Bool("tcp-established"),
}
if err := checkAllAndLatest(c); err != nil {
return err
@ -59,7 +71,7 @@ func checkpointCmd(c *cli.Context) error {
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
for _, ctr := range containers {
if err = ctr.Checkpoint(context.TODO(), keep); err != nil {
if err = ctr.Checkpoint(context.TODO(), options); err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}

View File

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

View File

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

View File

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

View File

@ -11,6 +11,7 @@ import (
"syscall"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations"
@ -66,7 +67,7 @@ func createCmd(c *cli.Context) error {
rootless.SetSkipStorageSetup(true)
}
runtime, err := libpodruntime.GetContainerRuntime(c)
runtime, err := libpodruntime.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@ -375,8 +376,8 @@ func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string {
return entrypoint
}
func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string) (map[string]string, error) {
pod, err := runtime.LookupPod(c.String("pod"))
func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) {
pod, err := runtime.LookupPod(podName)
if err != nil {
return namespaces, err
}
@ -409,6 +410,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
inputCommand, command []string
memoryLimit, memoryReservation, memorySwap, memoryKernel int64
blkioWeight uint16
namespaces map[string]string
)
idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname"))
if err != nil {
@ -492,12 +494,21 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together")
}
// EXPOSED PORTS
var portBindings map[nat.Port][]nat.PortBinding
if data != nil {
portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
if err != nil {
return nil, err
}
}
// Kernel Namespaces
// TODO Fix handling of namespace from pod
// Instead of integrating here, should be done in libpod
// However, that also involves setting up security opts
// when the pod's namespace is integrated
namespaces := map[string]string{
namespaces = map[string]string{
"pid": c.String("pid"),
"net": c.String("net"),
"ipc": c.String("ipc"),
@ -505,8 +516,41 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
"uts": c.String("uts"),
}
originalPodName := c.String("pod")
podName := strings.Replace(originalPodName, "new:", "", 1)
// after we strip out :new, make sure there is something left for a pod name
if len(podName) < 1 && c.IsSet("pod") {
return nil, errors.Errorf("new pod name must be at least one character")
}
if c.IsSet("pod") {
namespaces, err = configurePod(c, runtime, namespaces)
if strings.HasPrefix(originalPodName, "new:") {
// pod does not exist; lets make it
var podOptions []libpod.PodCreateOption
podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups())
if len(portBindings) > 0 {
ociPortBindings, err := cc.NatToOCIPortBindings(portBindings)
if err != nil {
return nil, err
}
podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings))
}
podNsOptions, err := shared.GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ","))
if err != nil {
return nil, err
}
podOptions = append(podOptions, podNsOptions...)
// make pod
pod, err := runtime.NewPod(ctx, podOptions...)
if err != nil {
return nil, err
}
logrus.Debugf("pod %s created by new container request", pod.ID())
// The container now cannot have port bindings; so we reset the map
portBindings = make(map[nat.Port][]nat.PortBinding)
}
namespaces, err = configurePod(c, runtime, namespaces, podName)
if err != nil {
return nil, err
}
@ -535,7 +579,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
// Make sure if network is set to container namespace, port binding is not also being asked for
netMode := ns.NetworkMode(namespaces["net"])
if netMode.IsContainer() {
if len(c.StringSlice("publish")) > 0 || c.Bool("publish-all") {
if len(portBindings) > 0 {
return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
}
}
@ -644,15 +688,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image")
}
// EXPOSED PORTS
var portBindings map[nat.Port][]nat.PortBinding
if data != nil {
portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts)
if err != nil {
return nil, err
}
}
// SHM Size
shmSize, err := units.FromHumanSize(c.String("shm-size"))
if err != nil {
@ -670,6 +705,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
if util.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
}
if !netMode.IsPrivate() {
if c.IsSet("dns-search") || c.IsSet("dns") || c.IsSet("dns-opt") {
return nil, errors.Errorf("specifying DNS flags when network mode is shared with the host or another container is not allowed")
}
}
// Validate domains are good
for _, dom := range c.StringSlice("dns-search") {
@ -741,7 +781,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
NetMode: netMode,
UtsMode: utsMode,
PidMode: pidMode,
Pod: c.String("pod"),
Pod: podName,
Privileged: c.Bool("privileged"),
Publish: c.StringSlice("publish"),
PublishAll: c.Bool("publish-all"),

120
cmd/podman/exists.go Normal file
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,
historyCommand,
importCommand,
imageExistsCommand,
inspectCommand,
loadCommand,
lsImagesCommand,

View File

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

View File

@ -139,7 +139,7 @@ func downloadFromURL(source string) (string, error) {
_, err = io.Copy(outFile, response.Body)
if err != nil {
return "", errors.Wrapf(err, "error saving %q to %q", source, outFile)
return "", errors.Wrapf(err, "error saving %s to %s", source, outFile.Name())
}
return outFile.Name(), nil

View File

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

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"))
if err != nil {
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
@ -154,12 +154,12 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
} else {
libpodInspectData, err := ctr.Inspect(c.Bool("size"))
if err != nil {
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID)
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
if err != nil {
inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID)
inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
break
}
}

View File

@ -1,15 +1,16 @@
package main
import (
"os"
"fmt"
"syscall"
"fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@ -41,6 +42,11 @@ var (
// killCmd kills one or more containers with a signal
func killCmd(c *cli.Context) error {
var (
killFuncs []shared.ParallelWorkerInput
killSignal uint = uint(syscall.SIGTERM)
)
if err := checkAllAndLatest(c); err != nil {
return err
}
@ -56,7 +62,6 @@ func killCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
var killSignal uint = uint(syscall.SIGTERM)
if c.String("signal") != "" {
// Check if the signalString provided by the user is valid
// Invalid signals will return err
@ -67,17 +72,32 @@ func killCmd(c *cli.Context) error {
killSignal = uint(sysSignal)
}
containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
if err != nil {
if len(containers) == 0 {
return err
}
fmt.Println(err.Error())
}
for _, ctr := range containers {
if err := ctr.Kill(killSignal); err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
con := ctr
f := func() error {
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,
})
}
maxWorkers := shared.Parallelize("kill")
if c.GlobalIsSet("max-workers") {
maxWorkers = c.GlobalInt("max-workers")
}
return lastError
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,36 +5,23 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// GetRuntime generates a new libpod runtime configured by command line options
func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
storageOpts, err := util.GetDefaultStoreOptions()
if err != nil {
return nil, err
}
return GetRuntimeWithStorageOpts(c, &storageOpts)
}
storageOpts := new(storage.StoreOptions)
options := []libpod.RuntimeOption{}
// GetContainerRuntime generates a new libpod runtime configured by command line options for containers
func GetContainerRuntime(c *cli.Context) (*libpod.Runtime, error) {
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, 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{}
}
if c.GlobalIsSet("root") {
storageOpts.GraphRoot = c.GlobalString("root")
@ -42,6 +29,9 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions
if c.GlobalIsSet("runroot") {
storageOpts.RunRoot = c.GlobalString("runroot")
}
if len(storageOpts.RunRoot) > 50 {
return nil, errors.New("the specified runroot is longer than 50 characters")
}
if c.GlobalIsSet("storage-driver") {
storageOpts.GraphDriverName = c.GlobalString("storage-driver")
}
@ -86,8 +76,8 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions
if c.GlobalIsSet("default-mounts-file") {
options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file")))
}
if c.GlobalIsSet("hooks-dir-path") {
options = append(options, libpod.WithHooksDir(c.GlobalString("hooks-dir-path")))
if c.GlobalIsSet("hooks-dir") {
options = append(options, libpod.WithHooksDir(c.GlobalStringSlice("hooks-dir")...))
}
// TODO flag to set CNI plugins dir?

View File

@ -48,6 +48,7 @@ var (
ArgsUsage: "CONTAINER",
SkipArgReorder: true,
OnUsageError: usageErrorHandler,
UseShortOptionHandling: true,
}
)

View File

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

View File

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

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{
podCreateCommand,
podExistsCommand,
podInspectCommand,
podKillCommand,
podPauseCommand,

View File

@ -3,11 +3,15 @@ package main
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@ -58,6 +62,10 @@ var podCreateFlags = []cli.Flag{
Name: "pod-id-file",
Usage: "Write the pod ID to the file",
},
cli.StringSliceFlag{
Name: "publish, p",
Usage: "Publish a container's port, or a range of ports, to the host (default [])",
},
cli.StringFlag{
Name: "share",
Usage: "A comma delimited list of kernel namespaces the pod will share",
@ -102,6 +110,16 @@ func podCreateCmd(c *cli.Context) error {
defer podIdFile.Close()
defer podIdFile.Sync()
}
if len(c.StringSlice("publish")) > 0 {
if !c.BoolT("infra") {
return errors.Errorf("you must have an infra container to publish port bindings to the host")
}
if rootless.IsRootless() {
return errors.Errorf("rootless networking does not allow port binding to the host")
}
}
if !c.BoolT("infra") && c.IsSet("share") && c.String("share") != "none" && c.String("share") != "" {
return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
}
@ -131,6 +149,14 @@ func podCreateCmd(c *cli.Context) error {
options = append(options, nsOptions...)
}
if len(c.StringSlice("publish")) > 0 {
portBindings, err := CreatePortBindings(c.StringSlice("publish"))
if err != nil {
return err
}
options = append(options, libpod.WithInfraContainerPorts(portBindings))
}
// always have containers use pod cgroups
// User Opt out is not yet supported
options = append(options, libpod.WithPodCgroups())
@ -152,3 +178,36 @@ func podCreateCmd(c *cli.Context) error {
return nil
}
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
// The conversion from []string to natBindings is temporary while mheon reworks the port
// deduplication code. Eventually that step will not be required.
_, natBindings, err := nat.ParsePortSpecs(ports)
if err != nil {
return nil, err
}
for containerPb, hostPb := range natBindings {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
var hostPort int
var err error
pm.HostIP = i.HostIP
if i.HostPort == "" {
hostPort = containerPb.Int()
} else {
hostPort, err = strconv.Atoi(i.HostPort)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert host port to integer")
}
}
pm.HostPort = int32(hostPort)
pm.Protocol = containerPb.Proto()
portBindings = append(portBindings, pm)
}
}
return portBindings, nil
}

View File

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

View File

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

View File

@ -4,7 +4,6 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@ -46,8 +45,6 @@ Running containers will not be removed without the -f option.
// saveCmd saves the image to either docker-archive or oci
func rmCmd(c *cli.Context) error {
var (
delContainers []*libpod.Container
lastError error
deleteFuncs []shared.ParallelWorkerInput
)
@ -65,7 +62,13 @@ func rmCmd(c *cli.Context) error {
return err
}
delContainers, lastError = getAllOrLatestContainers(c, runtime, -1, "all")
delContainers, err := getAllOrLatestContainers(c, runtime, -1, "all")
if err != nil {
if len(delContainers) == 0 {
return err
}
fmt.Println(err.Error())
}
for _, container := range delContainers {
con := container
@ -84,14 +87,7 @@ func rmCmd(c *cli.Context) error {
}
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
deleteErrors := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
for cid, result := range deleteErrors {
if result != nil {
fmt.Println(result.Error())
lastError = result
continue
}
fmt.Println(cid)
}
return lastError
// Run the parallel funcs
deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
return printParallelOutput(deleteErrors, errCount)
}

View File

@ -91,9 +91,24 @@ func rmiCmd(c *cli.Context) error {
if err != nil {
return errors.Wrapf(err, "unable to query local images")
}
lastNumberofImages := 0
for len(imagesToDelete) > 0 {
if lastNumberofImages == len(imagesToDelete) {
return errors.New("unable to delete all images; re-run the rmi command again.")
}
for _, i := range imagesToDelete {
isParent, err := i.IsParent()
if err != nil {
return err
}
if isParent {
continue
}
removeImage(i)
}
lastNumberofImages = len(imagesToDelete)
imagesToDelete, err = runtime.ImageRuntime().GetImages()
}
} else {
// Create image.image objects for deletion from user input.
// Note that we have to query the storage one-by-one to

View File

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

View File

@ -6,11 +6,9 @@ import (
"os"
"strings"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -94,7 +92,7 @@ func runlabelCmd(c *cli.Context) error {
imageName string
stdErr, stdOut io.Writer
stdIn io.Reader
newImage *image.Image
extraArgs []string
)
// Evil images could trick into recursively executing the runlabel
@ -124,6 +122,9 @@ func runlabelCmd(c *cli.Context) error {
return errors.Errorf("the display and quiet flags cannot be used together.")
}
if len(args) > 2 {
extraArgs = args[2:]
}
pull := c.Bool("pull")
label := args[0]
@ -151,75 +152,24 @@ func runlabelCmd(c *cli.Context) error {
stdIn = nil
}
if pull {
var registryCreds *types.DockerAuthConfig
if c.IsSet("creds") {
creds, err := util.ParseRegistryCreds(c.String("creds"))
if err != nil {
return err
}
registryCreds = creds
}
dockerRegistryOptions := image.DockerRegistryOptions{
DockerRegistryCreds: registryCreds,
DockerCertPath: c.String("cert-dir"),
DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
}
authfile := getAuthFile(c.String("authfile"))
newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, c.String("signature-policy"), authfile, stdOut, &dockerRegistryOptions, image.SigningOptions{}, false, false)
} else {
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
}
if err != nil {
return errors.Wrapf(err, "unable to find image")
}
if len(newImage.Names()) < 1 {
imageName = newImage.ID()
} else {
imageName = newImage.Names()[0]
}
runLabel, err := newImage.GetLabel(ctx, label)
runLabel, imageName, err := shared.GetRunlabel(label, runlabelImage, ctx, runtime, pull, c.String("creds"), dockerRegistryOptions, authfile, c.String("signature-policy"), stdOut)
if err != nil {
return err
}
// If no label to execute, we return
if runLabel == "" {
return nil
}
// The user provided extra arguments that need to be tacked onto the label's command
if len(args) > 2 {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " "))
}
cmd, err := shared.GenerateCommand(runLabel, imageName, c.String("name"))
cmd, env, err := shared.GenerateRunlabelCommand(runLabel, imageName, c.String("name"), opts, extraArgs)
if err != nil {
return errors.Wrapf(err, "unable to generate command")
return err
}
env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts)
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
envmap := envSliceToMap(env)
envmapper := func(k string) string {
switch k {
case "OPT1":
return envmap["OPT1"]
case "OPT2":
return envmap["OPT2"]
case "OPT3":
return envmap["OPT3"]
}
return ""
}
newS := os.Expand(strings.Join(cmd, " "), envmapper)
cmd = strings.Split(newS, " ")
if !c.Bool("quiet") {
fmt.Printf("Command: %s\n", strings.Join(cmd, " "))
if c.Bool("display") {
@ -228,12 +178,3 @@ func runlabelCmd(c *cli.Context) error {
}
return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...)
}
func envSliceToMap(env []string) map[string]string {
m := make(map[string]string)
for _, i := range env {
split := strings.Split(i, "=")
m[split[0]] = strings.Join(split[1:], " ")
}
return m
}

View File

@ -1,10 +1,15 @@
package shared
import (
"context"
"encoding/json"
"fmt"
"github.com/containers/image/types"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
"io"
"os"
"path/filepath"
"regexp"
@ -589,3 +594,79 @@ func portsToString(ports []ocicni.PortMapping) string {
}
return strings.Join(portDisplay, ", ")
}
// GetRunlabel is a helper function for runlabel; it gets the image if needed and begins the
// contruction of the runlabel output and environment variables
func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtime *libpod.Runtime, pull bool, inputCreds string, dockerRegistryOptions image.DockerRegistryOptions, authfile string, signaturePolicyPath string, output io.Writer) (string, string, error) {
var (
newImage *image.Image
err error
imageName string
)
if pull {
var registryCreds *types.DockerAuthConfig
if inputCreds != "" {
creds, err := util.ParseRegistryCreds(inputCreds)
if err != nil {
return "", "", err
}
registryCreds = creds
}
dockerRegistryOptions.DockerRegistryCreds = registryCreds
newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, false, false)
} else {
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
}
if err != nil {
return "", "", errors.Wrapf(err, "unable to find image")
}
if len(newImage.Names()) < 1 {
imageName = newImage.ID()
} else {
imageName = newImage.Names()[0]
}
runLabel, err := newImage.GetLabel(ctx, label)
return runLabel, imageName, err
}
// GenerateRunlabelCommand generates the command that will eventually be execucted by podman
func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string) ([]string, []string, error) {
// The user provided extra arguments that need to be tacked onto the label's command
if len(extraArgs) > 0 {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " "))
}
cmd, err := GenerateCommand(runLabel, imageName, name)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to generate command")
}
env := GenerateRunEnvironment(name, imageName, opts)
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
envmap := envSliceToMap(env)
envmapper := func(k string) string {
switch k {
case "OPT1":
return envmap["OPT1"]
case "OPT2":
return envmap["OPT2"]
case "OPT3":
return envmap["OPT3"]
}
return ""
}
newS := os.Expand(strings.Join(cmd, " "), envmapper)
cmd = strings.Split(newS, " ")
return cmd, env, nil
}
func envSliceToMap(env []string) map[string]string {
m := make(map[string]string)
for _, i := range env {
split := strings.Split(i, "=")
m[split[0]] = strings.Join(split[1:], " ")
}
return m
}

View File

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

View File

@ -18,10 +18,11 @@ var (
)
func TestGenerateCommand(t *testing.T) {
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo \"hello world\""
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo hello world"
newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
assert.Nil(t, err)
assert.Equal(t, "hello world", newCommand[11])
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}

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
// int determines how many workers/threads should be premade.
func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map[string]error {
func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) (map[string]error, int) {
var (
wg sync.WaitGroup
errorCount int
)
resultChan := make(chan containerError, len(functions))
@ -62,9 +63,12 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
close(resultChan)
for ctrError := range resultChan {
results[ctrError.ContainerID] = ctrError.Err
if ctrError.Err != nil {
errorCount += 1
}
}
return results
return results, errorCount
}
// Parallelize provides the maximum number of parallel workers (int) as calculated by a basic
@ -72,20 +76,37 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
func Parallelize(job string) int {
numCpus := runtime.NumCPU()
switch job {
case "stop":
if numCpus <= 2 {
return 4
} else {
case "kill":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
case "pause":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
case "ps":
return 8
case "restart":
return numCpus * 2
case "rm":
if numCpus <= 3 {
return numCpus * 3
} else {
return numCpus * 4
}
case "ps":
return 8
case "stop":
if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
case "unpause":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
}
return 3
}

View File

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

View File

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

View File

@ -207,3 +207,20 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error
}
return pods, lastError
}
//printParallelOutput takes the map of parallel worker results and outputs them
// to stdout
func printParallelOutput(m map[string]error, errCount int) error {
var lastError error
for cid, result := range m {
if result != nil {
if errCount > 1 {
fmt.Println(result.Error())
}
lastError = result
continue
}
fmt.Println(cid)
}
return lastError
}

View File

@ -371,6 +371,22 @@ type PodContainerErrorData (
reason: string
)
# Runlabel describes the required input for container runlabel
type Runlabel(
image: string,
authfile: string,
certDir: string,
creds: string,
display: bool,
name: string,
pull: bool,
signaturePolicyPath: string,
tlsVerify: bool,
label: string,
extraArgs: []string,
opts: [string]string
)
# Ping provides a response for developers to ensure their varlink setup is working.
# #### Example
# ~~~
@ -804,6 +820,42 @@ method TopPod() -> (notimplemented: NotImplemented)
# ~~~
method GetPodStats(name: string) -> (pod: string, containers: []ContainerStats)
# ImageExists talks a full or partial image ID or name and returns an int as to whether
# the image exists in local storage. An int result of 0 means the image does exist in
# local storage; whereas 1 indicates the image does not exists in local storage.
method ImageExists(name: string) -> (exists: int)
# ContainerExists takes a full or partial container ID or name and returns an int as to
# whether the container exists in local storage. A result of 0 means the container does
# exists; whereas a result of 1 means it could not be found.
method ContainerExists(name: string) -> (exists: int)
# ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container
# ID. On successful checkpoint, the id of the checkpointed container is returned.
method ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) -> (id: string)
# ContainerRestore restores a container that has been checkpointed. The container to be restored can
# be identified by its name or full/partial container ID. A successful restore will result in the return
# of the container's ID.
method ContainerRestore(name: string, keep: bool, tcpEstablished: bool) -> (id: string)
# ContainerRunlabel runs executes a command as described by a given container image label.
method ContainerRunlabel(runlabel: Runlabel) -> ()
# ListContainerMounts gathers all the mounted container mount points and returns them as an array
# of strings
method ListContainerMounts() -> (mounts: []string)
# MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination
# mount is returned as a string.
method MountContainer(name: string) -> (path: string)
# UnmountContainer umounts a container by its name or full/partial container ID.
method UnmountContainer(name: string, force: bool) -> ()
# This function is not implemented yet.
method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (name: string)

View File

@ -4,6 +4,7 @@ import (
"fmt"
"time"
"github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/libpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
@ -15,13 +16,26 @@ func versionCmd(c *cli.Context) error {
if err != nil {
errors.Wrapf(err, "unable to determine version")
}
versionOutputFormat := c.String("format")
if versionOutputFormat != "" {
var out formats.Writer
switch versionOutputFormat {
case formats.JSONString:
out = formats.JSONStruct{Output: output}
default:
out = formats.StdoutTemplate{Output: output, Template: versionOutputFormat}
}
formats.Writer(out).Out()
return nil
}
fmt.Println("Version: ", output.Version)
fmt.Println("Go Version: ", output.GoVersion)
if output.GitCommit != "" {
fmt.Println("Git Commit: ", output.GitCommit)
}
// Prints out the build time in readable format
if libpod.BuildInfo != "" {
if output.Built != 0 {
fmt.Println("Built: ", time.Unix(output.Built, 0).Format(time.ANSIC))
}
@ -30,8 +44,17 @@ func versionCmd(c *cli.Context) error {
}
// Cli command to print out the full version of podman
var versionCommand = cli.Command{
var (
versionCommand = cli.Command{
Name: "version",
Usage: "Display the PODMAN Version Information",
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",
},
}
)

View File

@ -1,5 +1,6 @@
: ${PROG:=$(basename ${BASH_SOURCE})}
__podman_previous_extglob_setting=$(shopt -p extglob)
shopt -s extglob
@ -715,16 +716,22 @@ _podman_container_attach() {
}
_podman_container_checkpoint() {
local options_with_args="
--help -h
"
local boolean_options="
--keep
-a
--all
-h
--help
-k
--keep
-l
--latest
-R
--leave-running
--tcp-established
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
COMPREPLY=($(compgen -W "$boolean_options" -- "$cur"))
;;
*)
__podman_complete_containers_running
@ -784,6 +791,8 @@ _podman_container_refresh() {
local options_with_args="
"
local boolean_options="
--help
-h
"
_complete_ "$options_with_args" "$boolean_options"
}
@ -793,16 +802,20 @@ _podman_container_restart() {
}
_podman_container_restore() {
local options_with_args="
--help -h
"
local boolean_options="
--keep
-a
--all
-h
--help
-k
--keep
-l
--latest
--tcp-established
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
COMPREPLY=($(compgen -W "$boolean_options" -- "$cur"))
;;
*)
__podman_complete_containers_created
@ -814,10 +827,6 @@ _podman_container_rm() {
_podman_rm
}
_podman_container_run() {
_podman_run
}
_podman_container_start() {
_podman_start
}
@ -1015,6 +1024,8 @@ _podman_diff() {
--format
"
local boolean_options="
--help
-h
"
_complete_ "$options_with_args" "$boolean_options"
@ -1036,6 +1047,8 @@ _podman_exec() {
-u
"
local boolean_options="
--help
-h
--latest
-l
--privileged
@ -1058,6 +1071,8 @@ _podman_export() {
-o
"
local boolean_options="
--help
-h
"
case "$cur" in
-*)
@ -1074,6 +1089,8 @@ _podman_history() {
--format
"
local boolean_options="
--help
-h
--human -H
--no-trunc
--quiet -q
@ -1349,6 +1366,8 @@ _podman_logs() {
local boolean_options="
--follow
-f
--help
-h
--latest
-l
--timestamps
@ -1374,7 +1393,10 @@ _podman_pull() {
--signature-policy
"
local boolean_options="
--all-tags -a
--all-tags
-a
--help
-h
--quiet
-q
--tls-verify
@ -1390,6 +1412,8 @@ _podman_search() {
--limit
"
local boolean_options="
--help
-h
--no-trunc
"
_complete_ "$options_with_args" "$boolean_options"
@ -1403,10 +1427,10 @@ _podman_umount() {
local boolean_options="
--all
-a
--force
-f
--help
-h
--force
-f
"
local options_with_args="
"
@ -1447,6 +1471,8 @@ _podman_mount() {
_podman_push() {
local boolean_options="
--compress
--help
-h
--quiet
-q
--remove-signatures
@ -1554,6 +1580,7 @@ _podman_container_run() {
local boolean_options="
--disable-content-trust=false
--help
-h
--init
--interactive -i
--oom-kill-disable
@ -1584,6 +1611,7 @@ _podman_container_run() {
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
return
;;
*)
__podman_complete_images --id
@ -1770,8 +1798,16 @@ _podman_restart() {
--timeout -t
"
local boolean_options="
--all
-a
--help
-h
--latest
-l"
-l
--running
--timeout
-t
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
@ -1788,6 +1824,8 @@ _podman_rm() {
-a
--force
-f
--help
-h
--latest
-l
--volumes
@ -1811,12 +1849,12 @@ _podman_rm() {
_podman_rmi() {
local boolean_options="
--help
-h
--all
-a
--force
-f
-a
--all
--help
-h
"
case "$cur" in
@ -1831,9 +1869,10 @@ _podman_rmi() {
_podman_stats() {
local boolean_options="
--help
--all
-a
--help
-h
--no-stream
--format
--no-reset
@ -1853,6 +1892,8 @@ _podman_tag() {
local options_with_args="
"
local boolean_options="
--help
-h
"
case "$cur" in
-*)
@ -1900,10 +1941,15 @@ _podman_top() {
}
_podman_version() {
local options_with_args="
"
local boolean_options="
--help
-h
"
local options_with_args="
--format
"
local all_options="$options_with_args $boolean_options"
_complete_ "$options_with_args" "$boolean_options"
}
@ -1914,6 +1960,8 @@ _podman_save() {
"
local boolean_options="
--compress
--help
-h
-q
--quiet
"
@ -1929,8 +1977,13 @@ _podman_save() {
}
_podman_pause() {
local boolean_options="
--all
-a
--help
-h
"
local options_with_args="
--help -h
"
local boolean_options=""
case "$cur" in
@ -1945,13 +1998,15 @@ _podman_pause() {
_podman_port() {
local options_with_args="
--help -h
"
local boolean_options="
--all
-a
--help
-h
-l
--latest"
--latest
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
@ -1975,9 +2030,10 @@ _podman_ps() {
"
local boolean_options="
--all -a
--help -h
--latest -l
--no-trunc
--pod
--pod -p
--quiet -q
--size -s
--namespace --ns
@ -1991,10 +2047,10 @@ _podman_start() {
"
local boolean_options="
--attach
-a
-h
--help
-a
--attach
-i
--interactive
--latest
@ -2017,8 +2073,11 @@ _podman_stop() {
local boolean_options="
--all
-a
-h
--help
--latest
-l"
-l
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
@ -2030,10 +2089,14 @@ _podman_stop() {
}
_podman_unpause() {
local options_with_args="
--help -h
local boolean_options="
--all
-a
--help
-h
"
local options_with_args="
"
local boolean_options=""
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
@ -2046,10 +2109,12 @@ _podman_unpause() {
_podman_varlink() {
local options_with_args="
--help -h
--timeout -t
"
local boolean_options=""
local boolean_options="
--help
-h
"
_complete_ "$options_with_args" "$boolean_options"
}
@ -2061,7 +2126,8 @@ _podman_wait() {
-i
-l
--interval
--latest"
--latest
"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
@ -2095,6 +2161,8 @@ _podman_load() {
--signature-policy
"
local boolean_options="
--help
-h
--quiet
-q
"
@ -2159,20 +2227,48 @@ _podman_container_runlabel() {
esac
}
_podman_container_exists() {
local options_with_args="
"
local boolean_options="
"
}
_podman_pod_exists() {
local options_with_args="
"
local boolean_options="
"
}
_podman_image_exists() {
local options_with_args="
"
local boolean_options="
"
}
_podman_pod_create() {
local options_with_args="
--cgroup-parent
--infra-command
--infra-image
--share
--podidfile
--label-file
--label
-l
--name
--podidfile
--publish
-p
--share
"
local boolean_options="
--help
-h
--infra
"
_complete_ "$options_with_args" "$boolean_options"
@ -2185,6 +2281,8 @@ _podman_pod_kill() {
local boolean_options="
--all
-a
--help
-h
--signal
-s
--latest
@ -2214,6 +2312,8 @@ __podman_pod_ps() {
--ctr-ids
--ctr-names
--ctr-status
--help
-h
-q
--quiet
--no-trunc
@ -2243,6 +2343,8 @@ _podman_pod_restart() {
local boolean_options="
--all
-a
--help
-h
--latest
-l
"
@ -2264,6 +2366,8 @@ _podman_pod_rm() {
local boolean_options="
-a
--all
--help
-h
-f
--force
--latest
@ -2287,6 +2391,8 @@ _podman_pod_start() {
local boolean_options="
--all
-a
--help
-h
--latest
-l
"
@ -2309,6 +2415,8 @@ _podman_pod_stop() {
--all
-a
--cleanup
--help
-h
--latest
-l
"
@ -2330,6 +2438,8 @@ _podman_pod_pause() {
local boolean_options="
--all
-a
--help
-h
--latest
-l
"
@ -2351,6 +2461,8 @@ _podman_pod_unpause() {
local boolean_options="
--all
-a
--help
-h
--latest
-l
"
@ -2411,8 +2523,10 @@ _podman_podman() {
--namespace
"
local boolean_options="
--help -h
--version -v
--help
-h
--version
-v
--syslog
"
commands="

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
"
# TODO: Skip building images if $CIRRUS_BRANCH =~ "master" and
# commit message of $CIRRUS_CHANGE_IN_REPO contains a magic word
# produced by 'commit_and_create_upstream_pr.sh' script (see .cirrus.yml)
show_env_vars
# Everything here is running on the 'image-builder-image' GCE image

View File

@ -106,7 +106,10 @@ ircmsg() {
SCRIPT="$GOSRC/$SCRIPT_BASE/podbot.py"
NICK="podbot_$CIRRUS_TASK_ID"
NICK="${NICK:0:15}" # Any longer will break things
set +e
$SCRIPT $NICK $1
echo "Ignoring exit($?)"
set -e
}
# Run sudo in directory with GOPATH set
@ -117,7 +120,6 @@ cdsudo() {
sudo --preserve-env=GOPATH --non-interactive bash -c "$CMD"
}
# Helper/wrapper script to only show stderr/stdout on non-zero exit
install_ooe() {
req_env_var "SCRIPT_BASE $SCRIPT_BASE"
@ -155,6 +157,19 @@ install_cni_plugins() {
sudo cp bin/* /usr/libexec/cni
}
install_runc_from_git(){
wd=$(pwd)
DEST="$GOPATH/src/github.com/opencontainers/runc"
rm -rf "$DEST"
ooe.sh git clone https://github.com/opencontainers/runc.git "$DEST"
cd "$DEST"
ooe.sh git fetch origin --tags
ooe.sh git checkout -q "$RUNC_COMMIT"
ooe.sh make static BUILDTAGS="seccomp selinux"
sudo install -m 755 runc /usr/bin/runc
cd $wd
}
install_runc(){
OS_RELEASE_ID=$(os_release_id)
echo "Installing RunC from commit $RUNC_COMMIT"
@ -177,14 +192,7 @@ install_runc(){
cd "$GOPATH/src/github.com/containers/libpod"
ooe.sh sudo make install.libseccomp.sudo
fi
DEST="$GOPATH/src/github.com/opencontainers/runc"
rm -rf "$DEST"
ooe.sh git clone https://github.com/opencontainers/runc.git "$DEST"
cd "$DEST"
ooe.sh git fetch origin --tags
ooe.sh git checkout -q "$RUNC_COMMIT"
ooe.sh make static BUILDTAGS="seccomp selinux"
sudo install -m 755 runc /usr/bin/runc
install_runc_from_git
}
install_buildah() {
@ -263,7 +271,7 @@ install_varlink(){
_finalize(){
echo "Removing leftover giblets from cloud-init"
cd /
sudo rm -rf /var/lib/cloud
sudo rm -rf /var/lib/cloud/instance?
sudo rm -rf /root/.ssh/*
sudo rm -rf /home/*
}

View File

@ -21,8 +21,7 @@ install_ooe
export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT
# breaks networking on f28/29 in GCE
# ooe.sh sudo dnf update -y
ooe.sh sudo dnf update -y
ooe.sh sudo dnf install -y \
atomic-registries \

View File

@ -21,10 +21,13 @@ install_ooe
export GOPATH="$(mktemp -d)"
trap "sudo rm -rf $GOPATH" EXIT
ooe.sh sudo apt-get -qq update
ooe.sh sudo apt-get -qq update # sometimes it needs to get it twice :S
ooe.sh sudo apt-get -qq upgrade
ooe.sh sudo apt-get -qq install --no-install-recommends \
export DEBIAN_FRONTEND=noninteractive
# Try twice as workaround for minor networking problems
echo "Updating system and installing package dependencies"
ooe.sh sudo -E apt-get -qq update || sudo -E apt-get -qq update
ooe.sh sudo -E apt-get -qq upgrade || sudo -E apt-get -qq upgrade
ooe.sh sudo -E apt-get -qq install --no-install-recommends \
apparmor \
autoconf \
automake \

View File

@ -53,6 +53,8 @@ then
# Some setup needs to vary between distros
case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
ubuntu-18)
# Always install runc on Ubuntu
install_runc_from_git
envstr='export BUILDTAGS="seccomp $($GOSRC/hack/btrfs_tag.sh) $($GOSRC/hack/btrfs_installed_tag.sh) $($GOSRC/hack/ostree_tag.sh) varlink exclude_graphdriver_devicemapper"'
;;
fedora-28) ;& # Continue to the next item

33
contrib/cirrus/system_test.sh Executable file
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
python-podman:
PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py sdist bdist
.PHONY: lint
@ -16,6 +17,7 @@ integration:
.PHONY: install
install:
PODMAN_VERSION=$(PODMAN_VERSION) \
$(PYTHON) setup.py install --root ${DESTDIR}
.PHONY: upload

View File

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

View File

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

View File

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

View File

@ -152,7 +152,7 @@ class TestContainers(PodmanTestCase):
changes.append('WORKDIR=/data/application')
id = self.alpine_ctnr.commit(
'alpine3', author='Bozo the clown', changes=changes, pause=True)
'alpine3', author='Bozo the clown', change=changes, pause=True)
img = self.pclient.images.get(id)
self.assertIsNotNone(img)

View File

@ -102,7 +102,7 @@ class TestImages(PodmanTestCase):
def test_push(self):
path = '{}/alpine_push'.format(self.tmpdir)
target = 'dir:{}'.format(path)
self.alpine_image.push(target)
self.alpine_image.push(target, tlsverify=False)
self.assertTrue(os.path.isfile(os.path.join(path, 'manifest.json')))
self.assertTrue(os.path.isfile(os.path.join(path, 'version')))

View File

@ -52,7 +52,8 @@ class TestPodsCtnrs(PodmanTestCase):
status = FoldedString(pod.containersinfo[0]['status'])
self.assertIn(status, ('stopped', 'exited', 'running'))
killed = pod.kill()
# Pod kill is broken, so use stop for now
killed = pod.stop()
self.assertEqual(pod, killed)
def test_999_remove(self):

View File

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

View File

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

View File

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

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

View File

@ -2,7 +2,7 @@
import sys
import podman
from pypodman.lib import AbstractActionBase, BooleanAction
from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction
class Commit(AbstractActionBase):
@ -12,7 +12,9 @@ class Commit(AbstractActionBase):
def subparser(cls, parent):
"""Add Commit command to parent parser."""
parser = parent.add_parser(
'commit', help='create image from container')
'commit',
help='create image from container',
)
parser.add_argument(
'--author',
help='Set the author for the committed image',
@ -20,11 +22,7 @@ class Commit(AbstractActionBase):
parser.add_argument(
'--change',
'-c',
choices=('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR'),
action='append',
type=str.upper,
help='Apply the following possible changes to the created image',
action=ChangeAction,
)
parser.add_argument(
'--format',
@ -32,7 +30,8 @@ class Commit(AbstractActionBase):
choices=('oci', 'docker'),
default='oci',
type=str.lower,
help='Set the format of the image manifest and metadata',
help='Set the format of the image manifest and metadata.'
' (Ignored.)',
)
parser.add_argument(
'--iidfile',
@ -42,7 +41,8 @@ class Commit(AbstractActionBase):
parser.add_argument(
'--message',
'-m',
help='Set commit message for committed image',
help='Set commit message for committed image'
' (Only on docker images.)',
)
parser.add_argument(
'--pause',
@ -69,27 +69,11 @@ class Commit(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='commit')
def __init__(self, args):
"""Construct Commit class."""
if not args.container:
raise ValueError('You must supply one container id'
' or name to be used as source.')
if not args.image:
raise ValueError('You must supply one image id'
' or name to be created.')
super().__init__(args)
# used only on client
del self.opts['image']
del self.opts['container']
def commit(self):
"""Create image from container."""
try:
try:
ctnr = self.client.containers.get(self._args.container[0])
ident = ctnr.commit(**self.opts)
print(ident)
except podman.ContainerNotFound as e:
sys.stdout.flush()
print(
@ -97,6 +81,17 @@ class Commit(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
else:
ident = ctnr.commit(
self.opts['image'][0],
change=self.opts.get('change', None),
message=self.opts.get('message', None),
pause=self.opts['pause'],
author=self.opts.get('author', None),
)
if not self.opts['quiet']:
print(ident)
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
@ -104,3 +99,4 @@ class Commit(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
return 0

View File

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

View File

@ -12,13 +12,16 @@ class Export(AbstractActionBase):
def subparser(cls, parent):
"""Add Export command to parent parser."""
parser = parent.add_parser(
'export', help='export container to tarball')
'export',
help='export container to tarball',
)
parser.add_argument(
'--output',
'-o',
metavar='PATH',
nargs=1,
help='Write to a file',
required=True,
help='Write to this file on host',
)
parser.add_argument(
'container',
@ -27,23 +30,11 @@ class Export(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='export')
def __init__(self, args):
"""Construct Export class."""
if not args.container:
raise ValueError('You must supply one container id'
' or name to be used as source.')
if not args.output:
raise ValueError('You must supply one filename'
' to be created as tarball using --output.')
super().__init__(args)
def export(self):
"""Create tarball from container filesystem."""
try:
try:
ctnr = self.client.containers.get(self._args.container[0])
ctnr.export(self._args.output[0])
except podman.ContainerNotFound as e:
sys.stdout.flush()
print(
@ -51,6 +42,8 @@ class Export(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
else:
ctnr.export(self._args.output[0])
except podman.ErrorOccurred as e:
sys.stdout.flush()
print(
@ -58,3 +51,4 @@ class Export(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
return 0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.
"""
import argparse
import copy
import os
# API defined by argparse.Action shut up pylint
# API defined by argparse.Action therefore shut up pylint
# pragma pylint: disable=redefined-builtin
# pragma pylint: disable=too-few-public-methods
# pragma pylint: disable=too-many-arguments
@ -36,7 +37,7 @@ class BooleanAction(argparse.Action):
const=None,
default=None,
type=None,
choices=('True', 'False'),
choices=None,
required=False,
help=None,
metavar='{True,False}'):
@ -58,11 +59,58 @@ class BooleanAction(argparse.Action):
try:
val = BooleanValidate()(values)
except ValueError:
parser.error('{} must be True or False.'.format(self.dest))
parser.error('"{}" must be True or False.'.format(option_string))
else:
setattr(namespace, self.dest, val)
class ChangeAction(argparse.Action):
"""Convert and validate change argument."""
def __init__(self,
option_strings,
dest,
nargs=None,
const=None,
default=[],
type=None,
choices=None,
required=False,
help=None,
metavar='OPT=VALUE'):
"""Create ChangeAction object."""
help = (help or '') + ('Apply change(s) to the new image.'
' May be given multiple times.')
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None):
"""Convert and Validate input."""
items = getattr(namespace, self.dest, None) or []
items = copy.copy(items)
choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR')
opt, val = values.split('=', 1)
if opt not in choices:
parser.error('Option "{}" is not supported by argument "{}",'
' valid options are: {}'.format(
opt, option_string, ', '.join(choices)))
items.append(values)
setattr(namespace, self.dest, items)
class UnitAction(argparse.Action):
"""Validate number given is positive integer, with optional suffix."""
@ -78,8 +126,8 @@ class UnitAction(argparse.Action):
help=None,
metavar='UNIT'):
"""Create UnitAction object."""
help = (help or metavar or dest
) + ' (format: <number>[<unit>], where unit = b, k, m or g)'
help = (help or metavar or dest)\
+ ' (format: <number>[<unit>], where unit = b, k, m or g)'
super().__init__(
option_strings=option_strings,
dest=dest,
@ -99,15 +147,15 @@ class UnitAction(argparse.Action):
except ValueError:
if not values[:-1].isdigit():
msg = ('{} must be a positive integer,'
' with optional suffix').format(self.dest)
' with optional suffix').format(option_string)
parser.error(msg)
if not values[-1] in ('b', 'k', 'm', 'g'):
msg = '{} only supports suffices of: b, k, m, g'.format(
self.dest)
option_string)
parser.error(msg)
else:
if val <= 0:
msg = '{} must be a positive integer'.format(self.dest)
msg = '{} must be a positive integer'.format(option_string)
parser.error(msg)
setattr(namespace, self.dest, values)
@ -125,19 +173,16 @@ class PositiveIntAction(argparse.Action):
type=int,
choices=None,
required=False,
help=None,
help='Must be a positive integer.',
metavar=None):
"""Create PositiveIntAction object."""
self.message = '{} must be a positive integer'.format(dest)
help = help or self.message
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
type=int,
type=type,
choices=choices,
required=required,
help=help,
@ -149,7 +194,8 @@ class PositiveIntAction(argparse.Action):
setattr(namespace, self.dest, values)
return
parser.error(self.message)
msg = '{} must be a positive integer'.format(option_string)
parser.error(msg)
class PathAction(argparse.Action):

View File

@ -97,6 +97,8 @@ class PodmanArgumentParser(argparse.ArgumentParser):
actions_parser = self.add_subparsers(
dest='subparser_name', help='commands')
# For create/exec/run: don't process options intended for subcommand
actions_parser.REMAINDER = argparse.REMAINDER
# import buried here to prevent import loops
import pypodman.lib.actions # pylint: disable=cyclic-import
@ -152,7 +154,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr(
'run_dir',
getattr(args, 'run_dir')
or os.environ.get('RUN_DIR')
or os.environ.get('PODMAN_RUN_DIR')
or config['default'].get('run_dir')
or str(Path(args.xdg_runtime_dir, 'pypodman'))
) # yapf: disable
@ -161,23 +163,24 @@ class PodmanArgumentParser(argparse.ArgumentParser):
args,
'host',
getattr(args, 'host')
or os.environ.get('HOST')
or os.environ.get('PODMAN_HOST')
or config['default'].get('host')
) # yapf:disable
reqattr(
'username',
getattr(args, 'username')
or os.environ.get('PODMAN_USER')
or config['default'].get('username')
or os.environ.get('USER')
or os.environ.get('LOGNAME')
or config['default'].get('username')
or getpass.getuser()
) # yapf:disable
reqattr(
'port',
getattr(args, 'port')
or os.environ.get('PORT')
or os.environ.get('PODMAN_PORT')
or config['default'].get('port', None)
or 22
) # yapf:disable
@ -185,7 +188,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr(
'remote_socket_path',
getattr(args, 'remote_socket_path')
or os.environ.get('REMOTE_SOCKET_PATH')
or os.environ.get('PODMAN_REMOTE_SOCKET_PATH')
or config['default'].get('remote_socket_path')
or '/run/podman/io.podman'
) # yapf:disable
@ -193,7 +196,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
reqattr(
'log_level',
getattr(args, 'log_level')
or os.environ.get('LOG_LEVEL')
or os.environ.get('PODMAN_LOG_LEVEL')
or config['default'].get('log_level')
or logging.WARNING
) # yapf:disable
@ -202,7 +205,7 @@ class PodmanArgumentParser(argparse.ArgumentParser):
args,
'identity_file',
getattr(args, 'identity_file')
or os.environ.get('IDENTITY_FILE')
or os.environ.get('PODMAN_IDENTITY_FILE')
or config['default'].get('identity_file')
or os.path.expanduser('~{}/.ssh/id_dsa'.format(args.username))
) # yapf:disable

View File

@ -1,8 +1,23 @@
"""Report Manager."""
import string
import sys
from collections import namedtuple
class ReportFormatter(string.Formatter):
"""Custom formatter to default missing keys to '<none>'."""
def get_value(self, key, args, kwargs):
"""Map missing key to value '<none>'."""
try:
if isinstance(key, int):
return args[key]
else:
return kwargs[key]
except KeyError:
return '<none>'
class ReportColumn(namedtuple('ReportColumn', 'key display width default')):
"""Hold attributes of output column."""
@ -26,18 +41,24 @@ class Report():
"""
self._columns = columns
self._file = file
self._format_string = None
self._formatter = ReportFormatter()
self._heading = heading
self.epilog = epilog
self._format = None
def row(self, **fields):
"""Print row for report."""
if self._heading:
hdrs = {k: v.display for (k, v) in self._columns.items()}
print(self._format.format(**hdrs), flush=True, file=self._file)
print(
self._formatter.format(self._format_string, **hdrs),
flush=True,
file=self._file,
)
self._heading = False
fields = {k: str(v) for k, v in fields.items()}
print(self._format.format(**fields))
print(self._formatter.format(self._format_string, **fields))
def __enter__(self):
"""Return `self` upon entering the runtime context."""
@ -63,4 +84,4 @@ class Report():
display_len = info.width
fmt.append('{{{0}:{1}.{1}}}'.format(key, display_len))
self._format = ' '.join(fmt)
self._format_string = ' '.join(fmt)

View File

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

View File

@ -24,6 +24,18 @@ libpod to manage containers.
**cgroup_manager**=""
Specify the CGroup Manager to use; valid values are "systemd" and "cgroupfs"
**hooks_dir**=["*path*", ...]
Each `*.json` file in the path configures a hook for Podman containers. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated.
Paths listed later in the array higher precedence (`oci-hooks(5)` discusses directory precedence).
For the annotation conditions, libpod uses any annotations set in the generated OCI configuration.
For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered.
If `hooks_dir` is unset for root callers, Podman and libpod will currently default to `/usr/share/containers/oci/hooks.d` and `/etc/containers/oci/hooks.d` in order of increasing precedence. Using these defaults is deprecated, and callers should migrate to explicitly setting `hooks_dir`.
**static_dir**=""
Directory for persistent libpod files (database, etc)
By default this will be configured relative to where containers/storage

View File

@ -171,7 +171,7 @@ value can be entered. The password is entered without echo.
**--disable-content-trust**
This is a Docker specific option to disable image verification to a Docker
registry and is not supported by Buildah. This flag is a NOOP and provided
registry and is not supported by Podman. This flag is a NOOP and provided
soley for scripting compatibility.
**--file, -f** *Dockerfile*

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
for further analysis.
**--all, -a**
Checkpoint all running containers.
**--latest, -l**
Instead of providing the container name or ID, checkpoint the last created container.
**--leave-running, -R**
Leave the container running after checkpointing instead of stopping it.
**--tcp-established**
Checkpoint a container with established TCP connections. If the checkpoint
image contains established TCP connections, this options is required during
restore. Defaults to not checkpointing containers with established TCP
connections.
## EXAMPLE
podman container checkpoint mywebserver

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
again.
**--all, -a**
Restore all checkpointed containers.
**--latest, -l**
Instead of providing the container name or ID, restore the last created container.
**--tcp-established**
Restore a container with established TCP connections. If the checkpoint image
contains established TCP connections, this option is required during restore.
If the checkpoint image does not contain established TCP connections this
option is ignored. Defaults to not restoring containers with established TCP
connections.
## EXAMPLE
podman container restore mywebserver

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. |
| diff | [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. |
| exec | [podman-exec(1)](podman-exec.1.md) | Execute a command in a running container. |
| exists | [podman-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage |
| export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. |
| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. |
| kill | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. |
@ -38,7 +39,6 @@ The container command allows you to manage containers
| stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. |
| top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. |
| umount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| unmount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |

View File

@ -66,7 +66,7 @@ Write the container ID to the file
**--conmon-pidfile**=""
Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers.
Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers.
**--cpu-count**=*0*
@ -321,13 +321,13 @@ Not implemented
**--log-driver**="*json-file*"
Logging driver for the container. Default is defined by daemon `--log-driver` flag.
**Warning**: the `podman logs` command works only for the `json-file` and
`journald` logging drivers.
Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility.
**--log-opt**=[]
Logging driver specific options.
Logging driver specific options. Used to set the path to the container log file. For example:
`--log-opt path=/var/log/container/mycontainer.json`
**--mac-address**=""
@ -414,7 +414,7 @@ UUID short identifier (“f78375b1c487”)
Name (“jonah”)
podman generates a UUID for each container, and if a name is not assigned
to the container with **--name** then the daemon will also generate a random
to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
@ -426,7 +426,8 @@ Set the Network mode for the container
'container:<name|id>': reuse another container's network stack
'host': use the podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
'<network-name>|<network-id>': connect to a user-defined network
'ns:<path>' path to a network namespace to join
'ns:<path>': path to a network namespace to join
'slirp4netns': use slirp4netns to create a user network stack. This is the default for rootless containers
**--network-alias**=[]
@ -454,7 +455,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain
**--pod**=""
Run container in an existing pod
Run container in an existing pod. If you want podman to make the pod for you, preference the pod name with `new:`.
To make a pod with more granular options, use the `podman pod create` command before creating a container.
**--privileged**=*true*|*false*
@ -465,9 +467,10 @@ By default, podman containers are
This is because by default a container is not allowed to access any devices.
A “privileged” container is given access to all devices.
When the operator executes **podman run --privileged**, podman enables access
to all devices on the host as well as set turn off most of the security measures
protecting the host from the container.
When the operator executes a privileged container, podman enables access
to all devices on the host, turns off graphdriver mount options, as well as
turning off most of the security measures protecting the host from the
container.
**-p**, **--publish**=[]

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 |
| -------- | ----------------------------------------- | ------------------------------------------------------------------------------ |
| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. |
| exists | [podman-exists(1)](podman-image-exists.1.md) | Check if a image exists in local storage |
| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. |
| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. |
| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a image or image's configuration. |

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More