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

|

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

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

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