summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--.github/ISSUE_TEMPLATE.md2
-rwxr-xr-xAPI.md92
-rw-r--r--README.md31
-rw-r--r--cmd/podman/attach.go2
-rw-r--r--cmd/podman/build.go29
-rw-r--r--cmd/podman/checkpoint.go9
-rw-r--r--cmd/podman/cleanup.go8
-rw-r--r--cmd/podman/cliconfig/config.go4
-rw-r--r--cmd/podman/commands.go24
-rw-r--r--cmd/podman/commands_remoteclient.go2
-rw-r--r--cmd/podman/common.go23
-rw-r--r--cmd/podman/containers_prune.go2
-rw-r--r--cmd/podman/cp.go34
-rw-r--r--cmd/podman/create.go11
-rw-r--r--cmd/podman/errors.go23
-rw-r--r--cmd/podman/exec.go2
-rw-r--r--cmd/podman/exists.go7
-rw-r--r--cmd/podman/export.go2
-rw-r--r--cmd/podman/formats/formats.go5
-rw-r--r--cmd/podman/generate.go13
-rw-r--r--cmd/podman/generate_kube.go4
-rw-r--r--cmd/podman/history.go2
-rw-r--r--cmd/podman/image.go2
-rw-r--r--cmd/podman/imagefilters/filters.go2
-rw-r--r--cmd/podman/images.go6
-rw-r--r--cmd/podman/images_prune.go2
-rw-r--r--cmd/podman/import.go2
-rw-r--r--cmd/podman/info.go2
-rw-r--r--cmd/podman/inspect.go4
-rw-r--r--cmd/podman/kill.go8
-rw-r--r--cmd/podman/libpodruntime/runtime.go9
-rw-r--r--cmd/podman/load.go54
-rw-r--r--cmd/podman/logs.go5
-rw-r--r--cmd/podman/main.go19
-rw-r--r--cmd/podman/mount.go4
-rw-r--r--cmd/podman/play_kube.go2
-rw-r--r--cmd/podman/pod.go16
-rw-r--r--cmd/podman/pod_create.go79
-rw-r--r--cmd/podman/pod_inspect.go11
-rw-r--r--cmd/podman/pod_kill.go52
-rw-r--r--cmd/podman/pod_pause.go52
-rw-r--r--cmd/podman/pod_ps.go48
-rw-r--r--cmd/podman/pod_restart.go52
-rw-r--r--cmd/podman/pod_rm.go46
-rw-r--r--cmd/podman/pod_start.go50
-rw-r--r--cmd/podman/pod_stats.go1
-rw-r--r--cmd/podman/pod_stop.go55
-rw-r--r--cmd/podman/pod_unpause.go52
-rw-r--r--cmd/podman/port.go8
-rw-r--r--cmd/podman/ps.go1
-rw-r--r--cmd/podman/pull.go11
-rw-r--r--cmd/podman/push.go2
-rw-r--r--cmd/podman/restart.go4
-rw-r--r--cmd/podman/restore.go8
-rw-r--r--cmd/podman/rm.go28
-rw-r--r--cmd/podman/rmi.go56
-rw-r--r--cmd/podman/runlabel.go4
-rw-r--r--cmd/podman/save.go120
-rw-r--r--cmd/podman/search.go234
-rw-r--r--cmd/podman/shared/pod.go4
-rw-r--r--cmd/podman/start.go1
-rw-r--r--cmd/podman/stats.go4
-rw-r--r--cmd/podman/stop.go8
-rw-r--r--cmd/podman/system_prune.go2
-rw-r--r--cmd/podman/system_renumber.go49
-rw-r--r--cmd/podman/tag.go2
-rw-r--r--cmd/podman/top.go1
-rw-r--r--cmd/podman/umount.go7
-rw-r--r--cmd/podman/utils.go18
-rw-r--r--cmd/podman/varlink/io.podman.varlink56
-rw-r--r--cmd/podman/volume_create.go2
-rw-r--r--cmd/podman/volume_inspect.go2
-rw-r--r--cmd/podman/volume_ls.go2
-rw-r--r--cmd/podman/volume_prune.go2
-rw-r--r--cmd/podman/volume_rm.go6
-rw-r--r--cmd/podman/wait.go5
-rwxr-xr-xcontrib/cirrus/integration_test.sh2
-rw-r--r--contrib/cirrus/lib.sh2
-rw-r--r--contrib/cirrus/packer/fedora_setup.sh1
-rw-r--r--contrib/cirrus/packer/ubuntu_setup.sh9
-rwxr-xr-xcontrib/cirrus/setup_environment.sh5
-rw-r--r--docs/podman-attach.1.md2
-rw-r--r--docs/podman-build.1.md6
-rw-r--r--docs/podman-container-checkpoint.1.md2
-rw-r--r--docs/podman-container-cleanup.1.md5
-rw-r--r--docs/podman-container-restore.1.md2
-rw-r--r--docs/podman-diff.1.md6
-rw-r--r--docs/podman-exec.1.md2
-rw-r--r--docs/podman-inspect.1.md2
-rw-r--r--docs/podman-kill.1.md2
-rw-r--r--docs/podman-load.1.md4
-rw-r--r--docs/podman-logs.1.md11
-rw-r--r--docs/podman-mount.1.md2
-rw-r--r--docs/podman-pod-inspect.1.md1
-rw-r--r--docs/podman-pod-kill.1.md2
-rw-r--r--docs/podman-pod-pause.1.md2
-rw-r--r--docs/podman-pod-ps.1.md2
-rw-r--r--docs/podman-pod-restart.1.md6
-rw-r--r--docs/podman-pod-rm.1.md2
-rw-r--r--docs/podman-pod-start.1.md2
-rw-r--r--docs/podman-pod-stop.1.md2
-rw-r--r--docs/podman-pod-top.1.md2
-rw-r--r--docs/podman-pod-unpause.1.md2
-rw-r--r--docs/podman-port.1.md10
-rw-r--r--docs/podman-ps.1.md2
-rw-r--r--docs/podman-restart.1.md2
-rw-r--r--docs/podman-rm.1.md15
-rw-r--r--docs/podman-rmi.1.md10
-rw-r--r--docs/podman-run.1.md14
-rw-r--r--docs/podman-start.1.md1
-rw-r--r--docs/podman-stats.1.md2
-rw-r--r--docs/podman-stop.1.md2
-rw-r--r--docs/podman-system-renumber.1.md29
-rw-r--r--docs/podman-top.1.md2
-rw-r--r--docs/podman-umount.1.md2
-rw-r--r--docs/podman-wait.1.md4
-rwxr-xr-xhack/tree_status.sh2
-rw-r--r--libpod/boltdb_state.go92
-rw-r--r--libpod/boltdb_state_internal.go7
-rw-r--r--libpod/container_commit.go2
-rw-r--r--libpod/container_internal.go23
-rw-r--r--libpod/container_internal_linux.go43
-rw-r--r--libpod/image/image.go69
-rw-r--r--libpod/image/search.go277
-rw-r--r--libpod/image/utils.go26
-rw-r--r--libpod/in_memory_state.go64
-rw-r--r--libpod/lock/in_memory_locks.go11
-rw-r--r--libpod/lock/lock.go14
-rw-r--r--libpod/lock/shm/shm_lock.c36
-rw-r--r--libpod/lock/shm/shm_lock.go16
-rw-r--r--libpod/lock/shm/shm_lock.h1
-rw-r--r--libpod/lock/shm/shm_lock_test.go28
-rw-r--r--libpod/lock/shm_lock_manager_linux.go7
-rw-r--r--libpod/lock/shm_lock_manager_unsupported.go5
-rw-r--r--libpod/options.go21
-rw-r--r--libpod/runtime.go43
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--libpod/runtime_img.go44
-rw-r--r--libpod/runtime_renumber.go57
-rw-r--r--libpod/runtime_volume.go13
-rw-r--r--libpod/runtime_volume_linux.go31
-rw-r--r--libpod/state.go24
-rw-r--r--libpod/state_test.go72
-rw-r--r--libpod/volume.go5
-rw-r--r--libpod/volume_internal.go3
-rw-r--r--pkg/adapter/client.go (renamed from libpod/adapter/client.go)0
-rw-r--r--pkg/adapter/containers_remote.go (renamed from libpod/adapter/containers_remote.go)31
-rw-r--r--pkg/adapter/images_remote.go (renamed from libpod/adapter/images_remote.go)0
-rw-r--r--pkg/adapter/info_remote.go (renamed from libpod/adapter/info_remote.go)0
-rw-r--r--pkg/adapter/pods.go323
-rw-r--r--pkg/adapter/pods_remote.go401
-rw-r--r--pkg/adapter/runtime.go (renamed from libpod/adapter/runtime.go)23
-rw-r--r--pkg/adapter/runtime_remote.go (renamed from libpod/adapter/runtime_remote.go)170
-rw-r--r--pkg/adapter/shortcuts/shortcuts.go27
-rw-r--r--pkg/adapter/volumes_remote.go (renamed from libpod/adapter/volumes_remote.go)0
-rw-r--r--pkg/rootless/rootless_linux.c63
-rw-r--r--pkg/spec/createconfig.go10
-rw-r--r--pkg/spec/spec.go58
-rw-r--r--pkg/varlinkapi/containers.go42
-rw-r--r--pkg/varlinkapi/images.go373
-rw-r--r--pkg/varlinkapi/pods.go56
-rw-r--r--test/e2e/libpod_suite_test.go4
-rw-r--r--test/e2e/load_test.go6
-rw-r--r--test/e2e/pod_start_test.go1
-rw-r--r--test/e2e/pod_stop_test.go1
-rw-r--r--test/e2e/rm_test.go5
-rw-r--r--test/e2e/rmi_test.go2
-rw-r--r--test/e2e/rootless_test.go45
-rw-r--r--test/e2e/run_networking_test.go39
-rw-r--r--test/e2e/save_test.go2
-rw-r--r--test/utils/podmantest_test.go2
-rw-r--r--test/utils/utils.go12
-rw-r--r--utils/utils.go30
-rw-r--r--vendor.conf13
-rw-r--r--vendor/github.com/VividCortex/ewma/LICENSE (renamed from vendor/github.com/mattn/go-runewidth/LICENSE)12
-rw-r--r--vendor/github.com/VividCortex/ewma/README.md140
-rw-r--r--vendor/github.com/VividCortex/ewma/ewma.go126
-rw-r--r--vendor/github.com/containers/buildah/buildah.go7
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/build.go19
-rw-r--r--vendor/github.com/containers/buildah/new.go68
-rw-r--r--vendor/github.com/containers/buildah/pkg/cli/common.go10
-rw-r--r--vendor/github.com/containers/buildah/pkg/parse/parse.go6
-rw-r--r--vendor/github.com/containers/buildah/pull.go117
-rw-r--r--vendor/github.com/containers/buildah/unshare/unshare.c140
-rw-r--r--vendor/github.com/containers/buildah/unshare/unshare.go6
-rw-r--r--vendor/github.com/containers/buildah/util/util.go56
-rw-r--r--vendor/github.com/containers/buildah/vendor.conf24
-rw-r--r--vendor/github.com/containers/image/copy/copy.go139
-rw-r--r--vendor/github.com/containers/image/vendor.conf4
-rw-r--r--vendor/github.com/containers/storage/containers.go3
-rw-r--r--vendor/github.com/containers/storage/containers_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/drivers/overlay/overlay.go2
-rw-r--r--vendor/github.com/containers/storage/errors.go2
-rw-r--r--vendor/github.com/containers/storage/images.go4
-rw-r--r--vendor/github.com/containers/storage/layers.go4
-rw-r--r--vendor/github.com/containers/storage/layers_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/lockfile.go52
-rw-r--r--vendor/github.com/containers/storage/lockfile_unix.go117
-rw-r--r--vendor/github.com/containers/storage/lockfile_windows.go5
-rw-r--r--vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/store.go95
-rw-r--r--vendor/github.com/mattn/go-isatty/LICENSE9
-rw-r--r--vendor/github.com/mattn/go-isatty/README.md50
-rw-r--r--vendor/github.com/mattn/go-isatty/doc.go2
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_appengine.go15
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_bsd.go18
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_linux.go18
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go19
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_others.go10
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_solaris.go16
-rw-r--r--vendor/github.com/mattn/go-isatty/isatty_windows.go94
-rw-r--r--vendor/github.com/mattn/go-runewidth/README.mkd27
-rw-r--r--vendor/github.com/mattn/go-runewidth/runewidth.go977
-rw-r--r--vendor/github.com/mattn/go-runewidth/runewidth_appengine.go8
-rw-r--r--vendor/github.com/mattn/go-runewidth/runewidth_js.go9
-rw-r--r--vendor/github.com/mattn/go-runewidth/runewidth_posix.go79
-rw-r--r--vendor/github.com/mattn/go-runewidth/runewidth_windows.go28
-rw-r--r--vendor/github.com/openshift/imagebuilder/README.md2
-rw-r--r--vendor/github.com/openshift/imagebuilder/builder.go31
-rw-r--r--vendor/github.com/openshift/imagebuilder/dispatchers.go13
-rw-r--r--vendor/github.com/vbauerster/mpb/LICENSE29
-rw-r--r--vendor/github.com/vbauerster/mpb/README.md117
-rw-r--r--vendor/github.com/vbauerster/mpb/bar.go455
-rw-r--r--vendor/github.com/vbauerster/mpb/bar_option.go124
-rw-r--r--vendor/github.com/vbauerster/mpb/cwriter/writer.go78
-rw-r--r--vendor/github.com/vbauerster/mpb/cwriter/writer_posix.go13
-rw-r--r--vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go77
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/counters.go206
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/decorator.go144
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/doc.go25
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/elapsed.go68
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/eta.go207
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/moving-average.go66
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/name.go45
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/percentage.go39
-rw-r--r--vendor/github.com/vbauerster/mpb/decor/speed.go270
-rw-r--r--vendor/github.com/vbauerster/mpb/doc.go6
-rw-r--r--vendor/github.com/vbauerster/mpb/internal/percentage.go10
-rw-r--r--vendor/github.com/vbauerster/mpb/internal/round.go49
-rw-r--r--vendor/github.com/vbauerster/mpb/options.go95
-rw-r--r--vendor/github.com/vbauerster/mpb/options_go1.7.go15
-rw-r--r--vendor/github.com/vbauerster/mpb/priority_queue.go40
-rw-r--r--vendor/github.com/vbauerster/mpb/progress.go251
-rw-r--r--vendor/github.com/vbauerster/mpb/progress_posix.go70
-rw-r--r--vendor/github.com/vbauerster/mpb/progress_windows.go43
-rw-r--r--vendor/github.com/vbauerster/mpb/proxyreader.go22
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/LICENSE12
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/README.md177
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/format.go125
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pb.go503
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go11
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go143
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go118
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pool.go104
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go45
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go29
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/reader.go25
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/runecount.go17
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go9
-rw-r--r--vendor/gopkg.in/cheggaaa/pb.v1/termios_sysv.go13
261 files changed, 7093 insertions, 3966 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index cb1279331..ad9edd404 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -29,7 +29,7 @@ env:
###
FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-7f4cd1f7"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-7f4cd1f7"
- UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-7f4cd1f7"
+ UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-84514d8b"
# RHEL_CACHE_IMAGE_NAME: "rhel-8-notready"
PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-7f4cd1f7"
# CENTOS_CACHE_IMAGE_NAME: "centos-7-notready"
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index a7663f3e3..2946f0b91 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -52,7 +52,7 @@ Briefly describe the problem you are having in a few paragraphs.
(paste your output here)
```
-**Output of `podman info`:**
+**Output of `podman info --debug`:**
```
(paste your output here)
diff --git a/API.md b/API.md
index 87cf6d242..c4f23c9bd 100755
--- a/API.md
+++ b/API.md
@@ -51,6 +51,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func GetPodStats(name: string) string, ContainerStats](#GetPodStats)
+[func GetPodsByContext(all: bool, latest: bool, args: []string) []string](#GetPodsByContext)
+
[func GetVersion() string, string, string, string, string, int](#GetVersion)
[func GetVolumes(args: []string, all: bool) Volume](#GetVolumes)
@@ -59,6 +61,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ImageExists(name: string) int](#ImageExists)
+[func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave)
+
[func ImagesPrune(all: bool) []string](#ImagesPrune)
[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage)
@@ -85,15 +89,19 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ListPods() ListPodData](#ListPods)
+[func LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) MoreResponse](#LoadImage)
+
[func MountContainer(name: string) string](#MountContainer)
[func PauseContainer(name: string) string](#PauseContainer)
[func PausePod(name: string) string](#PausePod)
-[func PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ?bool) string](#PullImage)
+[func PodStateData(name: string) string](#PodStateData)
-[func PushImage(name: string, tag: string, tlsverify: ?bool, signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) MoreResponse](#PushImage)
+[func PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ) MoreResponse](#PullImage)
+
+[func PushImage(name: string, tag: string, tlsverify: , signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) MoreResponse](#PushImage)
[func ReceiveFile(path: string, delete: bool) int](#ReceiveFile)
@@ -107,7 +115,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func RestartPod(name: string) string](#RestartPod)
-[func SearchImages(query: string, limit: int, tlsVerify: ?bool) ImageSearchResult](#SearchImages)
+[func SearchImages(query: string, limit: , tlsVerify: , filter: ImageSearchFilter) ImageSearchResult](#SearchImages)
[func SendFile(type: string, length: int) string](#SendFile)
@@ -163,6 +171,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type ImageHistory](#ImageHistory)
+[type ImageSaveOptions](#ImageSaveOptions)
+
+[type ImageSearchFilter](#ImageSearchFilter)
+
[type ImageSearchResult](#ImageSearchResult)
[type InfoDistribution](#InfoDistribution)
@@ -217,6 +229,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[error RuntimeError](#RuntimeError)
+[error VolumeNotFound](#VolumeNotFound)
+
## Methods
### <a name="BuildImage"></a>func BuildImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -523,6 +537,13 @@ $ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b
"pod": "7f62b508b6f12b11d8fe02e0db4de6b9e43a7d7699b33a4fc0d574f6e82b4ebd"
}
~~~
+### <a name="GetPodsByContext"></a>func GetPodsByContext
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method GetPodsByContext(all: [bool](https://godoc.org/builtin#bool), latest: [bool](https://godoc.org/builtin#bool), args: [[]string](#[]string)) [[]string](#[]string)</div>
+GetPodsByContext allows you to get a list pod ids depending on all, latest, or a list of
+pod names. The definition of latest pod means the latest by creation date. In a multi-
+user environment, results might differ from what you expect.
### <a name="GetVersion"></a>func GetVersion
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -554,6 +575,11 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "im
"exists": 1
}
~~~
+### <a name="ImageSave"></a>func ImageSave
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ImageSave(options: [ImageSaveOptions](#ImageSaveOptions)) [MoreResponse](#MoreResponse)</div>
+ImageSave allows you to save an image from the local image storage to a tarball
### <a name="ImagesPrune"></a>func ImagesPrune
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -714,6 +740,11 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ListPods
]
}
~~~
+### <a name="LoadImage"></a>func LoadImage
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method LoadImage(name: [string](https://godoc.org/builtin#string), inputFile: [string](https://godoc.org/builtin#string), quiet: [bool](https://godoc.org/builtin#bool), deleteFile: [bool](https://godoc.org/builtin#bool)) [MoreResponse](#MoreResponse)</div>
+LoadImage allows you to load an image into local storage from a tarball.
### <a name="MountContainer"></a>func MountContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -750,23 +781,23 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "fooba
"pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
}
~~~
+### <a name="PodStateData"></a>func PodStateData
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method PodStateData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+PodStateData returns inspectr level information of a given pod in string form. This call is for
+development of Podman only and generally should not be used.
### <a name="PullImage"></a>func PullImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method PullImage(name: [string](https://godoc.org/builtin#string), certDir: [string](https://godoc.org/builtin#string), creds: [string](https://godoc.org/builtin#string), signaturePolicy: [string](https://godoc.org/builtin#string), tlsVerify: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
-PullImage pulls an image from a repository to local storage. After the pull is successful, the ID of the image
-is returned.
-#### Example
-~~~
-$ varlink call -m unix:/run/podman/io.podman/io.podman.PullImage '{"name": "registry.fedoraproject.org/fedora"}'
-{
- "id": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e"
-}
-~~~
+method PullImage(name: [string](https://godoc.org/builtin#string), certDir: [string](https://godoc.org/builtin#string), creds: [string](https://godoc.org/builtin#string), signaturePolicy: [string](https://godoc.org/builtin#string), tlsVerify: [](#)) [MoreResponse](#MoreResponse)</div>
+PullImage pulls an image from a repository to local storage. After a successful pull, the image id and logs
+are returned as a [MoreResponse](#MoreResponse). This connection also will handle a WantsMores request to send
+status as it occurs.
### <a name="PushImage"></a>func PushImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method PushImage(name: [string](https://godoc.org/builtin#string), tag: [string](https://godoc.org/builtin#string), tlsverify: [bool](https://godoc.org/builtin#bool), signaturePolicy: [string](https://godoc.org/builtin#string), creds: [string](https://godoc.org/builtin#string), certDir: [string](https://godoc.org/builtin#string), compress: [bool](https://godoc.org/builtin#bool), format: [string](https://godoc.org/builtin#string), removeSignatures: [bool](https://godoc.org/builtin#bool), signBy: [string](https://godoc.org/builtin#string)) [MoreResponse](#MoreResponse)</div>
+method PushImage(name: [string](https://godoc.org/builtin#string), tag: [string](https://godoc.org/builtin#string), tlsverify: [](#), signaturePolicy: [string](https://godoc.org/builtin#string), creds: [string](https://godoc.org/builtin#string), certDir: [string](https://godoc.org/builtin#string), compress: [bool](https://godoc.org/builtin#bool), format: [string](https://godoc.org/builtin#string), removeSignatures: [bool](https://godoc.org/builtin#bool), signBy: [string](https://godoc.org/builtin#string)) [MoreResponse](#MoreResponse)</div>
PushImage takes three input arguments: the name or ID of an image, the fully-qualified destination name of the image,
and a boolean as to whether tls-verify should be used (with false disabling TLS, not affecting the default behavior).
It will return an [ImageNotFound](#ImageNotFound) error if
@@ -851,7 +882,7 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.RestartPod '{"name": "135
### <a name="SearchImages"></a>func SearchImages
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method SearchImages(query: [string](https://godoc.org/builtin#string), limit: [](#)) [ImageSearchResult](#ImageSearchResult)</div>
+method SearchImages(query: [string](https://godoc.org/builtin#string), limit: [](#), tlsVerify: [](#), filter: [ImageSearchFilter](#ImageSearchFilter)) [ImageSearchResult](#ImageSearchResult)</div>
SearchImages searches available registries for images that contain the
contents of "query" in their name. If "limit" is given, limits the amount of
search results per registry.
@@ -1414,6 +1445,32 @@ tags [[]string](#[]string)
size [int](https://godoc.org/builtin#int)
comment [string](https://godoc.org/builtin#string)
+### <a name="ImageSaveOptions"></a>type ImageSaveOptions
+
+
+
+name [string](https://godoc.org/builtin#string)
+
+format [string](https://godoc.org/builtin#string)
+
+output [string](https://godoc.org/builtin#string)
+
+outputType [string](https://godoc.org/builtin#string)
+
+moreTags [[]string](#[]string)
+
+quiet [bool](https://godoc.org/builtin#bool)
+
+compress [bool](https://godoc.org/builtin#bool)
+### <a name="ImageSearchFilter"></a>type ImageSearchFilter
+
+
+
+is_official [](#)
+
+is_automated [](#)
+
+star_count [int](https://godoc.org/builtin#int)
### <a name="ImageSearchResult"></a>type ImageSearchResult
Represents a single search result from SearchImages
@@ -1602,7 +1659,7 @@ pull [bool](https://godoc.org/builtin#bool)
signaturePolicyPath [string](https://godoc.org/builtin#string)
-tlsVerify [bool](https://godoc.org/builtin#bool)
+tlsVerify [](#)
label [string](https://godoc.org/builtin#string)
@@ -1686,3 +1743,6 @@ PodNotFound means the pod could not be found by the provided name or ID in local
### <a name="RuntimeError"></a>type RuntimeError
RuntimeErrors generally means a runtime could not be found or gotten.
+### <a name="VolumeNotFound"></a>type VolumeNotFound
+
+VolumeNotFound means the volume could not be found by the name or ID in local storage.
diff --git a/README.md b/README.md
index 52324c860..08c584c72 100644
--- a/README.md
+++ b/README.md
@@ -83,18 +83,23 @@ Information about contributing to this project.
## Buildah and Podman relationship
-Buildah and Podman are two complementary Open-source projects that are available on
-most Linux platforms and both projects reside at [GitHub.com](https://github.com)
-with [Buildah](https://buildah.io) [(GitHub)](https://github.com/containers/buildah) and
-[Podman](https://podman.io) [(GitHub)](https://github.com/containers/libpod). Both Buildah and Podman are
-command line tools that work on OCI images and containers. The two projects
-differentiate in their specialization.
+Buildah and Podman are two complementary open-source projects that are
+available on most Linux platforms and both projects reside at
+[GitHub.com](https://github.com) with Buildah
+[here](https://github.com/containers/buildah) and Podman
+[here](https://github.com/containers/libpod). Both, Buildah and Podman are
+command line tools that work on Open Container Initiative (OCI) images and
+containers. The two projects differentiate in their specialization.
Buildah specializes in building OCI images. Buildah's commands replicate all
-of the commands that are found in a Dockerfile. Buildah’s goal is also to
-provide a lower level coreutils interface to build images, allowing people to build
-containers without requiring a Dockerfile. The intent with Buildah is to allow other
-scripting languages to build container images, without requiring a daemon.
+of the commands that are found in a Dockerfile. This allows building images
+with and without Dockerfiles while not requiring any root privileges.
+Buildah’s ultimate goal is to provide a lower-level coreutils interface to
+build images. The flexibility of building images without Dockerfiles allows
+for the integration of other scripting languages into the build process.
+Buildah follows a simple fork-exec model and does not run as a daemon
+but it is based on a comprehensive API in golang, which can be vendored
+into other tools.
Podman specializes in all of the commands and functions that help you to maintain and modify
OCI images, such as pulling and tagging. It also allows you to create, run, and maintain those containers
@@ -103,12 +108,12 @@ created from those images.
A major difference between Podman and Buildah is their concept of a container. Podman
allows users to create "traditional containers" where the intent of these containers is
to be long lived. While Buildah containers are really just created to allow content
-to be added back to the container image. An easy way to think of it is the
+to be added back to the container image. An easy way to think of it is the
`buildah run` command emulates the RUN command in a Dockerfile while the `podman run`
command emulates the `docker run` command in functionality. Because of this and their underlying
-storage differences, you cannot see Podman containers from within Buildah or vice versa.
+storage differences, you can not see Podman containers from within Buildah or vice versa.
-In short Buildah is an efficient way to create OCI images while Podman allows
+In short, Buildah is an efficient way to create OCI images while Podman allows
you to manage and maintain those images and containers in a production environment using
familiar container cli commands. For more details, see the
[Container Tools Guide](https://github.com/containers/buildah/tree/master/docs/containertools).
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go
index b70ff649c..074675e45 100644
--- a/cmd/podman/attach.go
+++ b/cmd/podman/attach.go
@@ -35,8 +35,8 @@ func init() {
flags.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _")
flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false")
flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true)")
-
flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func attachCmd(c *cliconfig.AttachValues) error {
diff --git a/cmd/podman/build.go b/cmd/podman/build.go
index bb252f171..e40e35cb5 100644
--- a/cmd/podman/build.go
+++ b/cmd/podman/build.go
@@ -9,7 +9,7 @@ import (
"github.com/containers/buildah/imagebuildah"
buildahcli "github.com/containers/buildah/pkg/cli"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -52,12 +52,22 @@ func init() {
flags := buildCommand.Flags()
flags.SetInterspersed(false)
- flags.BoolVar(&layerValues.ForceRm, "force-rm", true, "Always remove intermediate containers after a build, even if the build is unsuccessful. (default true)")
- flags.BoolVar(&layerValues.Layers, "layers", true, "Cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override")
budFlags := buildahcli.GetBudFlags(&budFlagsValues)
+ flag := budFlags.Lookup("pull-always")
+ flag.Value.Set("true")
+ flag.DefValue = "true"
+ layerFlags := buildahcli.GetLayerFlags(&layerValues)
+ flag = layerFlags.Lookup("layers")
+ flag.Value.Set(useLayers())
+ flag.DefValue = (useLayers())
+ flag = layerFlags.Lookup("force-rm")
+ flag.Value.Set("true")
+ flag.DefValue = "true"
+
fromAndBugFlags := buildahcli.GetFromAndBudFlags(&fromAndBudValues, &userNSValues, &namespaceValues)
flags.AddFlagSet(&budFlags)
+ flags.AddFlagSet(&layerFlags)
flags.AddFlagSet(&fromAndBugFlags)
}
@@ -179,7 +189,7 @@ func buildCmd(c *cliconfig.BuildValues) error {
}
runtimeFlags := []string{}
- for _, arg := range c.RuntimeOpts {
+ for _, arg := range c.RuntimeFlags {
runtimeFlags = append(runtimeFlags, "--"+arg)
}
// end from buildah
@@ -258,6 +268,7 @@ func buildCmd(c *cliconfig.BuildValues) error {
RuntimeArgs: runtimeFlags,
SignaturePolicyPath: c.SignaturePolicy,
Squash: c.Squash,
+ Target: c.Target,
}
return runtime.Build(getContext(), c, options, dockerfiles)
}
@@ -271,3 +282,13 @@ func Tail(a []string) []string {
}
return []string{}
}
+
+// useLayers returns false if BUILDAH_LAYERS is set to "0" or "false"
+// otherwise it returns true
+func useLayers() string {
+ layers := os.Getenv("BUILDAH_LAYERS")
+ if strings.ToLower(layers) == "false" || layers == "0" {
+ return "false"
+ }
+ return "true"
+}
diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go
index 3484e8957..c9de5638b 100644
--- a/cmd/podman/checkpoint.go
+++ b/cmd/podman/checkpoint.go
@@ -29,6 +29,9 @@ var (
checkpointCommand.GlobalFlags = MainGlobalOpts
return checkpointCmd(&checkpointCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman checkpoint --keep ctrID
podman checkpoint --all
podman checkpoint --leave-running --latest`,
@@ -45,6 +48,7 @@ func init() {
flags.BoolVar(&checkpointCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
flags.BoolVarP(&checkpointCommand.All, "all", "a", false, "Checkpoint all running containers")
flags.BoolVarP(&checkpointCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func checkpointCmd(c *cliconfig.CheckpointValues) error {
@@ -63,11 +67,6 @@ func checkpointCmd(c *cliconfig.CheckpointValues) error {
KeepRunning: c.LeaveRunning,
TCPEstablished: c.TcpEstablished,
}
-
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
-
containers, lastError := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running")
for _, ctr := range containers {
diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go
index 89a4ba050..d68255aa2 100644
--- a/cmd/podman/cleanup.go
+++ b/cmd/podman/cleanup.go
@@ -26,6 +26,9 @@ var (
cleanupCommand.GlobalFlags = MainGlobalOpts
return cleanupCmd(&cleanupCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman container cleanup --latest
podman container cleanup ctrID1 ctrID2 ctrID3
podman container cleanup --all`,
@@ -40,6 +43,7 @@ func init() {
flags.BoolVarP(&cleanupCommand.All, "all", "a", false, "Cleans up all containers")
flags.BoolVarP(&cleanupCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&cleanupCommand.Remove, "rm", false, "After cleanup, remove the container entirely")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func cleanupCmd(c *cliconfig.CleanupValues) error {
@@ -49,10 +53,6 @@ func cleanupCmd(c *cliconfig.CleanupValues) error {
}
defer runtime.Shutdown(false)
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
-
cleanupContainers, lastError := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all")
ctx := getContext()
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index ca788529c..a9032202f 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -548,3 +548,7 @@ type SystemPruneValues struct {
Force bool
Volume bool
}
+
+type SystemRenumberValues struct {
+ PodmanCommand
+}
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index fa3839a53..fadcca689 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -6,6 +6,8 @@ import (
"github.com/spf13/cobra"
)
+const remoteclient = false
+
// Commands that the local client implements
func getMainCommands() []*cobra.Command {
rootCommands := []*cobra.Command{
@@ -14,12 +16,9 @@ func getMainCommands() []*cobra.Command {
_createCommand,
_diffCommand,
_execCommand,
- _killCommand,
- generateCommand.Command,
- podCommand.Command,
- _containerKubeCommand,
+ _generateCommand,
+ _playCommand,
_psCommand,
- _loadCommand,
_loginCommand,
_logoutCommand,
_logsCommand,
@@ -31,7 +30,6 @@ func getMainCommands() []*cobra.Command {
_restoreCommand,
_rmCommand,
_runCommand,
- _saveCommand,
_searchCommand,
_signCommand,
_startCommand,
@@ -40,7 +38,6 @@ func getMainCommands() []*cobra.Command {
_topCommand,
_umountCommand,
_unpauseCommand,
- volumeCommand.Command,
_waitCommand,
}
@@ -54,7 +51,6 @@ func getMainCommands() []*cobra.Command {
func getImageSubCommands() []*cobra.Command {
return []*cobra.Command{
_loadCommand,
- _saveCommand,
_signCommand,
}
}
@@ -96,19 +92,8 @@ func getContainerSubCommands() []*cobra.Command {
// Commands that the local client implements
func getPodSubCommands() []*cobra.Command {
return []*cobra.Command{
- _podCreateCommand,
- _podExistsCommand,
- _podInspectCommand,
- _podKillCommand,
- _podPauseCommand,
- _podPsCommand,
- _podRestartCommand,
- _podRmCommand,
- _podStartCommand,
_podStatsCommand,
- _podStopCommand,
_podTopCommand,
- _podUnpauseCommand,
}
}
@@ -137,5 +122,6 @@ func getTrustSubCommands() []*cobra.Command {
func getSystemSubCommands() []*cobra.Command {
return []*cobra.Command{
_pruneSystemCommand,
+ _renumberCommand,
}
}
diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go
index ba0a4d47e..081043b25 100644
--- a/cmd/podman/commands_remoteclient.go
+++ b/cmd/podman/commands_remoteclient.go
@@ -6,6 +6,8 @@ import (
"github.com/spf13/cobra"
)
+const remoteclient = true
+
// commands that only the remoteclient implements
func getMainCommands() []*cobra.Command {
return []*cobra.Command{}
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index fed07de7c..e297f3921 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
+ "github.com/spf13/cobra"
"os"
"strings"
@@ -36,16 +37,24 @@ func shortID(id string) string {
}
// checkAllAndLatest checks that --all and --latest are used correctly
-func checkAllAndLatest(c *cliconfig.PodmanCommand) error {
- argLen := len(c.InputArgs)
- if (c.Bool("all") || c.Bool("latest")) && argLen > 0 {
- return errors.Errorf("no arguments are needed with --all or --latest")
+func checkAllAndLatest(c *cobra.Command, args []string, ignoreArgLen bool) error {
+ argLen := len(args)
+ if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
+ return errors.New("unable to lookup values for 'latest' or 'all'")
}
- if c.Bool("all") && c.Bool("latest") {
+ all, _ := c.Flags().GetBool("all")
+ latest, _ := c.Flags().GetBool("latest")
+ if all && latest {
return errors.Errorf("--all and --latest cannot be used together")
}
- if argLen < 1 && !c.Bool("all") && !c.Bool("latest") {
- return errors.Errorf("you must provide at least one pod name or id")
+ if ignoreArgLen {
+ return nil
+ }
+ if (all || latest) && argLen > 0 {
+ return errors.Errorf("no arguments are needed with --all or --latest")
+ }
+ if argLen < 1 && !all && !latest {
+ return errors.Errorf("you must provide at least one name or id")
}
return nil
}
diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go
index bae578e1d..6e4960429 100644
--- a/cmd/podman/containers_prune.go
+++ b/cmd/podman/containers_prune.go
@@ -6,7 +6,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go
index 89114fda1..d9f230b67 100644
--- a/cmd/podman/cp.go
+++ b/cmd/podman/cp.go
@@ -1,8 +1,10 @@
package main
import (
+ "io/ioutil"
"os"
"path/filepath"
+ "strconv"
"strings"
"github.com/containers/buildah/util"
@@ -10,6 +12,7 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/chrootuser"
+ "github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/chrootarchive"
@@ -48,6 +51,9 @@ func cpCmd(c *cliconfig.CpValues) error {
if len(args) != 2 {
return errors.Errorf("you must provide a source path and a destination path")
}
+ if os.Geteuid() != 0 {
+ rootless.SetSkipStorageSetup(true)
+ }
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
@@ -76,6 +82,34 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
ctr = destCtr
}
+ if os.Geteuid() != 0 {
+ s, err := ctr.State()
+ if err != nil {
+ return err
+ }
+ var became bool
+ var ret int
+ if s == libpod.ContainerStateRunning || s == libpod.ContainerStatePaused {
+ data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile)
+ if err != nil {
+ return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile)
+ }
+ conmonPid, err := strconv.Atoi(string(data))
+ if err != nil {
+ return errors.Wrapf(err, "cannot parse PID %q", data)
+ }
+ became, ret, err = rootless.JoinDirectUserAndMountNS(uint(conmonPid))
+ } else {
+ became, ret, err = rootless.BecomeRootInUserNS()
+ }
+ if err != nil {
+ return err
+ }
+ if became {
+ os.Exit(ret)
+ }
+ }
+
mountPoint, err := ctr.Mount()
if err != nil {
return err
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 216f171a8..868f90d54 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -646,11 +646,6 @@ func parseCreateOpts(ctx context.Context, c *cliconfig.PodmanCommand, runtime *l
if util.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
}
- if !netMode.IsPrivate() {
- if c.IsSet("dns-search") || c.IsSet("dns") || c.IsSet("dns-opt") {
- return nil, errors.Errorf("specifying DNS flags when network mode is shared with the host or another container is not allowed")
- }
- }
// Validate domains are good
for _, dom := range c.StringSlice("dns-search") {
@@ -868,6 +863,12 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
if err != nil {
return false, -1, err
}
+ if pid == 0 {
+ if createConfig.Pod != "" {
+ continue
+ }
+ return false, -1, errors.Errorf("dependency container %s is not running", ctr.ID())
+ }
return rootless.JoinNS(uint(pid))
}
}
diff --git a/cmd/podman/errors.go b/cmd/podman/errors.go
new file mode 100644
index 000000000..2572b8779
--- /dev/null
+++ b/cmd/podman/errors.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "syscall"
+
+ "github.com/sirupsen/logrus"
+)
+
+func outputError(err error) {
+ if MainGlobalOpts.LogLevel == "debug" {
+ logrus.Errorf(err.Error())
+ } else {
+ if ee, ok := err.(*exec.ExitError); ok {
+ if status, ok := ee.Sys().(syscall.WaitStatus); ok {
+ exitCode = status.ExitStatus()
+ }
+ }
+ fmt.Fprintln(os.Stderr, "Error:", err.Error())
+ }
+}
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index 9599be528..7040a7b09 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -48,7 +48,7 @@ func init() {
flags.StringVarP(&execCommand.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command")
flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container")
-
+ markFlagHiddenForRemoteClient("latest", flags)
}
func execCmd(c *cliconfig.ExecValues) error {
diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go
index 7645bb716..74a4c841b 100644
--- a/cmd/podman/exists.go
+++ b/cmd/podman/exists.go
@@ -5,10 +5,9 @@ import (
"github.com/spf13/cobra"
"os"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
)
@@ -124,14 +123,14 @@ func podExistsCmd(c *cliconfig.PodExistsValues) error {
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.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
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 {
+ if errors.Cause(err) == libpod.ErrNoSuchPod || err.Error() == "io.podman.PodNotFound" {
os.Exit(1)
}
return err
diff --git a/cmd/podman/export.go b/cmd/podman/export.go
index a593a4753..5873bad3d 100644
--- a/cmd/podman/export.go
+++ b/cmd/podman/export.go
@@ -4,7 +4,7 @@ import (
"os"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
diff --git a/cmd/podman/formats/formats.go b/cmd/podman/formats/formats.go
index c454c39bd..37f9b8a20 100644
--- a/cmd/podman/formats/formats.go
+++ b/cmd/podman/formats/formats.go
@@ -120,11 +120,8 @@ func (t StdoutTemplateArray) Out() error {
fmt.Fprintln(w, "")
continue
}
- // Only print new line at the end of the output if stdout is the terminal
- if terminal.IsTerminal(int(os.Stdout.Fd())) {
- fmt.Fprintln(w, "")
- }
}
+ fmt.Fprintln(w, "")
return w.Flush()
}
diff --git a/cmd/podman/generate.go b/cmd/podman/generate.go
index 66cb7a465..773d625ee 100644
--- a/cmd/podman/generate.go
+++ b/cmd/podman/generate.go
@@ -5,17 +5,18 @@ import (
"github.com/spf13/cobra"
)
-var generateDescription = "Generate structured data based for a containers and pods"
-var generateCommand = cliconfig.PodmanCommand{
-
- Command: &cobra.Command{
+var (
+ generateCommand cliconfig.PodmanCommand
+ generateDescription = "Generate structured data based for a containers and pods"
+ _generateCommand = &cobra.Command{
Use: "generate",
Short: "Generated structured data",
Long: generateDescription,
- },
-}
+ }
+)
func init() {
+ generateCommand.Command = _generateCommand
generateCommand.AddCommand(getGenerateSubCommands()...)
generateCommand.SetUsageTemplate(UsageTemplate())
}
diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go
index ddb2daa34..15f374c73 100644
--- a/cmd/podman/generate_kube.go
+++ b/cmd/podman/generate_kube.go
@@ -25,7 +25,9 @@ var (
containerKubeCommand.GlobalFlags = MainGlobalOpts
return generateKubeYAMLCmd(&containerKubeCommand)
},
- Example: "CONTAINER|POD-NAME",
+ Example: `podman generate kube ctrID
+ podman generate kube podID
+ podman generate kube --service podID`,
}
)
diff --git a/cmd/podman/history.go b/cmd/podman/history.go
index 6791257d9..103ef08e8 100644
--- a/cmd/podman/history.go
+++ b/cmd/podman/history.go
@@ -8,8 +8,8 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index 4f9c7cd6a..14053cb0d 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -24,10 +24,12 @@ var imageSubCommands = []*cobra.Command{
_imagesCommand,
_importCommand,
_inspectCommand,
+ _loadCommand,
_pruneImagesCommand,
_pullCommand,
_pushCommand,
_rmiCommand,
+ _saveCommand,
_tagCommand,
}
diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go
index 366510202..d01eb7436 100644
--- a/cmd/podman/imagefilters/filters.go
+++ b/cmd/podman/imagefilters/filters.go
@@ -5,7 +5,7 @@ import (
"strings"
"time"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/inspect"
)
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index b269f6440..6e82195a9 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -11,8 +11,8 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/cmd/podman/imagefilters"
- "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/docker/go-units"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -96,7 +96,9 @@ var (
imagesCommand.GlobalFlags = MainGlobalOpts
return imagesCmd(&imagesCommand)
},
- Example: "",
+ Example: `podman images --format json
+ podman images --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}"
+ podman images --filter dangling=true`,
}
)
diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go
index cc0dcb99a..79dcd097c 100644
--- a/cmd/podman/images_prune.go
+++ b/cmd/podman/images_prune.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/import.go b/cmd/podman/import.go
index 053408ff3..a64b03d6d 100644
--- a/cmd/podman/import.go
+++ b/cmd/podman/import.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/info.go b/cmd/podman/info.go
index 06dbbd748..a1473dac9 100644
--- a/cmd/podman/info.go
+++ b/cmd/podman/info.go
@@ -7,7 +7,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/version"
"github.com/pkg/errors"
"github.com/spf13/cobra"
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index a1f3ef81f..46883b31d 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -8,7 +8,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
@@ -48,7 +48,7 @@ func init() {
flags.StringVarP(&inspectCommand.Format, "format", "f", "", "Change the output format to a Go template")
flags.BoolVarP(&inspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of if the type is a container")
flags.BoolVarP(&inspectCommand.Size, "size", "s", false, "Display total file size if the type is container")
-
+ markFlagHiddenForRemoteClient("latest", flags)
}
func inspectCmd(c *cliconfig.InspectValues) error {
diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go
index 1be4fa959..eb72d53e7 100644
--- a/cmd/podman/kill.go
+++ b/cmd/podman/kill.go
@@ -28,6 +28,9 @@ var (
killCommand.GlobalFlags = MainGlobalOpts
return killCmd(&killCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman kill mywebserver
podman kill 860a4b23
podman kill --signal TERM ctrID`,
@@ -43,6 +46,7 @@ func init() {
flags.StringVarP(&killCommand.Signal, "signal", "s", "KILL", "Signal to send to the container")
flags.BoolVarP(&killCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
// killCmd kills one or more containers with a signal
@@ -52,10 +56,6 @@ func killCmd(c *cliconfig.KillValues) error {
killSignal uint = uint(syscall.SIGTERM)
)
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
-
rootless.SetSkipStorageSetup(true)
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go
index 0b9568b8d..880b281bd 100644
--- a/cmd/podman/libpodruntime/runtime.go
+++ b/cmd/podman/libpodruntime/runtime.go
@@ -8,8 +8,17 @@ import (
"github.com/pkg/errors"
)
+// GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber
+func GetRuntimeRenumber(c *cliconfig.PodmanCommand) (*libpod.Runtime, error) {
+ return getRuntime(c, true)
+}
+
// GetRuntime generates a new libpod runtime configured by command line options
func GetRuntime(c *cliconfig.PodmanCommand) (*libpod.Runtime, error) {
+ return getRuntime(c, false)
+}
+
+func getRuntime(c *cliconfig.PodmanCommand, renumber bool) (*libpod.Runtime, error) {
options := []libpod.RuntimeOption{}
storageOpts, volumePath, err := util.GetDefaultStoreOptions()
diff --git a/cmd/podman/load.go b/cmd/podman/load.go
index 34a51cd0d..272cd78d2 100644
--- a/cmd/podman/load.go
+++ b/cmd/podman/load.go
@@ -6,12 +6,8 @@ import (
"io/ioutil"
"os"
- "github.com/containers/image/directory"
- dockerarchive "github.com/containers/image/docker/archive"
- ociarchive "github.com/containers/image/oci/archive"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -56,14 +52,16 @@ func loadCmd(c *cliconfig.LoadValues) error {
return errors.New("too many arguments. Requires exactly 1")
}
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
input := c.Input
-
+ if runtime.Remote && len(input) == 0 {
+ return errors.New("the remote client requires you to load via -i and a tarball")
+ }
if input == "/dev/stdin" {
fi, err := os.Stdin.Stat()
if err != nil {
@@ -96,46 +94,10 @@ func loadCmd(c *cliconfig.LoadValues) error {
return err
}
- var writer io.Writer
- if !c.Quiet {
- writer = os.Stderr
- }
-
- ctx := getContext()
-
- var newImages []*image.Image
- src, err := dockerarchive.ParseReference(input) // FIXME? We should add dockerarchive.NewReference()
- if err == nil {
- newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer)
- }
+ names, err := runtime.LoadImage(getContext(), imageName, c)
if err != nil {
- // generate full src name with specified image:tag
- src, err := ociarchive.NewReference(input, imageName) // imageName may be ""
- if err == nil {
- newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer)
- }
- if err != nil {
- src, err := directory.NewReference(input)
- if err == nil {
- newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer)
- }
- if err != nil {
- return errors.Wrapf(err, "error pulling %q", input)
- }
- }
+ return err
}
- fmt.Println("Loaded image(s): " + getImageNames(newImages))
+ fmt.Println("Loaded image(s): " + names)
return nil
}
-
-func getImageNames(images []*image.Image) string {
- var names string
- for i := range images {
- if i == 0 {
- names = images[i].InputName
- } else {
- names += ", " + images[i].InputName
- }
- }
- return names
-}
diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go
index 6962a1f6d..97d835d8f 100644
--- a/cmd/podman/logs.go
+++ b/cmd/podman/logs.go
@@ -26,7 +26,9 @@ var (
logsCommand.GlobalFlags = MainGlobalOpts
return logsCmd(&logsCommand)
},
- Example: "CONTAINER",
+ Example: `podman logs ctrID
+ podman logs --tail 2 mywebserver
+ podman logs --follow=true --since 10m ctrID`,
}
)
@@ -44,6 +46,7 @@ func init() {
flags.SetInterspersed(false)
+ markFlagHiddenForRemoteClient("latest", flags)
}
func logsCmd(c *cliconfig.LogsValues) error {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index ecb72f58b..19bdb40d6 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -2,11 +2,9 @@ package main
import (
"context"
- "fmt"
"io"
"log/syslog"
"os"
- "os/exec"
"runtime/pprof"
"strings"
"syscall"
@@ -18,7 +16,7 @@ import (
"github.com/containers/libpod/pkg/tracing"
"github.com/containers/libpod/version"
"github.com/containers/storage/pkg/reexec"
- opentracing "github.com/opentracing/opentracing-go"
+ "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
lsyslog "github.com/sirupsen/logrus/hooks/syslog"
@@ -45,9 +43,12 @@ var mainCommands = []*cobra.Command{
_infoCommand,
_inspectCommand,
_killCommand,
+ _loadCommand,
+ podCommand.Command,
_pullCommand,
_pushCommand,
_rmiCommand,
+ _saveCommand,
_tagCommand,
_versionCommand,
imageCommand.Command,
@@ -58,6 +59,7 @@ var cmdsNotRequiringRootless = map[*cobra.Command]bool{
_versionCommand: true,
_createCommand: true,
_execCommand: true,
+ _cpCommand: true,
_exportCommand: true,
//// `info` must be executed in an user namespace.
//// If this change, please also update libpod.refreshRootless()
@@ -220,16 +222,7 @@ func main() {
return
}
if err := rootCmd.Execute(); err != nil {
- if MainGlobalOpts.LogLevel == "debug" {
- logrus.Errorf(err.Error())
- } else {
- if ee, ok := err.(*exec.ExitError); ok {
- if status, ok := ee.Sys().(syscall.WaitStatus); ok {
- exitCode = status.ExitStatus()
- }
- }
- fmt.Fprintln(os.Stderr, "Error:", err.Error())
- }
+ outputError(err)
} else {
// The exitCode modified from 125, indicates an application
// running inside of a container failed, as opposed to the
diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go
index ce7c22d60..f4a7bd5ea 100644
--- a/cmd/podman/mount.go
+++ b/cmd/podman/mount.go
@@ -34,6 +34,9 @@ var (
mountCommand.GlobalFlags = MainGlobalOpts
return mountCmd(&mountCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, true)
+ },
}
)
@@ -46,6 +49,7 @@ func init() {
flags.BoolVarP(&mountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&mountCommand.NoTrunc, "notruncate", false, "Do not truncate output")
+ markFlagHiddenForRemoteClient("latest", flags)
}
// jsonMountPoint stores info about each container
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
index 9fc06dde9..a59460b71 100644
--- a/cmd/podman/play_kube.go
+++ b/cmd/podman/play_kube.go
@@ -52,8 +52,6 @@ func init() {
flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
-
- rootCmd.AddCommand(playKubeCommand.Command)
}
func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error {
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
index e988875ab..c1350bd4d 100644
--- a/cmd/podman/pod.go
+++ b/cmd/podman/pod.go
@@ -18,7 +18,23 @@ var podCommand = cliconfig.PodmanCommand{
},
}
+//podSubCommands are implemented both in local and remote clients
+var podSubCommands = []*cobra.Command{
+ _podCreateCommand,
+ _podExistsCommand,
+ _podInspectCommand,
+ _podKillCommand,
+ _podPauseCommand,
+ _podPsCommand,
+ _podRestartCommand,
+ _podRmCommand,
+ _podStartCommand,
+ _podStopCommand,
+ _podUnpauseCommand,
+}
+
func init() {
+ podCommand.AddCommand(podSubCommands...)
podCommand.AddCommand(getPodSubCommands()...)
podCommand.SetUsageTemplate(UsageTemplate())
}
diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go
index 0a0b86aab..f1bbecb84 100644
--- a/cmd/podman/pod_create.go
+++ b/cmd/podman/pod_create.go
@@ -3,12 +3,10 @@ package main
import (
"fmt"
"os"
- "strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -56,16 +54,29 @@ func init() {
}
func podCreateCmd(c *cliconfig.PodCreateValues) error {
- var options []libpod.PodCreateOption
- var err error
+ var (
+ err error
+ podIdFile *os.File
+ )
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ if len(c.InputArgs) > 0 {
+ return errors.New("podman pod create does not accept any arguments")
+ }
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- var podIdFile *os.File
+ if len(c.Publish) > 0 {
+ if !c.Infra {
+ return errors.Errorf("you must have an infra container to publish port bindings to the host")
+ }
+ }
+
+ if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" {
+ return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
+ }
if c.Flag("pod-id-file").Changed && os.Geteuid() == 0 {
podIdFile, err = libpod.OpenExclusiveFile(c.PodIDFile)
if err != nil && os.IsExist(err) {
@@ -78,67 +89,21 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error {
defer podIdFile.Sync()
}
- if len(c.Publish) > 0 {
- if !c.Infra {
- return errors.Errorf("you must have an infra container to publish port bindings to the host")
- }
- }
-
- if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" {
- return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
- }
-
- if c.Flag("cgroup-parent").Changed {
- options = append(options, libpod.WithPodCgroupParent(c.CgroupParent))
- }
-
labels, err := getAllLabels(c.LabelFile, c.Labels)
if err != nil {
return errors.Wrapf(err, "unable to process labels")
}
- if len(labels) != 0 {
- options = append(options, libpod.WithPodLabels(labels))
- }
-
- if c.Flag("name").Changed {
- options = append(options, libpod.WithPodName(c.Name))
- }
-
- if c.Infra {
- options = append(options, libpod.WithInfraContainer())
- nsOptions, err := shared.GetNamespaceOptions(strings.Split(c.Share, ","))
- if err != nil {
- return err
- }
- options = append(options, nsOptions...)
- }
- if len(c.Publish) > 0 {
- portBindings, err := shared.CreatePortBindings(c.Publish)
- if err != nil {
- return err
- }
- options = append(options, libpod.WithInfraContainerPorts(portBindings))
-
- }
- // always have containers use pod cgroups
- // User Opt out is not yet supported
- options = append(options, libpod.WithPodCgroups())
-
- ctx := getContext()
- pod, err := runtime.NewPod(ctx, options...)
+ podID, err := runtime.CreatePod(getContext(), c, labels)
if err != nil {
- return err
+ return errors.Wrapf(err, "unable to create pod")
}
-
if podIdFile != nil {
- _, err = podIdFile.WriteString(pod.ID())
+ _, err = podIdFile.WriteString(podID)
if err != nil {
logrus.Error(err)
}
}
-
- fmt.Printf("%s\n", pod.ID())
-
+ fmt.Printf("%s\n", podID)
return nil
}
diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go
index 58b15328e..5a32b5c5d 100644
--- a/cmd/podman/pod_inspect.go
+++ b/cmd/podman/pod_inspect.go
@@ -5,8 +5,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -33,17 +32,15 @@ func init() {
flags := podInspectCommand.Flags()
flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podInspectCmd(c *cliconfig.PodInspectValues) error {
var (
- pod *libpod.Pod
+ pod *adapter.Pod
)
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
args := c.InputArgs
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go
index febc820cd..aaaae0f7d 100644
--- a/cmd/podman/pod_kill.go
+++ b/cmd/podman/pod_kill.go
@@ -5,7 +5,7 @@ import (
"syscall"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -24,7 +24,12 @@ var (
podKillCommand.GlobalFlags = MainGlobalOpts
return podKillCmd(&podKillCommand)
},
- Example: "[POD_NAME_OR_ID]",
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
+ Example: `podman pod kill podID
+ podman pod kill --signal TERM mywebserver
+ podman pod kill --latest`,
}
)
@@ -35,15 +40,12 @@ func init() {
flags.BoolVarP(&podKillCommand.All, "all", "a", false, "Kill all containers in all pods")
flags.BoolVarP(&podKillCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
flags.StringVarP(&podKillCommand.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod")
+ markFlagHiddenForRemoteClient("latest", flags)
}
// podKillCmd kills one or more pods with a signal
func podKillCmd(c *cliconfig.PodKillValues) error {
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
@@ -61,30 +63,20 @@ func podKillCmd(c *cliconfig.PodKillValues) error {
killSignal = uint(sysSignal)
}
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ podKillIds, podKillErrors := runtime.KillPods(getContext(), c, killSignal)
+ for _, p := range podKillIds {
+ fmt.Println(p)
+ }
+ if len(podKillErrors) == 0 {
+ return nil
+ }
+ // Grab the last error
+ lastError := podKillErrors[len(podKillErrors)-1]
+ // Remove the last error from the error slice
+ podKillErrors = podKillErrors[:len(podKillErrors)-1]
- for _, pod := range pods {
- ctr_errs, err := pod.Kill(killSignal)
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to kill container %q in pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to kill pod %q", pod.ID())
- continue
- }
- fmt.Println(pod.ID())
+ for _, err := range podKillErrors {
+ logrus.Errorf("%q", err)
}
return lastError
}
diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go
index 2059727ae..284740d22 100644
--- a/cmd/podman/pod_pause.go
+++ b/cmd/podman/pod_pause.go
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -21,7 +21,12 @@ var (
podPauseCommand.GlobalFlags = MainGlobalOpts
return podPauseCmd(&podPauseCommand)
},
- Example: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]",
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
+ Example: `podman pod pause podID1 podID2
+ podman pod pause --latest
+ podman pod pause --all`,
}
)
@@ -31,44 +36,37 @@ func init() {
flags := podPauseCommand.Flags()
flags.BoolVarP(&podPauseCommand.All, "all", "a", false, "Pause all running pods")
flags.BoolVarP(&podPauseCommand.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podPauseCmd(c *cliconfig.PodPauseValues) error {
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ var lastError error
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ pauseIDs, conErrors, pauseErrors := runtime.PausePods(c)
- for _, pod := range pods {
- ctr_errs, err := pod.Pause()
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to pause container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
+ for _, p := range pauseIDs {
+ fmt.Println(p)
+ }
+ if conErrors != nil && len(conErrors) > 0 {
+ for ctr, err := range conErrors {
if lastError != nil {
logrus.Errorf("%q", lastError)
}
- lastError = errors.Wrapf(err, "unable to pause pod %q", pod.ID())
- continue
+ lastError = errors.Wrapf(err, "unable to pause container %s", ctr)
}
- fmt.Println(pod.ID())
}
-
+ if len(pauseErrors) > 0 {
+ lastError = pauseErrors[len(pauseErrors)-1]
+ // Remove the last error from the error slice
+ pauseErrors = pauseErrors[:len(pauseErrors)-1]
+ }
+ for _, err := range pauseErrors {
+ logrus.Errorf("%q", err)
+ }
return lastError
}
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
index 49af91a1e..70e077651 100644
--- a/cmd/podman/pod_ps.go
+++ b/cmd/podman/pod_ps.go
@@ -10,9 +10,9 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/util"
"github.com/docker/go-units"
"github.com/pkg/errors"
@@ -29,6 +29,8 @@ const (
NUM_CTR_INFO = 10
)
+type PodFilter func(*adapter.Pod) bool
+
var (
bc_opts shared.PsOptions
)
@@ -144,7 +146,7 @@ func init() {
flags.BoolVar(&podPsCommand.NoTrunc, "no-trunc", false, "Do not truncate pod and container IDs")
flags.BoolVarP(&podPsCommand.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only")
flags.StringVar(&podPsCommand.Sort, "sort", "created", "Sort output by created, id, name, or number")
-
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podPsCmd(c *cliconfig.PodPsValues) error {
@@ -152,7 +154,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
return errors.Wrapf(err, "error with flags passed")
}
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@@ -173,7 +175,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
opts.Format = genPodPsFormat(c)
- var filterFuncs []libpod.PodFilter
+ var filterFuncs []PodFilter
if c.Filter != "" {
filters := strings.Split(c.Filter, ",")
for _, f := range filters {
@@ -181,7 +183,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
if len(filterSplit) < 2 {
return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
- generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1], runtime)
+ generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
if err != nil {
return errors.Wrapf(err, "invalid filter")
}
@@ -189,7 +191,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- var pods []*libpod.Pod
+ var pods []*adapter.Pod
if c.Latest {
pod, err := runtime.GetLatestPod()
if err != nil {
@@ -203,7 +205,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- podsFiltered := make([]*libpod.Pod, 0, len(pods))
+ podsFiltered := make([]*adapter.Pod, 0, len(pods))
for _, pod := range pods {
include := true
for _, filter := range filterFuncs {
@@ -215,7 +217,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- return generatePodPsOutput(podsFiltered, opts, runtime)
+ return generatePodPsOutput(podsFiltered, opts)
}
// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
@@ -234,10 +236,10 @@ func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error {
return nil
}
-func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(pod *libpod.Pod) bool, error) {
+func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) bool, error) {
switch filter {
case "ctr-ids":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctrIds, err := p.AllContainersByID()
if err != nil {
return false
@@ -245,7 +247,7 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
return util.StringInSlice(filterValue, ctrIds)
}, nil
case "ctr-names":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctrs, err := p.AllContainers()
if err != nil {
return false
@@ -258,7 +260,7 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
return false
}, nil
case "ctr-number":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctrIds, err := p.AllContainersByID()
if err != nil {
return false
@@ -274,7 +276,7 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) {
return nil, errors.Errorf("%s is not a valid status", filterValue)
}
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctr_statuses, err := p.Status()
if err != nil {
return false
@@ -291,19 +293,19 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
return false
}, nil
case "id":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
return strings.Contains(p.ID(), filterValue)
}, nil
case "name":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
return strings.Contains(p.Name(), filterValue)
}, nil
case "status":
if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
return nil, errors.Errorf("%s is not a valid pod status", filterValue)
}
- return func(p *libpod.Pod) bool {
- status, err := shared.GetPodStatus(p)
+ return func(p *adapter.Pod) bool {
+ status, err := p.GetPodStatus()
if err != nil {
return false
}
@@ -448,7 +450,7 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP
return psOutput, nil
}
-func getNamespaces(pod *libpod.Pod) []string {
+func getNamespaces(pod *adapter.Pod) []string {
var shared []string
if pod.SharesPID() {
shared = append(shared, "pid")
@@ -475,7 +477,7 @@ func getNamespaces(pod *libpod.Pod) []string {
}
// getAndSortPodJSONOutput returns the container info in its raw, sorted form
-func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) ([]podPsJSONParams, error) {
+func getAndSortPodJSONParams(pods []*adapter.Pod, opts podPsOptions) ([]podPsJSONParams, error) {
var (
psOutput []podPsJSONParams
)
@@ -487,7 +489,7 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib
return nil, err
}
ctrNum := len(ctrs)
- status, err := shared.GetPodStatus(pod)
+ status, err := pod.GetPodStatus()
if err != nil {
return nil, err
}
@@ -497,7 +499,7 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib
return nil, err
}
for _, ctr := range ctrs {
- batchInfo, err := shared.BatchContainerOp(ctr, bc_opts)
+ batchInfo, err := adapter.BatchContainerOp(ctr, bc_opts)
if err != nil {
return nil, err
}
@@ -539,11 +541,11 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib
return sortPodPsOutput(opts.Sort, psOutput)
}
-func generatePodPsOutput(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) error {
+func generatePodPsOutput(pods []*adapter.Pod, opts podPsOptions) error {
if len(pods) == 0 && opts.Format != formats.JSONString {
return nil
}
- psOutput, err := getAndSortPodJSONParams(pods, opts, runtime)
+ psOutput, err := getAndSortPodJSONParams(pods, opts)
if err != nil {
return err
}
diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go
index f75d84956..741fce588 100644
--- a/cmd/podman/pod_restart.go
+++ b/cmd/podman/pod_restart.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -22,7 +22,12 @@ var (
podRestartCommand.GlobalFlags = MainGlobalOpts
return podRestartCmd(&podRestartCommand)
},
- Example: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]",
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
+ Example: `podman pod restart podID1 podID2
+ podman pod restart --latest
+ podman pod restart --all`,
}
)
@@ -33,44 +38,37 @@ func init() {
flags.BoolVarP(&podRestartCommand.All, "all", "a", false, "Restart all running pods")
flags.BoolVarP(&podRestartCommand.Latest, "latest", "l", false, "Restart the latest pod podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podRestartCmd(c *cliconfig.PodRestartValues) error {
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ var lastError error
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ restartIDs, conErrors, restartErrors := runtime.RestartPods(getContext(), c)
- ctx := getContext()
- for _, pod := range pods {
- ctr_errs, err := pod.Restart(ctx)
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to restart container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
+ for _, p := range restartIDs {
+ fmt.Println(p)
+ }
+ if conErrors != nil && len(conErrors) > 0 {
+ for ctr, err := range conErrors {
if lastError != nil {
logrus.Errorf("%q", lastError)
}
- lastError = errors.Wrapf(err, "unable to restart pod %q", pod.ID())
- continue
+ lastError = errors.Wrapf(err, "unable to pause container %s", ctr)
}
- fmt.Println(pod.ID())
+ }
+ if len(restartErrors) > 0 {
+ lastError = restartErrors[len(restartErrors)-1]
+ // Remove the last error from the error slice
+ restartErrors = restartErrors[:len(restartErrors)-1]
+ }
+ for _, err := range restartErrors {
+ logrus.Errorf("%q", err)
}
return lastError
}
diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go
index 54cee2a50..ba16d03c7 100644
--- a/cmd/podman/pod_rm.go
+++ b/cmd/podman/pod_rm.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -26,6 +26,9 @@ If --force is specified, all containers will be stopped, then removed.
podRmCommand.GlobalFlags = MainGlobalOpts
return podRmCmd(&podRmCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman pod rm mywebserverpod
podman pod rm -f 860a4b23
podman pod rm -f -a`,
@@ -39,39 +42,30 @@ func init() {
flags.BoolVarP(&podRmCommand.All, "all", "a", false, "Remove all running pods")
flags.BoolVarP(&podRmCommand.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false")
flags.BoolVarP(&podRmCommand.Latest, "latest", "l", false, "Remove the latest pod podman is aware of")
-
+ markFlagHiddenForRemoteClient("latest", flags)
}
-// saveCmd saves the image to either docker-archive or oci
+// podRmCmd deletes pods
func podRmCmd(c *cliconfig.PodRmValues) error {
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
+ podRmIds, podRmErrors := runtime.RemovePods(getContext(), c)
+ for _, p := range podRmIds {
+ fmt.Println(p)
+ }
+ if len(podRmErrors) == 0 {
+ return nil
+ }
+ // Grab the last error
+ lastError := podRmErrors[len(podRmErrors)-1]
+ // Remove the last error from the error slice
+ podRmErrors = podRmErrors[:len(podRmErrors)-1]
- ctx := getContext()
- force := c.Force
-
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
-
- for _, pod := range pods {
- err = runtime.RemovePod(ctx, pod, force, force)
- if err != nil {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "failed to delete pod %v", pod.ID())
- } else {
- fmt.Println(pod.ID())
- }
+ for _, err := range podRmErrors {
+ logrus.Errorf("%q", err)
}
return lastError
}
diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go
index d093c51cf..5761afd52 100644
--- a/cmd/podman/pod_start.go
+++ b/cmd/podman/pod_start.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -26,6 +26,9 @@ var (
podStartCommand.GlobalFlags = MainGlobalOpts
return podStartCmd(&podStartCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman pod start podID
podman pod start --latest
podman pod start --all`,
@@ -38,45 +41,30 @@ func init() {
flags := podStartCommand.Flags()
flags.BoolVarP(&podStartCommand.All, "all", "a", false, "Start all pods")
flags.BoolVarP(&podStartCommand.Latest, "latest", "l", false, "Start the latest pod podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podStartCmd(c *cliconfig.PodStartValues) error {
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
-
- ctx := getContext()
- for _, pod := range pods {
- ctr_errs, err := pod.Start(ctx)
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to start container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to start pod %q", pod.ID())
- continue
- }
- fmt.Println(pod.ID())
+ podStartIDs, podStartErrors := runtime.StartPods(getContext(), c)
+ for _, p := range podStartIDs {
+ fmt.Println(p)
+ }
+ if len(podStartErrors) == 0 {
+ return nil
}
+ // Grab the last error
+ lastError := podStartErrors[len(podStartErrors)-1]
+ // Remove the last error from the error slice
+ podStartErrors = podStartErrors[:len(podStartErrors)-1]
+ for _, err := range podStartErrors {
+ logrus.Errorf("%q", err)
+ }
return lastError
}
diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go
index b1779532f..907d6a547 100644
--- a/cmd/podman/pod_stats.go
+++ b/cmd/podman/pod_stats.go
@@ -47,6 +47,7 @@ func init() {
flags.BoolVarP(&podStatsCommand.Latest, "latest", "l", false, "Provide stats on the latest pod podman is aware of")
flags.BoolVar(&podStatsCommand.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false")
flags.BoolVar(&podStatsCommand.NoReset, "no-reset", false, "Disable resetting the screen between intervals")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podStatsCmd(c *cliconfig.PodStatsValues) error {
diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go
index a9237347e..62d0d4aa5 100644
--- a/cmd/podman/pod_stop.go
+++ b/cmd/podman/pod_stop.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -27,6 +27,9 @@ var (
podStopCommand.GlobalFlags = MainGlobalOpts
return podStopCmd(&podStopCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman pod stop mywebserverpod
podman pod stop --latest
podman pod stop --timeout 0 490eb 3557fb`,
@@ -40,50 +43,30 @@ func init() {
flags.BoolVarP(&podStopCommand.All, "all", "a", false, "Stop all running pods")
flags.BoolVarP(&podStopCommand.Latest, "latest", "l", false, "Stop the latest pod podman is aware of")
flags.UintVarP(&podStopCommand.Timeout, "timeout", "t", 0, "Seconds to wait for pod stop before killing the container")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podStopCmd(c *cliconfig.PodStopValues) error {
- timeout := -1
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
-
- ctx := getContext()
-
- if c.Flag("timeout").Changed {
- timeout = int(c.Timeout)
+ podStopIds, podStopErrors := runtime.StopPods(getContext(), c)
+ for _, p := range podStopIds {
+ fmt.Println(p)
+ }
+ if len(podStopErrors) == 0 {
+ return nil
}
- for _, pod := range pods {
- // set cleanup to true to clean mounts and namespaces
- ctr_errs, err := pod.StopWithTimeout(ctx, true, timeout)
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to stop container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to stop pod %q", pod.ID())
- continue
- }
- fmt.Println(pod.ID())
+ // Grab the last error
+ lastError := podStopErrors[len(podStopErrors)-1]
+ // Remove the last error from the error slice
+ podStopErrors = podStopErrors[:len(podStopErrors)-1]
+
+ for _, err := range podStopErrors {
+ logrus.Errorf("%q", err)
}
return lastError
}
diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go
index a917919c3..16481d0e2 100644
--- a/cmd/podman/pod_unpause.go
+++ b/cmd/podman/pod_unpause.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -22,7 +22,12 @@ var (
podUnpauseCommand.GlobalFlags = MainGlobalOpts
return podUnpauseCmd(&podUnpauseCommand)
},
- Example: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]",
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
+ Example: `podman pod unpause podID1 podID2
+ podman pod unpause --all
+ podman pod unpause --latest`,
}
)
@@ -32,44 +37,37 @@ func init() {
flags := podUnpauseCommand.Flags()
flags.BoolVarP(&podUnpauseCommand.All, "all", "a", false, "Unpause all running pods")
flags.BoolVarP(&podUnpauseCommand.Latest, "latest", "l", false, "Unpause the latest pod podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func podUnpauseCmd(c *cliconfig.PodUnpauseValues) error {
- if err := checkMutuallyExclusiveFlags(&c.PodmanCommand); err != nil {
- return err
- }
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ var lastError error
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ unpauseIDs, conErrors, unpauseErrors := runtime.UnpausePods(c)
- for _, pod := range pods {
- ctr_errs, err := pod.Unpause()
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to unpause container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
+ for _, p := range unpauseIDs {
+ fmt.Println(p)
+ }
+ if conErrors != nil && len(conErrors) > 0 {
+ for ctr, err := range conErrors {
if lastError != nil {
logrus.Errorf("%q", lastError)
}
- lastError = errors.Wrapf(err, "unable to unpause pod %q", pod.ID())
- continue
+ lastError = errors.Wrapf(err, "unable to unpause container %s", ctr)
}
- fmt.Println(pod.ID())
}
-
+ if len(unpauseErrors) > 0 {
+ lastError = unpauseErrors[len(unpauseErrors)-1]
+ // Remove the last error from the error slice
+ unpauseErrors = unpauseErrors[:len(unpauseErrors)-1]
+ }
+ for _, err := range unpauseErrors {
+ logrus.Errorf("%q", err)
+ }
return lastError
}
diff --git a/cmd/podman/port.go b/cmd/podman/port.go
index be84da065..bcf372a51 100644
--- a/cmd/podman/port.go
+++ b/cmd/podman/port.go
@@ -28,7 +28,12 @@ var (
portCommand.GlobalFlags = MainGlobalOpts
return portCmd(&portCommand)
},
- Example: "CONTAINER-NAME [mapping]",
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, true)
+ },
+ Example: `podman port --all
+ podman port ctrID 80/tcp
+ podman port --latest 80`,
}
)
@@ -40,6 +45,7 @@ func init() {
flags.BoolVarP(&portCommand.All, "all", "a", false, "Display port information for all containers")
flags.BoolVarP(&portCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func portCmd(c *cliconfig.PortValues) error {
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index d7f0d9da0..9c165b836 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -191,6 +191,7 @@ func init() {
flags.StringVar(&psCommand.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status")
flags.BoolVar(&psCommand.Sync, "sync", false, "Sync container state with OCI runtime")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func psCmd(c *cliconfig.PsValues) error {
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index 0065e975a..71f555162 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -11,9 +11,9 @@ import (
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/common"
image2 "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/util"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
@@ -74,19 +74,16 @@ func pullCmd(c *cliconfig.PullValues) error {
args := c.InputArgs
if len(args) == 0 {
- logrus.Errorf("an image name must be specified")
- return nil
+ return errors.Errorf("an image name must be specified")
}
if len(args) > 1 {
- logrus.Errorf("too many arguments. Requires exactly 1")
- return nil
+ return errors.Errorf("too many arguments. Requires exactly 1")
}
arr := strings.SplitN(args[0], ":", 2)
if len(arr) == 2 {
if c.Bool("all-tags") {
- logrus.Errorf("tag can't be used with --all-tags")
- return nil
+ return errors.Errorf("tag can't be used with --all-tags")
}
}
ctx := getContext()
diff --git a/cmd/podman/push.go b/cmd/podman/push.go
index 881d8cebc..56261a8d3 100644
--- a/cmd/podman/push.go
+++ b/cmd/podman/push.go
@@ -10,8 +10,8 @@ import (
"github.com/containers/image/manifest"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/util"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go
index 97b689c02..58fb38874 100644
--- a/cmd/podman/restart.go
+++ b/cmd/podman/restart.go
@@ -26,6 +26,9 @@ var (
restartCommand.GlobalFlags = MainGlobalOpts
return restartCmd(&restartCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman restart ctrID
podman restart --latest
podman restart ctrID1 ctrID2`,
@@ -42,6 +45,7 @@ func init() {
flags.UintVarP(&restartCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
flags.UintVar(&restartCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func restartCmd(c *cliconfig.RestartValues) error {
diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go
index 2911bbdd6..5f6e7b892 100644
--- a/cmd/podman/restore.go
+++ b/cmd/podman/restore.go
@@ -29,6 +29,9 @@ var (
restoreCommand.GlobalFlags = MainGlobalOpts
return restoreCmd(&restoreCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman container restore ctrID
podman container restore --latest
podman container restore --all`,
@@ -45,6 +48,7 @@ func init() {
// TODO: add ContainerStateCheckpointed
flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func restoreCmd(c *cliconfig.RestoreValues) error {
@@ -63,10 +67,6 @@ func restoreCmd(c *cliconfig.RestoreValues) error {
TCPEstablished: c.TcpEstablished,
}
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
-
containers, lastError := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateExited, "checkpointed")
for _, ctr := range containers {
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index 2e5fe1dc0..2dcb491d7 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -28,6 +29,9 @@ Running containers will not be removed without the -f option.
rmCommand.GlobalFlags = MainGlobalOpts
return rmCmd(&rmCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman rm imageID
podman rm mywebserver myflaskserver 860a4b23
podman rm --force --all`,
@@ -42,6 +46,7 @@ func init() {
flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running container. The default is false")
flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container")
+ markFlagHiddenForRemoteClient("latest", flags)
}
// saveCmd saves the image to either docker-archive or oci
@@ -57,19 +62,21 @@ func rmCmd(c *cliconfig.RmValues) error {
}
defer runtime.Shutdown(false)
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
-
+ failureCnt := 0
delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all")
if err != nil {
if c.Force && len(c.InputArgs) > 0 {
if errors.Cause(err) == libpod.ErrNoSuchCtr {
err = nil
+ } else {
+ failureCnt++
}
runtime.RemoveContainersFromStorage(c.InputArgs)
}
if len(delContainers) == 0 {
+ if err != nil && failureCnt == 0 {
+ exitCode = 1
+ }
return err
}
if err != nil {
@@ -96,5 +103,16 @@ func rmCmd(c *cliconfig.RmValues) error {
// Run the parallel funcs
deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
- return printParallelOutput(deleteErrors, errCount)
+ err = printParallelOutput(deleteErrors, errCount)
+ if err != nil {
+ for _, result := range deleteErrors {
+ if result != nil && errors.Cause(result) != image.ErrNoSuchCtr {
+ failureCnt++
+ }
+ }
+ if failureCnt == 0 {
+ exitCode = 1
+ }
+ }
+ return err
}
diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index fb27772f5..709ed14e0 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -5,7 +5,9 @@ import (
"os"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -29,6 +31,17 @@ var (
}
)
+func imageNotFound(err error) bool {
+ if errors.Cause(err) == image.ErrNoSuchImage {
+ return true
+ }
+ switch err.(type) {
+ case *iopodman.ImageNotFound:
+ return true
+ }
+ return false
+}
+
func init() {
rmiCommand.Command = _rmiCommand
rmiCommand.SetUsageTemplate(UsageTemplate())
@@ -39,10 +52,8 @@ func init() {
func rmiCmd(c *cliconfig.RmiValues) error {
var (
- lastError error
- deleted bool
- deleteErr error
- msg string
+ lastError error
+ failureCnt int
)
ctx := getContext()
@@ -64,19 +75,21 @@ func rmiCmd(c *cliconfig.RmiValues) error {
images := args[:]
removeImage := func(img *adapter.ContainerImage) {
- deleted = true
- msg, deleteErr = runtime.RemoveImage(ctx, img, c.Force)
- if deleteErr != nil {
- if errors.Cause(deleteErr) == storage.ErrImageUsedByContainer {
+ msg, err := runtime.RemoveImage(ctx, img, c.Force)
+ if err != nil {
+ if errors.Cause(err) == storage.ErrImageUsedByContainer {
fmt.Printf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID())
}
+ if !imageNotFound(err) {
+ failureCnt++
+ }
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
- lastError = deleteErr
- } else {
- fmt.Println(msg)
+ lastError = err
+ return
}
+ fmt.Println(msg)
}
if removeAll {
@@ -121,22 +134,21 @@ func rmiCmd(c *cliconfig.RmiValues) error {
for _, i := range images {
newImage, err := runtime.NewImageFromLocal(i)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ if lastError != nil {
+ if !imageNotFound(lastError) {
+ failureCnt++
+ }
+ fmt.Fprintln(os.Stderr, lastError)
+ }
+ lastError = err
continue
}
removeImage(newImage)
}
}
- // If the user calls remove all and there are none, it should not be a
- // non-zero exit
- if !deleted && removeAll {
- return nil
- }
- // the user tries to remove images that do not exist, that should be a
- // non-zero exit
- if !deleted {
- return errors.Errorf("no valid images to delete")
+ if imageNotFound(lastError) && failureCnt == 0 {
+ exitCode = 1
}
return lastError
diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go
index 54f210e62..d466651f3 100644
--- a/cmd/podman/runlabel.go
+++ b/cmd/podman/runlabel.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/utils"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -87,8 +86,7 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error {
args := c.InputArgs
if len(args) < 2 {
- logrus.Errorf("the runlabel command requires at least 2 arguments: LABEL IMAGE")
- return nil
+ return errors.Errorf("the runlabel command requires at least 2 arguments: LABEL IMAGE")
}
if c.Display && c.Quiet {
return errors.Errorf("the display and quiet flags cannot be used together.")
diff --git a/cmd/podman/save.go b/cmd/podman/save.go
index ff4a22453..161540deb 100644
--- a/cmd/podman/save.go
+++ b/cmd/podman/save.go
@@ -1,21 +1,12 @@
package main
import (
- "fmt"
- "io"
"os"
"strings"
- "github.com/containers/image/directory"
- dockerarchive "github.com/containers/image/docker/archive"
- "github.com/containers/image/docker/reference"
- "github.com/containers/image/manifest"
- ociarchive "github.com/containers/image/oci/archive"
- "github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- libpodImage "github.com/containers/libpod/libpod/image"
- imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -23,9 +14,13 @@ import (
const (
ociManifestDir = "oci-dir"
+ ociArchive = "oci-archive"
v2s2ManifestDir = "docker-dir"
+ v2s2Archive = "docker-archive"
)
+var validFormats = []string{ociManifestDir, ociArchive, v2s2ManifestDir, v2s2Archive}
+
var (
saveCommand cliconfig.SaveValues
saveDescription = `
@@ -41,6 +36,16 @@ var (
saveCommand.GlobalFlags = MainGlobalOpts
return saveCmd(&saveCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ format, err := cmd.Flags().GetString("format")
+ if err != nil {
+ return err
+ }
+ if !util.StringInSlice(format, validFormats) {
+ return errors.Errorf("format value must be one of %s", strings.Join(validFormats, " "))
+ }
+ return nil
+ },
Example: `podman save --quiet -o myimage.tar imageID
podman save --format docker-dir -o ubuntu-dir ubuntu
podman save > alpine-all.tar alpine:latest`,
@@ -52,7 +57,7 @@ func init() {
saveCommand.SetUsageTemplate(UsageTemplate())
flags := saveCommand.Flags()
flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)")
- flags.StringVar(&saveCommand.Format, "format", "", "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-dir (directory with v2s2 manifest type)")
+ flags.StringVar(&saveCommand.Format, "format", v2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT")
flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output")
}
@@ -64,7 +69,7 @@ func saveCmd(c *cliconfig.SaveValues) error {
return errors.Errorf("need at least 1 argument")
}
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
@@ -74,11 +79,6 @@ func saveCmd(c *cliconfig.SaveValues) error {
return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'")
}
- var writer io.Writer
- if !c.Quiet {
- writer = os.Stderr
- }
-
output := c.Output
if output == "/dev/stdout" {
fi := os.Stdout
@@ -89,87 +89,5 @@ func saveCmd(c *cliconfig.SaveValues) error {
if err := validateFileName(output); err != nil {
return err
}
-
- source := args[0]
- newImage, err := runtime.ImageRuntime().NewFromLocal(source)
- if err != nil {
- return err
- }
-
- var destRef types.ImageReference
- var manifestType string
- switch c.Format {
- case "oci-archive":
- destImageName := imageNameForSaveDestination(newImage, source)
- destRef, err = ociarchive.NewReference(output, destImageName) // destImageName may be ""
- if err != nil {
- return errors.Wrapf(err, "error getting OCI archive ImageReference for (%q, %q)", output, destImageName)
- }
- case "oci-dir":
- destRef, err = directory.NewReference(output)
- if err != nil {
- return errors.Wrapf(err, "error getting directory ImageReference for %q", output)
- }
- manifestType = imgspecv1.MediaTypeImageManifest
- case "docker-dir":
- destRef, err = directory.NewReference(output)
- if err != nil {
- return errors.Wrapf(err, "error getting directory ImageReference for %q", output)
- }
- manifestType = manifest.DockerV2Schema2MediaType
- case "docker-archive", "":
- dst := output
- destImageName := imageNameForSaveDestination(newImage, source)
- if destImageName != "" {
- dst = fmt.Sprintf("%s:%s", dst, destImageName)
- }
- destRef, err = dockerarchive.ParseReference(dst) // FIXME? Add dockerarchive.NewReference
- if err != nil {
- return errors.Wrapf(err, "error getting Docker archive ImageReference for %q", dst)
- }
- default:
- return errors.Errorf("unknown format option %q", c.String("format"))
- }
-
- // supports saving multiple tags to the same tar archive
- var additionaltags []reference.NamedTagged
- if len(args) > 1 {
- additionaltags, err = libpodImage.GetAdditionalTags(args[1:])
- if err != nil {
- return err
- }
- }
- if err := newImage.PushImageToReference(getContext(), destRef, manifestType, "", "", writer, c.Bool("compress"), libpodImage.SigningOptions{}, &libpodImage.DockerRegistryOptions{}, additionaltags); err != nil {
- if err2 := os.Remove(output); err2 != nil {
- logrus.Errorf("error deleting %q: %v", output, err)
- }
- return errors.Wrapf(err, "unable to save %q", args)
- }
-
- return nil
-}
-
-// imageNameForSaveDestination returns a Docker-like reference appropriate for saving img,
-// which the user referred to as imgUserInput; or an empty string, if there is no appropriate
-// reference.
-func imageNameForSaveDestination(img *libpodImage.Image, imgUserInput string) string {
- if strings.Contains(img.ID(), imgUserInput) {
- return ""
- }
-
- prepend := ""
- localRegistryPrefix := fmt.Sprintf("%s/", libpodImage.DefaultLocalRegistry)
- if !strings.HasPrefix(imgUserInput, localRegistryPrefix) {
- // we need to check if localhost was added to the image name in NewFromLocal
- for _, name := range img.Names() {
- // If the user is saving an image in the localhost registry, getLocalImage need
- // a name that matches the format localhost/<tag1>:<tag2> or localhost/<tag>:latest to correctly
- // set up the manifest and save.
- if strings.HasPrefix(name, localRegistryPrefix) && (strings.HasSuffix(name, imgUserInput) || strings.HasSuffix(name, fmt.Sprintf("%s:latest", imgUserInput))) {
- prepend = localRegistryPrefix
- break
- }
- }
- }
- return fmt.Sprintf("%s%s", prepend, imgUserInput)
+ return runtime.SaveImage(getContext(), c)
}
diff --git a/cmd/podman/search.go b/cmd/podman/search.go
index c8b64039a..f63131c84 100644
--- a/cmd/podman/search.go
+++ b/cmd/podman/search.go
@@ -1,19 +1,13 @@
package main
import (
- "context"
- "reflect"
- "strconv"
"strings"
- "github.com/containers/image/docker"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/libpod/common"
- sysreg "github.com/containers/libpod/pkg/registries"
+ "github.com/containers/libpod/libpod/image"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -54,30 +48,6 @@ func init() {
flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
}
-type searchParams struct {
- Index string
- Name string
- Description string
- Stars int
- Official string
- Automated string
-}
-
-type searchOpts struct {
- filter []string
- limit int
- noTrunc bool
- format string
- authfile string
- insecureSkipTLSVerify types.OptionalBool
-}
-
-type searchFilterParams struct {
- stars int
- isAutomated *bool
- isOfficial *bool
-}
-
func searchCmd(c *cliconfig.SearchValues) error {
args := c.InputArgs
if len(args) > 1 {
@@ -88,37 +58,29 @@ func searchCmd(c *cliconfig.SearchValues) error {
}
term := args[0]
- // Check if search term has a registry in it
- registry, err := sysreg.GetRegistry(term)
+ filter, err := image.ParseSearchFilter(c.Filter)
if err != nil {
- return errors.Wrapf(err, "error getting registry from %q", term)
- }
- if registry != "" {
- term = term[len(registry)+1:]
+ return err
}
- format := genSearchFormat(c.Format)
- opts := searchOpts{
- format: format,
- noTrunc: c.NoTrunc,
- limit: c.Limit,
- filter: c.Filter,
- authfile: getAuthFile(c.Authfile),
+ searchOptions := image.SearchOptions{
+ NoTrunc: c.NoTrunc,
+ Limit: c.Limit,
+ Filter: *filter,
+ Authfile: getAuthFile(c.Authfile),
}
if c.Flag("tls-verify").Changed {
- opts.insecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify)
- }
- registries, err := getRegistries(registry)
- if err != nil {
- return err
+ searchOptions.InsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify)
}
- filter, err := parseSearchFilter(&opts)
+ results, err := image.SearchImages(term, searchOptions)
if err != nil {
return err
}
-
- return generateSearchOutput(term, registries, opts, *filter)
+ format := genSearchFormat(c.Format)
+ out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: results[0].HeaderMap()}
+ formats.Writer(out).Out()
+ return nil
}
func genSearchFormat(format string) string {
@@ -130,175 +92,9 @@ func genSearchFormat(format string) string {
return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t"
}
-func searchToGeneric(params []searchParams) (genericParams []interface{}) {
+func searchToGeneric(params []image.SearchResult) (genericParams []interface{}) {
for _, v := range params {
genericParams = append(genericParams, interface{}(v))
}
return genericParams
}
-
-func (s *searchParams) headerMap() map[string]string {
- v := reflect.Indirect(reflect.ValueOf(s))
- values := make(map[string]string, v.NumField())
-
- for i := 0; i < v.NumField(); i++ {
- key := v.Type().Field(i).Name
- value := key
- values[key] = strings.ToUpper(splitCamelCase(value))
- }
- return values
-}
-
-// getRegistries returns the list of registries to search, depending on an optional registry specification
-func getRegistries(registry string) ([]string, error) {
- var registries []string
- if registry != "" {
- registries = append(registries, registry)
- } else {
- var err error
- registries, err = sysreg.GetRegistries()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting registries to search")
- }
- }
- return registries, nil
-}
-
-func getSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) ([]searchParams, error) {
- // Max number of queries by default is 25
- limit := maxQueries
- if opts.limit != 0 {
- limit = opts.limit
- }
-
- sc := common.GetSystemContext("", opts.authfile, false)
- sc.DockerInsecureSkipTLSVerify = opts.insecureSkipTLSVerify
- sc.SystemRegistriesConfPath = sysreg.SystemRegistriesConfPath() // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place.
- var paramsArr []searchParams
- for _, reg := range registries {
- results, err := docker.SearchRegistry(context.TODO(), sc, reg, term, limit)
- if err != nil {
- logrus.Errorf("error searching registry %q: %v", reg, err)
- continue
- }
- index := reg
- arr := strings.Split(reg, ".")
- if len(arr) > 2 {
- index = strings.Join(arr[len(arr)-2:], ".")
- }
-
- // limit is the number of results to output
- // if the total number of results is less than the limit, output all
- // if the limit has been set by the user, output those number of queries
- limit := maxQueries
- if len(results) < limit {
- limit = len(results)
- }
- if opts.limit != 0 && opts.limit < len(results) {
- limit = opts.limit
- }
-
- for i := 0; i < limit; i++ {
- if len(opts.filter) > 0 {
- // Check whether query matches filters
- if !(matchesAutomatedFilter(filter, results[i]) && matchesOfficialFilter(filter, results[i]) && matchesStarFilter(filter, results[i])) {
- continue
- }
- }
- official := ""
- if results[i].IsOfficial {
- official = "[OK]"
- }
- automated := ""
- if results[i].IsAutomated {
- automated = "[OK]"
- }
- description := strings.Replace(results[i].Description, "\n", " ", -1)
- if len(description) > 44 && !opts.noTrunc {
- description = description[:descriptionTruncLength] + "..."
- }
- name := reg + "/" + results[i].Name
- if index == "docker.io" && !strings.Contains(results[i].Name, "/") {
- name = index + "/library/" + results[i].Name
- }
- params := searchParams{
- Index: index,
- Name: name,
- Description: description,
- Official: official,
- Automated: automated,
- Stars: results[i].StarCount,
- }
- paramsArr = append(paramsArr, params)
- }
- }
- return paramsArr, nil
-}
-
-func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) error {
- searchOutput, err := getSearchOutput(term, registries, opts, filter)
- if err != nil {
- return err
- }
- if len(searchOutput) == 0 {
- return nil
- }
- out := formats.StdoutTemplateArray{Output: searchToGeneric(searchOutput), Template: opts.format, Fields: searchOutput[0].headerMap()}
- return formats.Writer(out).Out()
-}
-
-func parseSearchFilter(opts *searchOpts) (*searchFilterParams, error) {
- filterParams := &searchFilterParams{}
- ptrTrue := true
- ptrFalse := false
- for _, filter := range opts.filter {
- arr := strings.Split(filter, "=")
- switch arr[0] {
- case "stars":
- if len(arr) < 2 {
- return nil, errors.Errorf("invalid `stars` filter %q, should be stars=<value>", filter)
- }
- stars, err := strconv.Atoi(arr[1])
- if err != nil {
- return nil, errors.Wrapf(err, "incorrect value type for stars filter")
- }
- filterParams.stars = stars
- break
- case "is-automated":
- if len(arr) == 2 && arr[1] == "false" {
- filterParams.isAutomated = &ptrFalse
- } else {
- filterParams.isAutomated = &ptrTrue
- }
- break
- case "is-official":
- if len(arr) == 2 && arr[1] == "false" {
- filterParams.isOfficial = &ptrFalse
- } else {
- filterParams.isOfficial = &ptrTrue
- }
- break
- default:
- return nil, errors.Errorf("invalid filter type %q", filter)
- }
- }
- return filterParams, nil
-}
-
-func matchesStarFilter(filter searchFilterParams, result docker.SearchResult) bool {
- return result.StarCount >= filter.stars
-}
-
-func matchesAutomatedFilter(filter searchFilterParams, result docker.SearchResult) bool {
- if filter.isAutomated != nil {
- return result.IsAutomated == *filter.isAutomated
- }
- return true
-}
-
-func matchesOfficialFilter(filter searchFilterParams, result docker.SearchResult) bool {
- if filter.isOfficial != nil {
- return result.IsOfficial == *filter.isOfficial
- }
- return true
-}
diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index 30dd14845..5f65c40ac 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -26,6 +26,10 @@ func GetPodStatus(pod *libpod.Pod) (string, error) {
if err != nil {
return errored, err
}
+ return CreatePodStatusResults(ctrStatuses)
+}
+
+func CreatePodStatusResults(ctrStatuses map[string]libpod.ContainerStatus) (string, error) {
ctrNum := len(ctrStatuses)
if ctrNum == 0 {
return created, nil
diff --git a/cmd/podman/start.go b/cmd/podman/start.go
index db8abae83..c645a35c4 100644
--- a/cmd/podman/start.go
+++ b/cmd/podman/start.go
@@ -44,6 +44,7 @@ func init() {
flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&startCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true if attaching, false otherwise)")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func startCmd(c *cliconfig.StartValues) error {
diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go
index 642e54f49..2bbcd0a17 100644
--- a/cmd/podman/stats.go
+++ b/cmd/podman/stats.go
@@ -41,6 +41,9 @@ var (
statsCommand.GlobalFlags = MainGlobalOpts
return statsCmd(&statsCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman stats --all --no-stream
podman stats ctrID
podman stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`,
@@ -56,6 +59,7 @@ func init() {
flags.BoolVarP(&statsCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&statsCommand.NoReset, "no-reset", false, "Disable resetting the screen between intervals")
flags.BoolVar(&statsCommand.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func statsCmd(c *cliconfig.StatsValues) error {
diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go
index 94fdf321e..67c15b2a8 100644
--- a/cmd/podman/stop.go
+++ b/cmd/podman/stop.go
@@ -32,6 +32,9 @@ var (
stopCommand.GlobalFlags = MainGlobalOpts
return stopCmd(&stopCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
Example: `podman stop ctrID
podman stop --latest
podman stop --timeout 2 mywebserver 6e534f14da9d`,
@@ -46,6 +49,7 @@ func init() {
flags.BoolVarP(&stopCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.UintVar(&stopCommand.Timeout, "time", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
flags.UintVarP(&stopCommand.Timeout, "timeout", "t", libpod.CtrRemoveTimeout, "Seconds to wait for stop before killing the container")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func stopCmd(c *cliconfig.StopValues) error {
@@ -54,10 +58,6 @@ func stopCmd(c *cliconfig.StopValues) error {
defer span.Finish()
}
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
-
rootless.SetSkipStorageSetup(true)
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
if err != nil {
diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go
index a91d7bf0a..a823dcad1 100644
--- a/cmd/podman/system_prune.go
+++ b/cmd/podman/system_prune.go
@@ -8,7 +8,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
diff --git a/cmd/podman/system_renumber.go b/cmd/podman/system_renumber.go
new file mode 100644
index 000000000..c8ce628b1
--- /dev/null
+++ b/cmd/podman/system_renumber.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ renumberCommand cliconfig.SystemRenumberValues
+ renumberDescription = `
+ podman system renumber
+
+ Migrate lock numbers to handle a change in maximum number of locks.
+ Mandatory after the number of locks in libpod.conf is changed.
+`
+
+ _renumberCommand = &cobra.Command{
+ Use: "renumber",
+ Short: "Migrate lock numbers",
+ Long: renumberDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ renumberCommand.InputArgs = args
+ renumberCommand.GlobalFlags = MainGlobalOpts
+ return renumberCmd(&renumberCommand)
+ },
+ }
+)
+
+func init() {
+ renumberCommand.Command = _renumberCommand
+ renumberCommand.SetUsageTemplate(UsageTemplate())
+}
+
+func renumberCmd(c *cliconfig.SystemRenumberValues) error {
+ // We need to pass one extra option to NewRuntime.
+ // This will inform the OCI runtime to start a renumber.
+ // That's controlled by the last argument to GetRuntime.
+ r, err := libpodruntime.GetRuntimeRenumber(&c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "error renumbering locks")
+ }
+ if err := r.Shutdown(false); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/podman/tag.go b/cmd/podman/tag.go
index 422e9dbf6..2b9d67066 100644
--- a/cmd/podman/tag.go
+++ b/cmd/podman/tag.go
@@ -2,7 +2,7 @@ package main
import (
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/top.go b/cmd/podman/top.go
index d85e1be53..36d6bb6b4 100644
--- a/cmd/podman/top.go
+++ b/cmd/podman/top.go
@@ -55,6 +55,7 @@ func init() {
flags.BoolVar(&topCommand.ListDescriptors, "list-descriptors", false, "")
flags.MarkHidden("list-descriptors")
flags.BoolVarP(&topCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func topCmd(c *cliconfig.TopValues) error {
diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go
index afa0e86db..6d9009388 100644
--- a/cmd/podman/umount.go
+++ b/cmd/podman/umount.go
@@ -31,6 +31,9 @@ An unmount can be forced with the --force flag.
umountCommand.GlobalFlags = MainGlobalOpts
return umountCmd(&umountCommand)
},
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, true)
+ },
Example: `podman umount ctrID
podman umount ctrID1 ctrID2 ctrID3
podman umount --all`,
@@ -44,6 +47,7 @@ func init() {
flags.BoolVarP(&umountCommand.All, "all", "a", false, "Umount all of the currently mounted containers")
flags.BoolVarP(&umountCommand.Force, "force", "f", false, "Force the complete umount all of the currently mounted containers")
flags.BoolVarP(&umountCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func umountCmd(c *cliconfig.UmountValues) error {
@@ -55,9 +59,6 @@ func umountCmd(c *cliconfig.UmountValues) error {
force := c.Force
umountAll := c.All
- if err := checkAllAndLatest(&c.PodmanCommand); err != nil {
- return err
- }
containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all")
if err != nil {
diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go
index c76e7f2a4..4ec0f8a13 100644
--- a/cmd/podman/utils.go
+++ b/cmd/podman/utils.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
+ "github.com/spf13/pflag"
"os"
gosignal "os/signal"
@@ -158,13 +159,6 @@ func (f *RawTtyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
return bytes, err
}
-func checkMutuallyExclusiveFlags(c *cliconfig.PodmanCommand) error {
- if err := checkAllAndLatest(c); err != nil {
- return err
- }
- return nil
-}
-
// For pod commands that have a latest and all flag, getPodsFromContext gets
// pods the user specifies. If there's an error before getting pods, the pods slice
// will be empty and error will be not nil. If an error occured after, the pod slice
@@ -178,7 +172,7 @@ func getPodsFromContext(c *cliconfig.PodmanCommand, r *libpod.Runtime) ([]*libpo
var err error
if c.Bool("all") {
- pods, err = r.Pods()
+ pods, err = r.GetAllPods()
if err != nil {
return nil, errors.Wrapf(err, "unable to get running pods")
}
@@ -251,3 +245,11 @@ func printParallelOutput(m map[string]error, errCount int) error {
}
return lastError
}
+
+// markFlagHiddenForRemoteClient makes the flag not appear as part of the CLI
+// on the remote-client
+func markFlagHiddenForRemoteClient(flagName string, flags *pflag.FlagSet) {
+ if remoteclient {
+ flags.MarkHidden(flagName)
+ }
+}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 90cadf2b4..618af3481 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -26,6 +26,16 @@ type ContainerChanges (
deleted: []string
)
+type ImageSaveOptions (
+ name: string,
+ format: string,
+ output: string,
+ outputType: string,
+ moreTags: []string,
+ quiet: bool,
+ compress: bool
+)
+
type VolumeCreateOpts (
volumeName: string,
driver: string,
@@ -72,6 +82,12 @@ type ImageSearchResult (
star_count: int
)
+type ImageSearchFilter (
+ is_official: ?bool,
+ is_automated: ?bool,
+ star_count: int
+)
+
type Container (
id: string,
image: string,
@@ -681,7 +697,7 @@ method RemoveImage(name: string, force: bool) -> (image: string)
# SearchImages searches available registries for images that contain the
# contents of "query" in their name. If "limit" is given, limits the amount of
# search results per registry.
-method SearchImages(query: string, limit: ?int, tlsVerify: ?bool) -> (results: []ImageSearchResult)
+method SearchImages(query: string, limit: ?int, tlsVerify: ?bool, filter: ImageSearchFilter) -> (results: []ImageSearchResult)
# DeleteUnusedImages deletes any images not associated with a container. The IDs of the deleted images are returned
# in a string array.
@@ -719,16 +735,10 @@ method ImportImage(source: string, reference: string, message: string, changes:
# error will be returned. See also [ImportImage](ImportImage).
method ExportImage(name: string, destination: string, compress: bool, tags: []string) -> (image: string)
-# PullImage pulls an image from a repository to local storage. After the pull is successful, the ID of the image
-# is returned.
-# #### Example
-# ~~~
-# $ varlink call -m unix:/run/podman/io.podman/io.podman.PullImage '{"name": "registry.fedoraproject.org/fedora"}'
-# {
-# "id": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e"
-# }
-# ~~~
-method PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ?bool) -> (id: string)
+# PullImage pulls an image from a repository to local storage. After a successful pull, the image id and logs
+# are returned as a [MoreResponse](#MoreResponse). This connection also will handle a WantsMores request to send
+# status as it occurs.
+method PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ?bool) -> (reply: MoreResponse)
# CreatePod creates a new empty pod. It uses a [PodCreate](#PodCreate) type for input.
# On success, the ID of the newly created pod will be returned.
@@ -1072,6 +1082,10 @@ method ContainerInspectData(name: string) -> (config: string)
# development of Podman only and generally should not be used.
method ContainerStateData(name: string) -> (config: string)
+# PodStateData returns inspectr level information of a given pod in string form. This call is for
+# development of Podman only and generally should not be used.
+method PodStateData(name: string) -> (config: string)
+
# Sendfile allows a remote client to send a file to the host
method SendFile(type: string, length: int) -> (file_handle: string)
@@ -1090,17 +1104,31 @@ method GetVolumes(args: []string, all: bool) -> (volumes: []Volume)
# VolumesPrune removes unused volumes on the host
method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string)
+# ImageSave allows you to save an image from the local image storage to a tarball
+method ImageSave(options: ImageSaveOptions) -> (reply: MoreResponse)
+
+# GetPodsByContext allows you to get a list pod ids depending on all, latest, or a list of
+# pod names. The definition of latest pod means the latest by creation date. In a multi-
+# user environment, results might differ from what you expect.
+method GetPodsByContext(all: bool, latest: bool, args: []string) -> (pods: []string)
+
+# LoadImage allows you to load an image into local storage from a tarball.
+method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) -> (reply: MoreResponse)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
-error ImageNotFound (id: string)
+error ImageNotFound (id: string, reason: string)
# ContainerNotFound means the container could not be found by the provided name or ID in local storage.
-error ContainerNotFound (id: string)
+error ContainerNotFound (id: string, reason: string)
# NoContainerRunning means none of the containers requested are running in a command that requires a running container.
error NoContainerRunning ()
# PodNotFound means the pod could not be found by the provided name or ID in local storage.
-error PodNotFound (name: string)
+error PodNotFound (name: string, reason: string)
+
+# VolumeNotFound means the volume could not be found by the name or ID in local storage.
+error VolumeNotFound (id: string, reason: string)
# PodContainerError means a container associated with a pod failed to preform an operation. It contains
# a container ID of the container that failed.
diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go
index 6c8a78922..833191082 100644
--- a/cmd/podman/volume_create.go
+++ b/cmd/podman/volume_create.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go
index 3b4ba51d5..dc6afbc36 100644
--- a/cmd/podman/volume_inspect.go
+++ b/cmd/podman/volume_inspect.go
@@ -2,7 +2,7 @@ package main
import (
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go
index 0edadc5ac..5adfc1e91 100644
--- a/cmd/podman/volume_ls.go
+++ b/cmd/podman/volume_ls.go
@@ -6,7 +6,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
diff --git a/cmd/podman/volume_prune.go b/cmd/podman/volume_prune.go
index a2205140f..1f7931aa4 100644
--- a/cmd/podman/volume_prune.go
+++ b/cmd/podman/volume_prune.go
@@ -8,7 +8,7 @@ import (
"strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
diff --git a/cmd/podman/volume_rm.go b/cmd/podman/volume_rm.go
index f301749e9..03b6ccae1 100644
--- a/cmd/podman/volume_rm.go
+++ b/cmd/podman/volume_rm.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -28,7 +28,9 @@ not being used by any containers. To remove the volumes anyways, use the
volumeRmCommand.GlobalFlags = MainGlobalOpts
return volumeRmCmd(&volumeRmCommand)
},
- Example: "[VOLUME-NAME ...]",
+ Example: `podman volume rm myvol1 myvol2
+ podman volume rm --all
+ podman volume rm --force myvol`,
}
)
diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go
index 616c8feb5..9df7cdbae 100644
--- a/cmd/podman/wait.go
+++ b/cmd/podman/wait.go
@@ -28,7 +28,9 @@ var (
waitCommand.GlobalFlags = MainGlobalOpts
return waitCmd(&waitCommand)
},
- Example: "CONTAINER-NAME [CONTAINER-NAME ...]",
+ Example: `podman wait --latest
+ podman wait --interval 5000 ctrID
+ podman wait ctrID1 ctrID2`,
}
)
@@ -38,6 +40,7 @@ func init() {
flags := waitCommand.Flags()
flags.UintVarP(&waitCommand.Interval, "interval", "i", 250, "Milliseconds to wait before polling for completion")
flags.BoolVarP(&waitCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
}
func waitCmd(c *cliconfig.WaitValues) error {
diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh
index 58c8af289..0fd86dfdc 100755
--- a/contrib/cirrus/integration_test.sh
+++ b/contrib/cirrus/integration_test.sh
@@ -19,7 +19,7 @@ case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in
ubuntu-18)
make install PREFIX=/usr ETCDIR=/etc
make test-binaries
- SKIP_USERNS=1 make localintegration
+ SKIP_USERNS=1 make localintegration GINKGOTIMEOUT=90m
;;
fedora-29) ;& # Continue to the next item
fedora-28) ;&
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 39e6c7699..8be696933 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -325,7 +325,7 @@ install_packer_copied_files(){
/etc/containers/registries.d/registry.access.redhat.com.yaml
}
-install_varlink(){
+install_varlink() {
echo "Installing varlink from the cheese-factory"
ooe.sh sudo -H pip3 install varlink
}
diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh
index 01c468901..de7ad4506 100644
--- a/contrib/cirrus/packer/fedora_setup.sh
+++ b/contrib/cirrus/packer/fedora_setup.sh
@@ -40,6 +40,7 @@ ooe.sh sudo dnf install -y \
golang-github-cpuguy83-go-md2man \
gpgme-devel \
iptables \
+ iproute \
libassuan-devel \
libcap-devel \
libnet \
diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh
index af5671c90..5b7e1d714 100644
--- a/contrib/cirrus/packer/ubuntu_setup.sh
+++ b/contrib/cirrus/packer/ubuntu_setup.sh
@@ -28,7 +28,13 @@ export DEBIAN_FRONTEND=noninteractive
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 \
+ooe.sh sudo -E apt-get -qq install software-properties-common
+
+# Required to have Go 1.11 on Ubuntu 18.0.4
+ooe.sh sudo -E add-apt-repository --yes ppa:longsleep/golang-backports
+ooe.sh sudo -E apt-get -qq update || sudo -E apt-get -qq update
+
+ooe.sh sudo -E apt-get -qq install \
apparmor \
autoconf \
automake \
@@ -42,6 +48,7 @@ ooe.sh sudo -E apt-get -qq install --no-install-recommends \
gettext \
go-md2man \
golang \
+ iproute \
iptables \
libaio-dev \
libapparmor-dev \
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index 838f3c3f3..77c20d9bd 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -79,8 +79,9 @@ then
cd "${GOSRC}/"
source "$SCRIPT_BASE/lib.sh"
- # Only testing-VMs need deps installed
- [[ -n "$PACKER_BUILDS" ]] || install_testing_dependencies # must exist in $GOPATH
+ # Only testing-VMs need deps installed, not image-builder VM
+ echo "$CIRRUS_TASK_NAME" | grep -q 'image' || \
+ install_testing_dependencies # must exist in $GOPATH
fi
record_timestamp "env. setup end"
diff --git a/docs/podman-attach.1.md b/docs/podman-attach.1.md
index 4322ca2be..ceb945a0e 100644
--- a/docs/podman-attach.1.md
+++ b/docs/podman-attach.1.md
@@ -24,6 +24,8 @@ ctrl-[value] where [value] is one of: a-z, @, ^, [, , or _.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--no-stdin**
Do not attach STDIN. The default is false.
diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md
index 76e16b42c..fdae48b93 100644
--- a/docs/podman-build.1.md
+++ b/docs/podman-build.1.md
@@ -362,6 +362,12 @@ Specifies the name which will be assigned to the resulting image if the build
process completes successfully.
If _imageName_ does not include a registry name, the registry name *localhost* will be prepended to the image name.
+**--target** *stageName*
+
+Set the target build stage to build. When building a Dockerfile with multiple build stages, --target
+can be used to specify an intermediate build stage by name as the final stage for the resulting image.
+Commands after the target stage will be skipped.
+
**--tls-verify** *bool-value*
Require HTTPS and verify certificates when talking to container registries (defaults to true).
diff --git a/docs/podman-container-checkpoint.1.md b/docs/podman-container-checkpoint.1.md
index 94e52dc78..666dbbb80 100644
--- a/docs/podman-container-checkpoint.1.md
+++ b/docs/podman-container-checkpoint.1.md
@@ -25,6 +25,8 @@ Checkpoint all running containers.
Instead of providing the container name or ID, checkpoint the last created container.
+The latest option is not supported on the remote client.
+
**--leave-running, -R**
Leave the container running after checkpointing instead of stopping it.
diff --git a/docs/podman-container-cleanup.1.md b/docs/podman-container-cleanup.1.md
index 2819d1138..2ad39d214 100644
--- a/docs/podman-container-cleanup.1.md
+++ b/docs/podman-container-cleanup.1.md
@@ -19,6 +19,9 @@ Cleanup all containers.
**--latest, -l**
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+
+The latest option is not supported on the remote client.
+
## EXAMPLE
`podman container cleanup mywebserver`
@@ -27,7 +30,7 @@ to run containers such as CRI-O, the last started container could be from either
`podman container cleanup 860a4b23`
-`podman container-cleanup -a`
+`podman container cleanup -a`
`podman container cleanup --latest`
diff --git a/docs/podman-container-restore.1.md b/docs/podman-container-restore.1.md
index 44219f3ef..e47d585cc 100644
--- a/docs/podman-container-restore.1.md
+++ b/docs/podman-container-restore.1.md
@@ -32,6 +32,8 @@ Restore all checkpointed containers.
Instead of providing the container name or ID, restore the last created container.
+The latest option is not supported on the remote client.
+
**--tcp-established**
Restore a container with established TCP connections. If the checkpoint image
diff --git a/docs/podman-diff.1.md b/docs/podman-diff.1.md
index dd5f0edcf..8837b744f 100644
--- a/docs/podman-diff.1.md
+++ b/docs/podman-diff.1.md
@@ -15,6 +15,12 @@ Displays changes on a container or image's filesystem. The container or image w
Alter the output into a different format. The only valid format for diff is `json`.
+**--latest, -l**
+
+Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
+to run containers such as CRI-O, the last started container could be from either of those methods.
+
+The latest option is not supported on the remote client.
## EXAMPLE
diff --git a/docs/podman-exec.1.md b/docs/podman-exec.1.md
index 6f464a8f2..14088b468 100644
--- a/docs/podman-exec.1.md
+++ b/docs/podman-exec.1.md
@@ -24,6 +24,8 @@ Not supported. All exec commands are interactive by default.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--privileged**
Give the process extended Linux capabilities when running the command in container.
diff --git a/docs/podman-inspect.1.md b/docs/podman-inspect.1.md
index b01bc0f4e..5748f29f4 100644
--- a/docs/podman-inspect.1.md
+++ b/docs/podman-inspect.1.md
@@ -27,6 +27,8 @@ The keys of the returned JSON can be used as the values for the --format flag (s
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--size, -s**
Display the total file size if the type is a container
diff --git a/docs/podman-kill.1.md b/docs/podman-kill.1.md
index 85f68a73d..1c14b71d5 100644
--- a/docs/podman-kill.1.md
+++ b/docs/podman-kill.1.md
@@ -19,6 +19,8 @@ Signal all running containers. This does not include paused containers.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--signal, s**
Signal to send to the container. For more information on Linux signals, refer to *man signal(7)*.
diff --git a/docs/podman-load.1.md b/docs/podman-load.1.md
index eca7ecb2e..8b6501a5c 100644
--- a/docs/podman-load.1.md
+++ b/docs/podman-load.1.md
@@ -22,7 +22,9 @@ Note: `:` is a restricted character and cannot be part of the file name.
**--input, -i**
-Read from archive file, default is STDIN
+Read from archive file, default is STDIN.
+
+The remote client requires the use of this option.
**--quiet, -q**
diff --git a/docs/podman-logs.1.md b/docs/podman-logs.1.md
index 71a798bc0..bc02df954 100644
--- a/docs/podman-logs.1.md
+++ b/docs/podman-logs.1.md
@@ -17,6 +17,13 @@ any logs at the time you execute podman logs
Follow log output. Default is false
+**--latest, -l**
+
+Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
+to run containers such as CRI-O, the last started container could be from either of those methods.
+
+The latest option is not supported on the remote client.
+
**--since=TIMESTAMP**
Show logs since TIMESTAMP. The --since option can be Unix timestamps, date formatted timestamps, or Go duration
@@ -61,7 +68,7 @@ podman logs --tail 2 b3f2436bdb97
To view a containers logs since a certain time:
```
-podman logs 224c375f27cd --since 2017-08-07T10:10:09.055837383-04:00 myserver
+podman logs --since 2017-08-07T10:10:09.055837383-04:00 myserver
1:M 07 Aug 14:10:09.055 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 07 Aug 14:10:09.055 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
@@ -72,7 +79,7 @@ podman logs 224c375f27cd --since 2017-08-07T10:10:09.055837383-04:00 myserver
To view a container's logs generated in the last 10 minutes:
```
-podman logs 224c375f27cd --since 10m myserver
+podman logs --since 10m myserver
1:M 07 Aug 14:10:09.055 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 07 Aug 14:10:09.055 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
diff --git a/docs/podman-mount.1.md b/docs/podman-mount.1.md
index 2cccf5ee0..e244e5daf 100644
--- a/docs/podman-mount.1.md
+++ b/docs/podman-mount.1.md
@@ -33,6 +33,8 @@ Instead of providing the container name or ID, use the last created container.
If you use methods other than Podman to run containers such as CRI-O, the last
started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--notruncate**
Do not truncate IDs in output.
diff --git a/docs/podman-pod-inspect.1.md b/docs/podman-pod-inspect.1.md
index b8fb143ba..a4e6a5559 100644
--- a/docs/podman-pod-inspect.1.md
+++ b/docs/podman-pod-inspect.1.md
@@ -16,6 +16,7 @@ that belong to the pod.
Instead of providing the pod name or ID, use the last created pod. If you use methods other than Podman
to run pods such as CRI-O, the last started pod could be from either of those methods.
+The latest option is not supported on the remote client.
## EXAMPLE
```
diff --git a/docs/podman-pod-kill.1.md b/docs/podman-pod-kill.1.md
index 4376c2d29..2a863d3d9 100644
--- a/docs/podman-pod-kill.1.md
+++ b/docs/podman-pod-kill.1.md
@@ -19,6 +19,8 @@ Sends signal to all containers associated with a pod.
Instead of providing the pod name or ID, use the last created pod. If you use methods other than Podman
to run pods such as CRI-O, the last started pod could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--signal, s**
Signal to send to the containers in the pod. For more information on Linux signals, refer to *man signal(7)*.
diff --git a/docs/podman-pod-pause.1.md b/docs/podman-pod-pause.1.md
index 32c5b927e..418a9ee2a 100644
--- a/docs/podman-pod-pause.1.md
+++ b/docs/podman-pod-pause.1.md
@@ -19,6 +19,8 @@ Pause all pods.
Instead of providing the pod name or ID, pause the last created pod.
+The latest option is not supported on the remote client.
+
## EXAMPLE
podman pod pause mywebserverpod
diff --git a/docs/podman-pod-ps.1.md b/docs/podman-pod-ps.1.md
index 24b343438..ee215154a 100644
--- a/docs/podman-pod-ps.1.md
+++ b/docs/podman-pod-ps.1.md
@@ -42,6 +42,8 @@ Includes the container statuses in the container info field
Show the latest pod created (all states)
+The latest option is not supported on the remote client.
+
**--no-trunc**
Display the extended information
diff --git a/docs/podman-pod-restart.1.md b/docs/podman-pod-restart.1.md
index 17fb4ed4c..cd6e5c8ce 100644
--- a/docs/podman-pod-restart.1.md
+++ b/docs/podman-pod-restart.1.md
@@ -22,8 +22,11 @@ Restarts all pods
Instead of providing the pod name or ID, restart the last created pod.
+The latest option is not supported on the remote client.
+
## EXAMPLE
+```
podman pod restart mywebserverpod
cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907
@@ -31,8 +34,6 @@ podman pod restart 490eb 3557fb
490eb241aaf704d4dd2629904410fe4aa31965d9310a735f8755267f4ded1de5
3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
-3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
-
podman pod restart --latest
3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
@@ -42,6 +43,7 @@ podman pod restart --all
490eb241aaf704d4dd2629904410fe4aa31965d9310a735f8755267f4ded1de5
70c358daecf71ef9be8f62404f926080ca0133277ef7ce4f6aa2d5af6bb2d3e9
cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907
+```
## SEE ALSO
podman-pod(1), podman-pod-start(1), podman-restart(1)
diff --git a/docs/podman-pod-rm.1.md b/docs/podman-pod-rm.1.md
index 7a300c152..aa26a1bbb 100644
--- a/docs/podman-pod-rm.1.md
+++ b/docs/podman-pod-rm.1.md
@@ -19,6 +19,8 @@ Remove all pods. Can be used in conjunction with \-f as well.
Instead of providing the pod name or ID, remove the last created pod.
+The latest option is not supported on the remote client.
+
**--force, f**
Stop running containers and delete all stopped containers before removal of pod.
diff --git a/docs/podman-pod-start.1.md b/docs/podman-pod-start.1.md
index 253bb5f53..27e40740f 100644
--- a/docs/podman-pod-start.1.md
+++ b/docs/podman-pod-start.1.md
@@ -20,6 +20,8 @@ Starts all pods
Instead of providing the pod name or ID, start the last created pod.
+The latest option is not supported on the remote client.
+
## EXAMPLE
podman pod start mywebserverpod
diff --git a/docs/podman-pod-stop.1.md b/docs/podman-pod-stop.1.md
index 7544f8bf7..338e04d67 100644
--- a/docs/podman-pod-stop.1.md
+++ b/docs/podman-pod-stop.1.md
@@ -19,6 +19,8 @@ Stops all pods
Instead of providing the pod name or ID, stop the last created pod.
+The latest option is not supported on the remote client.
+
**--timeout, --time, t**
Timeout to wait before forcibly stopping the containers in the pod.
diff --git a/docs/podman-pod-top.1.md b/docs/podman-pod-top.1.md
index 0b330eb03..a77ca2b37 100644
--- a/docs/podman-pod-top.1.md
+++ b/docs/podman-pod-top.1.md
@@ -19,6 +19,8 @@ Display the running process of containers in a pod. The *format-descriptors* are
Instead of providing the pod name or ID, use the last created pod.
+The latest option is not supported on the remote client.
+
## FORMAT DESCRIPTORS
The following descriptors are supported in addition to the AIX format descriptors mentioned in ps (1):
diff --git a/docs/podman-pod-unpause.1.md b/docs/podman-pod-unpause.1.md
index 643544ef1..1004e09f9 100644
--- a/docs/podman-pod-unpause.1.md
+++ b/docs/podman-pod-unpause.1.md
@@ -19,6 +19,8 @@ Unpause all pods.
Instead of providing the pod name or ID, unpause the last created pod.
+The latest option is not supported on the remote client.
+
## EXAMPLE
podman pod unpause mywebserverpod
diff --git a/docs/podman-port.1.md b/docs/podman-port.1.md
index 874ea5036..020a25d32 100644
--- a/docs/podman-port.1.md
+++ b/docs/podman-port.1.md
@@ -21,11 +21,13 @@ or private ports/protocols as filters.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
## EXAMPLE
List all port mappings
```
-#podman port -a
+# podman port -a
b4d2f05432e482e017b1a4b2eae15fa7b4f6fb7e9f65c1bde46294fdef285906
80/udp -> 0.0.0.0:44327
80/tcp -> 0.0.0.0:44327
@@ -34,21 +36,21 @@ b4d2f05432e482e017b1a4b2eae15fa7b4f6fb7e9f65c1bde46294fdef285906
List port mappings for a specific container
```
-#podman port b4d2f054
+# podman port b4d2f054
80/udp -> 0.0.0.0:44327
80/tcp -> 0.0.0.0:44327
#
```
List the port mappings for the latest container and port 80
```
-#podman port b4d2f054 80
+# podman port b4d2f054 80
0.0.0.0:44327
#
```
List the port mappings for a specific container for port 80 and the tcp protocol.
```
-#podman port b4d2f054 80/tcp
+# podman port b4d2f054 80/tcp
0.0.0.0:44327
#
```
diff --git a/docs/podman-ps.1.md b/docs/podman-ps.1.md
index 8b86703d8..b8b1c3d62 100644
--- a/docs/podman-ps.1.md
+++ b/docs/podman-ps.1.md
@@ -75,6 +75,8 @@ Print the n last created containers (all states)
Show the latest container created (all states)
+The latest option is not supported on the remote client.
+
**--namespace, --ns**
Display namespace information
diff --git a/docs/podman-restart.1.md b/docs/podman-restart.1.md
index 875afa385..dd28e34c7 100644
--- a/docs/podman-restart.1.md
+++ b/docs/podman-restart.1.md
@@ -19,6 +19,8 @@ Restart all containers regardless of their current state.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--running**
Restart all containers that are already in the *running* state.
diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md
index f4513c2be..dc1729188 100644
--- a/docs/podman-rm.1.md
+++ b/docs/podman-rm.1.md
@@ -1,9 +1,11 @@
-% podman-rm(1)
+% podman-container-rm(1)
## NAME
-podman\-rm - Remove one or more containers
+podman\-container\-rm (podman\-rm) - Remove one or more containers
## SYNOPSIS
+**podman container rm** [*options*] *container*
+
**podman rm** [*options*] *container*
## DESCRIPTION
@@ -26,6 +28,8 @@ Containers could have been created by a different container engine.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--volumes, -v**
Remove the volumes associated with the container. (Not yet implemented)
@@ -55,8 +59,13 @@ Forcibly remove the latest container created.
podman rm -f --latest
```
+## Exit Status
+**_0_** if all specified containers removed
+**_1_** if one of the specified containers did not exist, and no other failures
+**_125_** if command fails for a reason other then an container did not exist
+
## SEE ALSO
-podman(1), podman-rmi(1)
+podman(1), podman-image-rm(1)
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
diff --git a/docs/podman-rmi.1.md b/docs/podman-rmi.1.md
index 9c080c9f1..8c22bba2c 100644
--- a/docs/podman-rmi.1.md
+++ b/docs/podman-rmi.1.md
@@ -1,9 +1,11 @@
-% podman-rmi(1)
+% podman-image-rm(1)
## NAME
-podman\-rmi - Removes one or more images
+podman\-image\-rm (podman\-rmi) - Removes one or more images
## SYNOPSIS
+**podman image rm** *image* ...
+
**podman rmi** *image* ...
## DESCRIPTION
@@ -38,6 +40,10 @@ Remove all images and containers.
```
podman rmi -a -f
```
+## Exit Status
+**_0_** if all specified images removed
+**_1_** if one of the specified images did not exist, and no other failures
+**_125_** if command fails for a reason other then an image did not exist
## SEE ALSO
podman(1)
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index b928f61f5..bbf10a2ce 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -28,6 +28,8 @@ servers in the created `resolv.conf`). Additionally, an empty file is created in
each container to indicate to programs they are running in a container. This file
is located at `/run/.containerenv`.
+When running from a user defined network namespace, the /etc/netns/NSNAME/resolv.conf will be used if it exists, otherwise /etc/resolv.conf will be used.
+
## OPTIONS
**--add-host**=[]
@@ -694,21 +696,21 @@ Current supported mount TYPES are bind, and tmpfs.
Common Options:
- · src, source: mount source spec for bind and volume. Mandatory for bind.
+ · src, source: mount source spec for bind and volume. Mandatory for bind.
- · dst, destination, target: mount destination spec.
+ · dst, destination, target: mount destination spec.
- · ro, read-only: true or false (default).
+ · ro, read-only: true or false (default).
Options specific to bind:
- · bind-propagation: Z, z, shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
+ · bind-propagation: Z, z, shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
Options specific to tmpfs:
- · tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
+ · tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
- · tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
+ · tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
**--userns**=""
diff --git a/docs/podman-start.1.md b/docs/podman-start.1.md
index 786936721..b0167003e 100644
--- a/docs/podman-start.1.md
+++ b/docs/podman-start.1.md
@@ -33,6 +33,7 @@ Attach container's STDIN. The default is false.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
**--sig-proxy**=*true*|*false*
diff --git a/docs/podman-stats.1.md b/docs/podman-stats.1.md
index d0b56b2e6..97a9be961 100644
--- a/docs/podman-stats.1.md
+++ b/docs/podman-stats.1.md
@@ -20,6 +20,8 @@ Show all containers. Only running containers are shown by default
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--no-reset**
Do not clear the terminal/screen in between reporting intervals
diff --git a/docs/podman-stop.1.md b/docs/podman-stop.1.md
index e36265d5e..2016a7301 100644
--- a/docs/podman-stop.1.md
+++ b/docs/podman-stop.1.md
@@ -24,6 +24,8 @@ Stop all running containers. This does not include paused containers.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
**--timeout, --time, t**
Timeout to wait before forcibly stopping the container
diff --git a/docs/podman-system-renumber.1.md b/docs/podman-system-renumber.1.md
new file mode 100644
index 000000000..a88640d63
--- /dev/null
+++ b/docs/podman-system-renumber.1.md
@@ -0,0 +1,29 @@
+% podman-system-renumber(1) podman
+
+## NAME
+podman\-system\-renumber - Renumber container locks
+
+## SYNOPSIS
+** podman system renumber**
+
+## DESCRIPTION
+** podman system renumber** renumbers locks used by containers and pods.
+
+Each Podman container and pod is allocated a lock at creation time, up to a maximum number controlled by the **num_locks** parameter in **libpod.conf**.
+
+When all available locks are exhausted, no further containers and pods can be created until some existing containers and pods are removed. This can be avoided by increasing the number of locks available via modifying **libpod.conf** and subsequently running **podman system renumber** to prepare the new locks (and reallocate lock numbers to fit the new struct).
+
+**podman system renumber** must be called after any changes to **num_locks** - failure to do so will result in errors starting Podman as the number of locks available conflicts with the configured number of locks.
+
+**podman system renumber** can also be used to migrate 1.0 and earlier versions of Podman, which used a different locking scheme, to the new locking model. It is not strictly required to do this, but it is highly recommended to do so as deadlocks can occur otherwise.
+
+If possible, avoid calling **podman system renumber** while there are other Podman processes running.
+
+## SYNOPSIS
+**podman system renumber**
+
+## SEE ALSO
+`podman(1)`, `libpod.conf(5)`
+
+# HISTORY
+February 2019, Originally compiled by Matt Heon (mheon at redhat dot com)
diff --git a/docs/podman-top.1.md b/docs/podman-top.1.md
index 7782a3f29..52d1238ef 100644
--- a/docs/podman-top.1.md
+++ b/docs/podman-top.1.md
@@ -20,6 +20,8 @@ Display the running process of the container. The *format-descriptors* are ps (1
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
## FORMAT DESCRIPTORS
The following descriptors are supported in addition to the AIX format descriptors mentioned in ps (1):
diff --git a/docs/podman-umount.1.md b/docs/podman-umount.1.md
index cceb63019..795f0402d 100644
--- a/docs/podman-umount.1.md
+++ b/docs/podman-umount.1.md
@@ -35,6 +35,8 @@ Instead of providing the container name or ID, use the last created container.
If you use methods other than Podman to run containers such as CRI-O, the last
started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
## EXAMPLE
podman umount containerID
diff --git a/docs/podman-wait.1.md b/docs/podman-wait.1.md
index dd5dc7907..672316eef 100644
--- a/docs/podman-wait.1.md
+++ b/docs/podman-wait.1.md
@@ -22,9 +22,11 @@ After the container stops, the container's return code is printed.
**--latest, -l**
- Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
+Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods.
+The latest option is not supported on the remote client.
+
## EXAMPLES
```
diff --git a/hack/tree_status.sh b/hack/tree_status.sh
index 12c1d1ee5..78fb4c6a3 100755
--- a/hack/tree_status.sh
+++ b/hack/tree_status.sh
@@ -8,6 +8,6 @@ then
else
echo "tree is dirty, please commit all changes and sync the vendor.conf"
echo ""
- echo $STATUS
+ echo "$STATUS"
exit 1
fi
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 5bc15dd7f..25ef5cd0e 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -783,6 +783,94 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// RewriteContainerConfig rewrites a container's configuration.
+// WARNING: This function is DANGEROUS. Do not use without reading the full
+// comment on this function in state.go.
+func (s *BoltState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ newCfgJSON, err := json.Marshal(newCfg)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling new configuration JSON for container %s", ctr.ID())
+ }
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer s.closeDBCon(db)
+
+ err = db.Update(func(tx *bolt.Tx) error {
+ ctrBkt, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ ctrDB := ctrBkt.Bucket([]byte(ctr.ID()))
+ if ctrDB == nil {
+ ctr.valid = false
+ return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in DB", ctr.ID())
+ }
+
+ if err := ctrDB.Put(configKey, newCfgJSON); err != nil {
+ return errors.Wrapf(err, "error updating container %s config JSON", ctr.ID())
+ }
+
+ return nil
+ })
+ return err
+}
+
+// RewritePodConfig rewrites a pod's configuration.
+// WARNING: This function is DANGEROUS. Do not use without reading the full
+// comment on this function in state.go.
+func (s *BoltState) RewritePodConfig(pod *Pod, newCfg *PodConfig) error {
+ if !s.valid {
+ return ErrDBClosed
+ }
+
+ if !pod.valid {
+ return ErrPodRemoved
+ }
+
+ newCfgJSON, err := json.Marshal(newCfg)
+ if err != nil {
+ return errors.Wrapf(err, "error marshalling new configuration JSON for container %s", pod.ID())
+ }
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return err
+ }
+ defer s.closeDBCon(db)
+
+ err = db.Update(func(tx *bolt.Tx) error {
+ podBkt, err := getPodBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ podDB := podBkt.Bucket([]byte(pod.ID()))
+ if podDB == nil {
+ pod.valid = false
+ return errors.Wrapf(ErrNoSuchPod, "no pod with ID %s found in DB", pod.ID())
+ }
+
+ if err := podDB.Put(configKey, newCfgJSON); err != nil {
+ return errors.Wrapf(err, "error updating pod %s config JSON", pod.ID())
+ }
+
+ return nil
+ })
+ return err
+}
+
// Pod retrieves a pod given its full ID
func (s *BoltState) Pod(id string) (*Pod, error) {
if id == "" {
@@ -1281,10 +1369,6 @@ func (s *BoltState) RemoveVolume(volume *Volume) error {
return ErrDBClosed
}
- if !volume.valid {
- return ErrVolumeRemoved
- }
-
volName := []byte(volume.Name())
db, err := s.getDBCon()
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index ea150cfac..3d749849d 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -348,13 +348,6 @@ func (s *BoltState) getVolumeFromDB(name []byte, volume *Volume, volBkt *bolt.Bu
return errors.Wrapf(err, "error unmarshalling volume %s config from DB", string(name))
}
- // Get the lock
- lock, err := s.runtime.lockManager.RetrieveLock(volume.config.LockID)
- if err != nil {
- return errors.Wrapf(err, "error retrieving lockfile for volume %s", string(name))
- }
- volume.lock = lock
-
volume.runtime = s.runtime
volume.valid = true
diff --git a/libpod/container_commit.go b/libpod/container_commit.go
index 026611e51..5c4fd1a31 100644
--- a/libpod/container_commit.go
+++ b/libpod/container_commit.go
@@ -162,7 +162,7 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
importBuilder.SetWorkDir(splitChange[1])
}
}
- candidates, _, err := util.ResolveName(destImage, "", sc, c.runtime.store)
+ candidates, _, _, err := util.ResolveName(destImage, "", sc, c.runtime.store)
if err != nil {
return nil, errors.Wrapf(err, "error resolving name %q", destImage)
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index b5f93c8a0..e3753d825 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -652,16 +652,19 @@ func (c *Container) startDependencies(ctx context.Context) error {
return errors.Wrapf(err, "error generating dependency graph for container %s", c.ID())
}
- ctrErrors := make(map[string]error)
- // reset ctrsVisisted for next round of recursion
- ctrsVisited := make(map[string]bool)
-
// If there are no containers without dependencies, we can't start
// Error out
if len(graph.noDepNodes) == 0 {
+ // we have no dependencies that need starting, go ahead and return
+ if len(graph.nodes) == 0 {
+ return nil
+ }
return errors.Wrapf(ErrNoSuchCtr, "All dependencies have dependencies of %s", c.ID())
}
+ ctrErrors := make(map[string]error)
+ ctrsVisited := make(map[string]bool)
+
// Traverse the graph beginning at nodes with no dependencies
for _, node := range graph.noDepNodes {
startNode(ctx, node, false, ctrErrors, ctrsVisited, true)
@@ -698,10 +701,18 @@ func (c *Container) getAllDependencies(visited map[string]*Container) error {
if err != nil {
return err
}
- visited[depID] = dep
- if err := dep.getAllDependencies(visited); err != nil {
+ status, err := dep.State()
+ if err != nil {
return err
}
+ // if the dependency is already running, we can assume its dependencies are also running
+ // so no need to add them to those we need to start
+ if status != ContainerStateRunning {
+ visited[depID] = dep
+ if err := dep.getAllDependencies(visited); err != nil {
+ return err
+ }
+ }
}
}
return nil
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 86f94477e..f182b6bdf 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -26,7 +26,6 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/secrets"
"github.com/containers/storage/pkg/idtools"
- "github.com/mrunalp/fileutils"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
@@ -677,20 +676,12 @@ func (c *Container) makeBindMounts() error {
// If it doesn't, don't copy them
resolvPath, exists := bindMounts["/etc/resolv.conf"]
if exists {
- resolvDest := filepath.Join(c.state.RunDir, "resolv.conf")
- if err := fileutils.CopyFile(resolvPath, resolvDest); err != nil {
- return errors.Wrapf(err, "error copying resolv.conf from dependency container %s of container %s", depCtr.ID(), c.ID())
- }
- c.state.BindMounts["/etc/resolv.conf"] = resolvDest
- }
+ c.state.BindMounts["/etc/resolv.conf"] = resolvPath
+ }
hostsPath, exists := bindMounts["/etc/hosts"]
if exists {
- hostsDest := filepath.Join(c.state.RunDir, "hosts")
- if err := fileutils.CopyFile(hostsPath, hostsDest); err != nil {
- return errors.Wrapf(err, "error copying hosts file from dependency container %s of container %s", depCtr.ID(), c.ID())
- }
- c.state.BindMounts["/etc/hosts"] = hostsDest
+ c.state.BindMounts["/etc/hosts"] = hostsPath
}
} else {
newResolv, err := c.generateResolvConf()
@@ -705,6 +696,14 @@ func (c *Container) makeBindMounts() error {
}
c.state.BindMounts["/etc/hosts"] = newHosts
}
+
+ if err := label.Relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil {
+ return err
+ }
+
+ if err := label.Relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil {
+ return err
+ }
}
// SHM is always added when we mount the container
@@ -758,8 +757,24 @@ func (c *Container) makeBindMounts() error {
// generateResolvConf generates a containers resolv.conf
func (c *Container) generateResolvConf() (string, error) {
+ resolvConf := "/etc/resolv.conf"
+ for _, ns := range c.config.Spec.Linux.Namespaces {
+ if ns.Type == spec.NetworkNamespace {
+ if ns.Path != "" && !strings.HasPrefix(ns.Path, "/proc/") {
+ definedPath := filepath.Join("/etc/netns", filepath.Base(ns.Path), "resolv.conf")
+ _, err := os.Stat(definedPath)
+ if err == nil {
+ resolvConf = definedPath
+ } else if !os.IsNotExist(err) {
+ return "", errors.Wrapf(err, "failed to stat %s", definedPath)
+ }
+ }
+ break
+ }
+ }
+
// Determine the endpoint for resolv.conf in case it is a symlink
- resolvPath, err := filepath.EvalSymlinks("/etc/resolv.conf")
+ resolvPath, err := filepath.EvalSymlinks(resolvConf)
if err != nil {
return "", err
}
@@ -809,7 +824,7 @@ func (c *Container) generateResolvConf() (string, error) {
}
// Relabel resolv.conf for the container
- if err := label.Relabel(destPath, c.config.MountLabel, false); err != nil {
+ if err := label.Relabel(destPath, c.config.MountLabel, true); err != nil {
return "", err
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 028a795ea..b20419d7b 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -5,14 +5,18 @@ import (
"encoding/json"
"fmt"
"io"
+ "os"
"strings"
"syscall"
"time"
types2 "github.com/containernetworking/cni/pkg/types"
cp "github.com/containers/image/copy"
+ "github.com/containers/image/directory"
+ dockerarchive "github.com/containers/image/docker/archive"
"github.com/containers/image/docker/reference"
"github.com/containers/image/manifest"
+ ociarchive "github.com/containers/image/oci/archive"
is "github.com/containers/image/storage"
"github.com/containers/image/tarball"
"github.com/containers/image/transports"
@@ -26,8 +30,9 @@ import (
"github.com/containers/storage"
"github.com/containers/storage/pkg/reexec"
digest "github.com/opencontainers/go-digest"
+ imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
- opentracing "github.com/opentracing/opentracing-go"
+ "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -1084,3 +1089,65 @@ func (i *Image) Comment(ctx context.Context, manifestType string) (string, error
}
return ociv1Img.History[0].Comment, nil
}
+
+// Save writes a container image to the filesystem
+func (i *Image) Save(ctx context.Context, source, format, output string, moreTags []string, quiet, compress bool) error {
+ var (
+ writer io.Writer
+ destRef types.ImageReference
+ manifestType string
+ err error
+ )
+
+ if quiet {
+ writer = os.Stderr
+ }
+ switch format {
+ case "oci-archive":
+ destImageName := imageNameForSaveDestination(i, source)
+ destRef, err = ociarchive.NewReference(output, destImageName) // destImageName may be ""
+ if err != nil {
+ return errors.Wrapf(err, "error getting OCI archive ImageReference for (%q, %q)", output, destImageName)
+ }
+ case "oci-dir":
+ destRef, err = directory.NewReference(output)
+ if err != nil {
+ return errors.Wrapf(err, "error getting directory ImageReference for %q", output)
+ }
+ manifestType = imgspecv1.MediaTypeImageManifest
+ case "docker-dir":
+ destRef, err = directory.NewReference(output)
+ if err != nil {
+ return errors.Wrapf(err, "error getting directory ImageReference for %q", output)
+ }
+ manifestType = manifest.DockerV2Schema2MediaType
+ case "docker-archive", "":
+ dst := output
+ destImageName := imageNameForSaveDestination(i, source)
+ if destImageName != "" {
+ dst = fmt.Sprintf("%s:%s", dst, destImageName)
+ }
+ destRef, err = dockerarchive.ParseReference(dst) // FIXME? Add dockerarchive.NewReference
+ if err != nil {
+ return errors.Wrapf(err, "error getting Docker archive ImageReference for %q", dst)
+ }
+ default:
+ return errors.Errorf("unknown format option %q", format)
+ }
+ // supports saving multiple tags to the same tar archive
+ var additionaltags []reference.NamedTagged
+ if len(moreTags) > 0 {
+ additionaltags, err = GetAdditionalTags(moreTags)
+ if err != nil {
+ return err
+ }
+ }
+ if err := i.PushImageToReference(ctx, destRef, manifestType, "", "", writer, compress, SigningOptions{}, &DockerRegistryOptions{}, additionaltags); err != nil {
+ if err2 := os.Remove(output); err2 != nil {
+ logrus.Errorf("error deleting %q: %v", output, err)
+ }
+ return errors.Wrapf(err, "unable to save %q", source)
+ }
+
+ return nil
+}
diff --git a/libpod/image/search.go b/libpod/image/search.go
new file mode 100644
index 000000000..212eff00b
--- /dev/null
+++ b/libpod/image/search.go
@@ -0,0 +1,277 @@
+package image
+
+import (
+ "context"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/containers/image/docker"
+ "github.com/containers/image/types"
+ "github.com/containers/libpod/libpod/common"
+ sysreg "github.com/containers/libpod/pkg/registries"
+ "github.com/fatih/camelcase"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sync/semaphore"
+)
+
+const (
+ descriptionTruncLength = 44
+ maxQueries = 25
+ maxParallelSearches = int64(6)
+)
+
+// SearchResult is holding image-search related data.
+type SearchResult struct {
+ // Index is the image index (e.g., "docker.io" or "quay.io")
+ Index string
+ // Name is the canoncical name of the image (e.g., "docker.io/library/alpine").
+ Name string
+ // Description of the image.
+ Description string
+ // Stars is the number of stars of the image.
+ Stars int
+ // Official indicates if it's an official image.
+ Official string
+ // Automated indicates if the image was created by an automated build.
+ Automated string
+}
+
+// SearchOptions are used to control the behaviour of SearchImages.
+type SearchOptions struct {
+ // Filter allows to filter the results.
+ Filter SearchFilter
+ // Limit limits the number of queries per index (default: 25). Must be
+ // greater than 0 to overwrite the default value.
+ Limit int
+ // NoTrunc avoids the output to be truncated.
+ NoTrunc bool
+ // Authfile is the path to the authentication file.
+ Authfile string
+ // InsecureSkipTLSVerify allows to skip TLS verification.
+ InsecureSkipTLSVerify types.OptionalBool
+}
+
+// SearchFilter allows filtering the results of SearchImages.
+type SearchFilter struct {
+ // Stars describes the minimal amount of starts of an image.
+ Stars int
+ // IsAutomated decides if only images from automated builds are displayed.
+ IsAutomated types.OptionalBool
+ // IsOfficial decides if only official images are displayed.
+ IsOfficial types.OptionalBool
+}
+
+func splitCamelCase(src string) string {
+ entries := camelcase.Split(src)
+ return strings.Join(entries, " ")
+}
+
+// HeaderMap returns the headers of a SearchResult.
+func (s *SearchResult) HeaderMap() map[string]string {
+ v := reflect.Indirect(reflect.ValueOf(s))
+ values := make(map[string]string, v.NumField())
+
+ for i := 0; i < v.NumField(); i++ {
+ key := v.Type().Field(i).Name
+ value := key
+ values[key] = strings.ToUpper(splitCamelCase(value))
+ }
+ return values
+}
+
+// SearchImages searches images based on term and the specified SearchOptions
+// in all registries.
+func SearchImages(term string, options SearchOptions) ([]SearchResult, error) {
+ // Check if search term has a registry in it
+ registry, err := sysreg.GetRegistry(term)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting registry from %q", term)
+ }
+ if registry != "" {
+ term = term[len(registry)+1:]
+ }
+
+ registries, err := getRegistries(registry)
+ if err != nil {
+ return nil, err
+ }
+
+ // searchOutputData is used as a return value for searching in parallel.
+ type searchOutputData struct {
+ data []SearchResult
+ err error
+ }
+
+ // Let's follow Firefox by limiting parallel downloads to 6.
+ sem := semaphore.NewWeighted(maxParallelSearches)
+ wg := sync.WaitGroup{}
+ wg.Add(len(registries))
+ data := make([]searchOutputData, len(registries))
+
+ searchImageInRegistryHelper := func(index int, registry string) {
+ defer sem.Release(1)
+ defer wg.Done()
+ searchOutput, err := searchImageInRegistry(term, registry, options)
+ data[index] = searchOutputData{data: searchOutput, err: err}
+ }
+
+ ctx := context.Background()
+ for i := range registries {
+ sem.Acquire(ctx, 1)
+ go searchImageInRegistryHelper(i, registries[i])
+ }
+
+ wg.Wait()
+ results := []SearchResult{}
+ for _, d := range data {
+ if d.err != nil {
+ return nil, d.err
+ }
+ results = append(results, d.data...)
+ }
+ return results, nil
+}
+
+// getRegistries returns the list of registries to search, depending on an optional registry specification
+func getRegistries(registry string) ([]string, error) {
+ var registries []string
+ if registry != "" {
+ registries = append(registries, registry)
+ } else {
+ var err error
+ registries, err = sysreg.GetRegistries()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting registries to search")
+ }
+ }
+ return registries, nil
+}
+
+func searchImageInRegistry(term string, registry string, options SearchOptions) ([]SearchResult, error) {
+ // Max number of queries by default is 25
+ limit := maxQueries
+ if options.Limit > 0 {
+ limit = options.Limit
+ }
+
+ sc := common.GetSystemContext("", options.Authfile, false)
+ sc.DockerInsecureSkipTLSVerify = options.InsecureSkipTLSVerify
+ // FIXME: Set this more globally. Probably no reason not to have it in
+ // every types.SystemContext, and to compute the value just once in one
+ // place.
+ sc.SystemRegistriesConfPath = sysreg.SystemRegistriesConfPath()
+ results, err := docker.SearchRegistry(context.TODO(), sc, registry, term, limit)
+ if err != nil {
+ logrus.Errorf("error searching registry %q: %v", registry, err)
+ return []SearchResult{}, nil
+ }
+ index := registry
+ arr := strings.Split(registry, ".")
+ if len(arr) > 2 {
+ index = strings.Join(arr[len(arr)-2:], ".")
+ }
+
+ // limit is the number of results to output
+ // if the total number of results is less than the limit, output all
+ // if the limit has been set by the user, output those number of queries
+ limit = maxQueries
+ if len(results) < limit {
+ limit = len(results)
+ }
+ if options.Limit != 0 && options.Limit < len(results) {
+ limit = options.Limit
+ }
+
+ paramsArr := []SearchResult{}
+ for i := 0; i < limit; i++ {
+ // Check whether query matches filters
+ if !(options.Filter.matchesAutomatedFilter(results[i]) && options.Filter.matchesOfficialFilter(results[i]) && options.Filter.matchesStarFilter(results[i])) {
+ continue
+ }
+ official := ""
+ if results[i].IsOfficial {
+ official = "[OK]"
+ }
+ automated := ""
+ if results[i].IsAutomated {
+ automated = "[OK]"
+ }
+ description := strings.Replace(results[i].Description, "\n", " ", -1)
+ if len(description) > 44 && !options.NoTrunc {
+ description = description[:descriptionTruncLength] + "..."
+ }
+ name := registry + "/" + results[i].Name
+ if index == "docker.io" && !strings.Contains(results[i].Name, "/") {
+ name = index + "/library/" + results[i].Name
+ }
+ params := SearchResult{
+ Index: index,
+ Name: name,
+ Description: description,
+ Official: official,
+ Automated: automated,
+ Stars: results[i].StarCount,
+ }
+ paramsArr = append(paramsArr, params)
+ }
+ return paramsArr, nil
+}
+
+// ParseSearchFilter turns the filter into a SearchFilter that can be used for
+// searching images.
+func ParseSearchFilter(filter []string) (*SearchFilter, error) {
+ sFilter := new(SearchFilter)
+ for _, f := range filter {
+ arr := strings.Split(f, "=")
+ switch arr[0] {
+ case "stars":
+ if len(arr) < 2 {
+ return nil, errors.Errorf("invalid `stars` filter %q, should be stars=<value>", filter)
+ }
+ stars, err := strconv.Atoi(arr[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "incorrect value type for stars filter")
+ }
+ sFilter.Stars = stars
+ break
+ case "is-automated":
+ if len(arr) == 2 && arr[1] == "false" {
+ sFilter.IsAutomated = types.OptionalBoolFalse
+ } else {
+ sFilter.IsAutomated = types.OptionalBoolTrue
+ }
+ break
+ case "is-official":
+ if len(arr) == 2 && arr[1] == "false" {
+ sFilter.IsOfficial = types.OptionalBoolFalse
+ } else {
+ sFilter.IsOfficial = types.OptionalBoolTrue
+ }
+ break
+ default:
+ return nil, errors.Errorf("invalid filter type %q", f)
+ }
+ }
+ return sFilter, nil
+}
+
+func (f *SearchFilter) matchesStarFilter(result docker.SearchResult) bool {
+ return result.StarCount >= f.Stars
+}
+
+func (f *SearchFilter) matchesAutomatedFilter(result docker.SearchResult) bool {
+ if f.IsAutomated != types.OptionalBoolUndefined {
+ return result.IsAutomated == (f.IsAutomated == types.OptionalBoolTrue)
+ }
+ return true
+}
+
+func (f *SearchFilter) matchesOfficialFilter(result docker.SearchResult) bool {
+ if f.IsOfficial != types.OptionalBoolUndefined {
+ return result.IsOfficial == (f.IsOfficial == types.OptionalBoolTrue)
+ }
+ return true
+}
diff --git a/libpod/image/utils.go b/libpod/image/utils.go
index 3585428ad..544796a4b 100644
--- a/libpod/image/utils.go
+++ b/libpod/image/utils.go
@@ -1,6 +1,7 @@
package image
import (
+ "fmt"
"io"
"net/url"
"regexp"
@@ -148,3 +149,28 @@ func IsValidImageURI(imguri string) (bool, error) {
}
return true, nil
}
+
+// imageNameForSaveDestination returns a Docker-like reference appropriate for saving img,
+// which the user referred to as imgUserInput; or an empty string, if there is no appropriate
+// reference.
+func imageNameForSaveDestination(img *Image, imgUserInput string) string {
+ if strings.Contains(img.ID(), imgUserInput) {
+ return ""
+ }
+
+ prepend := ""
+ localRegistryPrefix := fmt.Sprintf("%s/", DefaultLocalRegistry)
+ if !strings.HasPrefix(imgUserInput, localRegistryPrefix) {
+ // we need to check if localhost was added to the image name in NewFromLocal
+ for _, name := range img.Names() {
+ // If the user is saving an image in the localhost registry, getLocalImage need
+ // a name that matches the format localhost/<tag1>:<tag2> or localhost/<tag>:latest to correctly
+ // set up the manifest and save.
+ if strings.HasPrefix(name, localRegistryPrefix) && (strings.HasSuffix(name, imgUserInput) || strings.HasSuffix(name, fmt.Sprintf("%s:latest", imgUserInput))) {
+ prepend = localRegistryPrefix
+ break
+ }
+ }
+ }
+ return fmt.Sprintf("%s%s", prepend, imgUserInput)
+}
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 314799309..ab4fc8ba7 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -378,6 +378,58 @@ func (s *InMemoryState) ContainerInUse(ctr *Container) ([]string, error) {
return arr, nil
}
+// AllContainers retrieves all containers from the state
+func (s *InMemoryState) AllContainers() ([]*Container, error) {
+ ctrs := make([]*Container, 0, len(s.containers))
+ for _, ctr := range s.containers {
+ if s.namespace == "" || ctr.config.Namespace == s.namespace {
+ ctrs = append(ctrs, ctr)
+ }
+ }
+
+ return ctrs, nil
+}
+
+// RewriteContainerConfig rewrites a container's configuration.
+// This function is DANGEROUS, even with an in-memory state.
+// Please read the full comment on it in state.go before using it.
+func (s *InMemoryState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error {
+ if !ctr.valid {
+ return ErrCtrRemoved
+ }
+
+ // If the container does not exist, return error
+ stateCtr, ok := s.containers[ctr.ID()]
+ if !ok {
+ ctr.valid = false
+ return errors.Wrapf(ErrNoSuchCtr, "container with ID %s not found in state", ctr.ID())
+ }
+
+ stateCtr.config = newCfg
+
+ return nil
+}
+
+// RewritePodConfig rewrites a pod's configuration.
+// This function is DANGEROUS, even with in-memory state.
+// Please read the full comment on it in state.go before using it.
+func (s *InMemoryState) RewritePodConfig(pod *Pod, newCfg *PodConfig) error {
+ if !pod.valid {
+ return ErrPodRemoved
+ }
+
+ // If the pod does not exist, return error
+ statePod, ok := s.pods[pod.ID()]
+ if !ok {
+ pod.valid = false
+ return errors.Wrapf(ErrNoSuchPod, "pod with ID %s not found in state", pod.ID())
+ }
+
+ statePod.config = newCfg
+
+ return nil
+}
+
// Volume retrieves a volume from its full name
func (s *InMemoryState) Volume(name string) (*Volume, error) {
if name == "" {
@@ -486,18 +538,6 @@ func (s *InMemoryState) AllVolumes() ([]*Volume, error) {
return allVols, nil
}
-// AllContainers retrieves all containers from the state
-func (s *InMemoryState) AllContainers() ([]*Container, error) {
- ctrs := make([]*Container, 0, len(s.containers))
- for _, ctr := range s.containers {
- if s.namespace == "" || ctr.config.Namespace == s.namespace {
- ctrs = append(ctrs, ctr)
- }
- }
-
- return ctrs, nil
-}
-
// Pod retrieves a pod from the state from its full ID
func (s *InMemoryState) Pod(id string) (*Pod, error) {
if id == "" {
diff --git a/libpod/lock/in_memory_locks.go b/libpod/lock/in_memory_locks.go
index db8f20e95..7c9605917 100644
--- a/libpod/lock/in_memory_locks.go
+++ b/libpod/lock/in_memory_locks.go
@@ -89,3 +89,14 @@ func (m *InMemoryManager) RetrieveLock(id uint32) (Locker, error) {
return m.locks[id], nil
}
+
+// FreeAllLocks frees all locks.
+// This function is DANGEROUS. Please read the full comment in locks.go before
+// trying to use it.
+func (m *InMemoryManager) FreeAllLocks() error {
+ for _, lock := range m.locks {
+ lock.allocated = false
+ }
+
+ return nil
+}
diff --git a/libpod/lock/lock.go b/libpod/lock/lock.go
index 1f94171fe..d6841646b 100644
--- a/libpod/lock/lock.go
+++ b/libpod/lock/lock.go
@@ -24,6 +24,20 @@ type Manager interface {
// The underlying lock MUST be the same as another other lock with the
// same UUID.
RetrieveLock(id uint32) (Locker, error)
+ // PLEASE READ FULL DESCRIPTION BEFORE USING.
+ // FreeAllLocks frees all allocated locks, in preparation for lock
+ // reallocation.
+ // As this deallocates all presently-held locks, this can be very
+ // dangerous - if there are other processes running that might be
+ // attempting to allocate new locks and free existing locks, we may
+ // encounter races leading to an inconsistent state.
+ // (This is in addition to the fact that FreeAllLocks instantly makes
+ // the state inconsistent simply by using it, and requires a full
+ // lock renumbering to restore consistency!).
+ // In short, this should only be used as part of unit tests, or lock
+ // renumbering, where reasonable guarantees about other processes can be
+ // made.
+ FreeAllLocks() error
}
// Locker is similar to sync.Locker, but provides a method for freeing the lock
diff --git a/libpod/lock/shm/shm_lock.c b/libpod/lock/shm/shm_lock.c
index 4af58d857..d11fce71a 100644
--- a/libpod/lock/shm/shm_lock.c
+++ b/libpod/lock/shm/shm_lock.c
@@ -203,6 +203,8 @@ shm_struct_t *setup_lock_shm(char *path, uint32_t num_locks, int *error_code) {
// terminating NULL byte.
// Returns a valid pointer on success or NULL on error.
// If an error occurs, negative ERRNO values will be written to error_code.
+// ERANGE is returned for a mismatch between num_locks and the number of locks
+// available in the the SHM lock struct.
shm_struct_t *open_lock_shm(char *path, uint32_t num_locks, int *error_code) {
int shm_fd;
shm_struct_t *shm;
@@ -255,11 +257,11 @@ shm_struct_t *open_lock_shm(char *path, uint32_t num_locks, int *error_code) {
// Need to check the SHM to see if it's actually our locks
if (shm->magic != MAGIC) {
- *error_code = -1 * errno;
+ *error_code = -1 * EBADF;
goto CLEANUP;
}
if (shm->num_locks != (num_bitmaps * BITMAP_SIZE)) {
- *error_code = -1 * errno;
+ *error_code = -1 * ERANGE;
goto CLEANUP;
}
@@ -407,6 +409,36 @@ int32_t deallocate_semaphore(shm_struct_t *shm, uint32_t sem_index) {
return 0;
}
+// Deallocate all semaphores unconditionally.
+// Returns negative ERRNO values.
+int32_t deallocate_all_semaphores(shm_struct_t *shm) {
+ int ret_code;
+ uint i;
+
+ if (shm == NULL) {
+ return -1 * EINVAL;
+ }
+
+ // Lock the mutex controlling access to our shared memory
+ ret_code = take_mutex(&(shm->segment_lock));
+ if (ret_code != 0) {
+ return -1 * ret_code;
+ }
+
+ // Iterate through all bitmaps and reset to unused
+ for (i = 0; i < shm->num_bitmaps; i++) {
+ shm->locks[i].bitmap = 0;
+ }
+
+ // Unlock the allocation control mutex
+ ret_code = release_mutex(&(shm->segment_lock));
+ if (ret_code != 0) {
+ return -1 * ret_code;
+ }
+
+ return 0;
+}
+
// Lock a given semaphore
// Does not check if the semaphore is allocated - this ensures that, even for
// removed containers, we can still successfully lock to check status (and
diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go
index 87d28e5c1..e70ea8743 100644
--- a/libpod/lock/shm/shm_lock.go
+++ b/libpod/lock/shm/shm_lock.go
@@ -155,6 +155,22 @@ func (locks *SHMLocks) DeallocateSemaphore(sem uint32) error {
return nil
}
+// DeallocateAllSemaphores frees all semaphores so they can be reallocated to
+// other containers and pods.
+func (locks *SHMLocks) DeallocateAllSemaphores() error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+
+ retCode := C.deallocate_all_semaphores(locks.lockStruct)
+ if retCode < 0 {
+ // Negative errno return from C
+ return syscall.Errno(-1 * retCode)
+ }
+
+ return nil
+}
+
// LockSemaphore locks the given semaphore.
// If the semaphore is already locked, LockSemaphore will block until the lock
// can be acquired.
diff --git a/libpod/lock/shm/shm_lock.h b/libpod/lock/shm/shm_lock.h
index 8e7e23fb7..58e4297e2 100644
--- a/libpod/lock/shm/shm_lock.h
+++ b/libpod/lock/shm/shm_lock.h
@@ -40,6 +40,7 @@ shm_struct_t *open_lock_shm(char *path, uint32_t num_locks, int *error_code);
int32_t close_lock_shm(shm_struct_t *shm);
int64_t allocate_semaphore(shm_struct_t *shm);
int32_t deallocate_semaphore(shm_struct_t *shm, uint32_t sem_index);
+int32_t deallocate_all_semaphores(shm_struct_t *shm);
int32_t lock_semaphore(shm_struct_t *shm, uint32_t sem_index);
int32_t unlock_semaphore(shm_struct_t *shm, uint32_t sem_index);
diff --git a/libpod/lock/shm/shm_lock_test.go b/libpod/lock/shm/shm_lock_test.go
index 594eb5d8e..830035881 100644
--- a/libpod/lock/shm/shm_lock_test.go
+++ b/libpod/lock/shm/shm_lock_test.go
@@ -4,7 +4,6 @@ import (
"fmt"
"os"
"runtime"
- "syscall"
"testing"
"time"
@@ -53,12 +52,8 @@ func runLockTest(t *testing.T, testFunc func(*testing.T, *SHMLocks)) {
}
defer func() {
// Deallocate all locks
- // Ignore ENOENT (lock is not allocated)
- var i uint32
- for i = 0; i < numLocks; i++ {
- if err := locks.DeallocateSemaphore(i); err != nil && err != syscall.ENOENT {
- t.Fatalf("Error deallocating semaphore %d: %v", i, err)
- }
+ if err := locks.DeallocateAllSemaphores(); err != nil {
+ t.Fatalf("Error deallocating semaphores: %v", err)
}
if err := locks.Close(); err != nil {
@@ -212,6 +207,25 @@ func TestAllocateDeallocateCycle(t *testing.T) {
})
}
+// Test that DeallocateAllSemaphores deallocates all semaphores
+func TestDeallocateAllSemaphoresDeallocatesAll(t *testing.T) {
+ runLockTest(t, func(t *testing.T, locks *SHMLocks) {
+ // Allocate a lock
+ locks1, err := locks.AllocateSemaphore()
+ assert.NoError(t, err)
+
+ // Free all locks
+ err = locks.DeallocateAllSemaphores()
+ assert.NoError(t, err)
+
+ // Allocate another lock
+ locks2, err := locks.AllocateSemaphore()
+ assert.NoError(t, err)
+
+ assert.Equal(t, locks1, locks2)
+ })
+}
+
// Test that locks actually lock
func TestLockSemaphoreActuallyLocks(t *testing.T) {
runLockTest(t, func(t *testing.T, locks *SHMLocks) {
diff --git a/libpod/lock/shm_lock_manager_linux.go b/libpod/lock/shm_lock_manager_linux.go
index 94dfd7dd7..8678958ee 100644
--- a/libpod/lock/shm_lock_manager_linux.go
+++ b/libpod/lock/shm_lock_manager_linux.go
@@ -71,6 +71,13 @@ func (m *SHMLockManager) RetrieveLock(id uint32) (Locker, error) {
return lock, nil
}
+// FreeAllLocks frees all locks in the manager.
+// This function is DANGEROUS. Please read the full comment in locks.go before
+// trying to use it.
+func (m *SHMLockManager) FreeAllLocks() error {
+ return m.locks.DeallocateAllSemaphores()
+}
+
// SHMLock is an individual shared memory lock.
type SHMLock struct {
lockID uint32
diff --git a/libpod/lock/shm_lock_manager_unsupported.go b/libpod/lock/shm_lock_manager_unsupported.go
index cbdb2f7bc..1d6e3fcbd 100644
--- a/libpod/lock/shm_lock_manager_unsupported.go
+++ b/libpod/lock/shm_lock_manager_unsupported.go
@@ -27,3 +27,8 @@ func (m *SHMLockManager) AllocateLock() (Locker, error) {
func (m *SHMLockManager) RetrieveLock(id string) (Locker, error) {
return nil, fmt.Errorf("not supported")
}
+
+// FreeAllLocks is not supported on this platform
+func (m *SHMLockManager) FreeAllLocks() error {
+ return fmt.Errorf("not supported")
+}
diff --git a/libpod/options.go b/libpod/options.go
index 06737776b..e22c81f91 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -149,6 +149,7 @@ func WithOCIRuntime(runtime string) RuntimeOption {
}
rt.config.OCIRuntime = runtime
+ rt.config.RuntimePath = nil
return nil
}
@@ -394,6 +395,22 @@ func WithDefaultInfraCommand(cmd string) RuntimeOption {
}
}
+// WithRenumber instructs libpod to perform a lock renumbering while
+// initializing. This will handle migrations from early versions of libpod with
+// file locks to newer versions with SHM locking, as well as changes in the
+// number of configured locks.
+func WithRenumber() RuntimeOption {
+ return func(rt *Runtime) error {
+ if rt.valid {
+ return ErrRuntimeFinalized
+ }
+
+ rt.doRenumber = true
+
+ return nil
+ }
+}
+
// Container Creation Options
// WithShmDir sets the directory that should be mounted on /dev/shm.
@@ -887,10 +904,10 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmo
}
ctr.config.PostConfigureNetNS = postConfigureNetNS
- ctr.config.CreateNetNS = true
+ ctr.config.NetMode = namespaces.NetworkMode(netmode)
+ ctr.config.CreateNetNS = !ctr.config.NetMode.IsUserDefined()
ctr.config.PortMappings = portMappings
ctr.config.Networks = networks
- ctr.config.NetMode = namespaces.NetworkMode(netmode)
return nil
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 4f5d1e292..94dbf37dd 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -7,6 +7,7 @@ import (
"os/exec"
"path/filepath"
"sync"
+ "syscall"
"github.com/BurntSushi/toml"
is "github.com/containers/image/storage"
@@ -79,7 +80,8 @@ type RuntimeOption func(*Runtime) error
// Runtime is the core libpod runtime
type Runtime struct {
- config *RuntimeConfig
+ config *RuntimeConfig
+
state State
store storage.Store
storageService *storageService
@@ -88,12 +90,23 @@ type Runtime struct {
netPlugin ocicni.CNIPlugin
ociRuntimePath OCIRuntimePath
conmonPath string
- valid bool
- lock sync.RWMutex
imageRuntime *image.Runtime
firewallBackend firewall.FirewallBackend
lockManager lock.Manager
configuredFrom *runtimeConfiguredFrom
+
+ // doRenumber indicates that the runtime should perform a lock renumber
+ // during initialization.
+ // Once the runtime has been initialized and returned, this variable is
+ // unused.
+ doRenumber bool
+
+ // valid indicates whether the runtime is ready to use.
+ // valid is set to true when a runtime is returned from GetRuntime(),
+ // and remains true until the runtime is shut down (rendering its
+ // storage unusable). When valid is false, the runtime cannot be used.
+ valid bool
+ lock sync.RWMutex
}
// OCIRuntimePath contains information about an OCI runtime.
@@ -753,6 +766,7 @@ func makeRuntime(runtime *Runtime) (err error) {
aliveLock.Unlock()
}
}()
+
_, err = os.Stat(runtimeAliveFile)
if err != nil {
// If the file doesn't exist, we need to refresh the state
@@ -778,12 +792,35 @@ func makeRuntime(runtime *Runtime) (err error) {
if err != nil {
return err
}
+ } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber {
+ logrus.Debugf("Number of locks does not match - removing old locks")
+
+ // ERANGE indicates a lock numbering mismatch.
+ // Since we're renumbering, this is not fatal.
+ // Remove the earlier set of locks and recreate.
+ if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil {
+ return errors.Wrapf(err, "error removing libpod locks file %s", lockPath)
+ }
+
+ manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks)
+ if err != nil {
+ return err
+ }
} else {
return err
}
}
runtime.lockManager = manager
+ // If we're renumbering locks, do it now.
+ // It breaks out of normal runtime init, and will not return a valid
+ // runtime.
+ if runtime.doRenumber {
+ if err := runtime.renumberLocks(); err != nil {
+ return err
+ }
+ }
+
// If we need to refresh the state, do it now - things are guaranteed to
// be set up by now.
if doRefresh {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 1055da75b..2ec8d0795 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -421,7 +421,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
for _, v := range volumes {
if volume, err := runtime.state.Volume(v); err == nil {
- if err := runtime.removeVolume(ctx, volume, false, true); err != nil && err != ErrNoSuchVolume && err != ErrVolumeBeingUsed {
+ if err := runtime.removeVolume(ctx, volume, false); err != nil && err != ErrNoSuchVolume && err != ErrVolumeBeingUsed {
logrus.Errorf("cleanup volume (%s): %v", v, err)
}
}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 1e9689362..451c2ebe7 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -3,7 +3,6 @@ package libpod
import (
"context"
"fmt"
- "github.com/opencontainers/image-spec/specs-go/v1"
"io"
"io/ioutil"
"net/http"
@@ -15,6 +14,11 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
+
+ "github.com/containers/image/directory"
+ dockerarchive "github.com/containers/image/docker/archive"
+ ociarchive "github.com/containers/image/oci/archive"
+ "github.com/opencontainers/image-spec/specs-go/v1"
)
// Runtime API
@@ -211,3 +215,41 @@ func downloadFromURL(source string) (string, error) {
return outFile.Name(), nil
}
+
+// LoadImage loads a container image into local storage
+func (r *Runtime) LoadImage(ctx context.Context, name, inputFile string, writer io.Writer, signaturePolicy string) (string, error) {
+ var newImages []*image.Image
+ src, err := dockerarchive.ParseReference(inputFile) // FIXME? We should add dockerarchive.NewReference()
+ if err == nil {
+ newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer)
+ }
+ if err != nil {
+ // generate full src name with specified image:tag
+ src, err := ociarchive.NewReference(inputFile, name) // imageName may be ""
+ if err == nil {
+ newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer)
+ }
+ if err != nil {
+ src, err := directory.NewReference(inputFile)
+ if err == nil {
+ newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer)
+ }
+ if err != nil {
+ return "", errors.Wrapf(err, "error pulling %q", name)
+ }
+ }
+ }
+ return getImageNames(newImages), nil
+}
+
+func getImageNames(images []*image.Image) string {
+ var names string
+ for i := range images {
+ if i == 0 {
+ names = images[i].InputName
+ } else {
+ names += ", " + images[i].InputName
+ }
+ }
+ return names
+}
diff --git a/libpod/runtime_renumber.go b/libpod/runtime_renumber.go
new file mode 100644
index 000000000..125cf0825
--- /dev/null
+++ b/libpod/runtime_renumber.go
@@ -0,0 +1,57 @@
+package libpod
+
+import (
+ "github.com/pkg/errors"
+)
+
+// renumberLocks reassigns lock numbers for all containers and pods in the
+// state.
+// TODO: It would be desirable to make it impossible to call this until all
+// other libpod sessions are dead.
+// Possibly use a read-write file lock, with all non-renumber podmans owning the
+// lock as read, renumber attempting to take a write lock?
+// The alternative is some sort of session tracking, and I don't know how
+// reliable that can be.
+func (r *Runtime) renumberLocks() error {
+ // Start off by deallocating all locks
+ if err := r.lockManager.FreeAllLocks(); err != nil {
+ return err
+ }
+
+ allCtrs, err := r.state.AllContainers()
+ if err != nil {
+ return err
+ }
+ for _, ctr := range allCtrs {
+ lock, err := r.lockManager.AllocateLock()
+ if err != nil {
+ return errors.Wrapf(err, "error allocating lock for container %s", ctr.ID())
+ }
+
+ ctr.config.LockID = lock.ID()
+
+ // Write the new lock ID
+ if err := r.state.RewriteContainerConfig(ctr, ctr.config); err != nil {
+ return err
+ }
+ }
+ allPods, err := r.state.AllPods()
+ if err != nil {
+ return err
+ }
+ for _, pod := range allPods {
+ lock, err := r.lockManager.AllocateLock()
+ if err != nil {
+ return errors.Wrapf(err, "error allocating lock for pod %s", pod.ID())
+ }
+
+ pod.config.LockID = lock.ID()
+
+ // Write the new lock ID
+ if err := r.state.RewritePodConfig(pod, pod.config); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go
index beae50ac9..11f37ad4b 100644
--- a/libpod/runtime_volume.go
+++ b/libpod/runtime_volume.go
@@ -19,7 +19,7 @@ type VolumeCreateOption func(*Volume) error
type VolumeFilter func(*Volume) bool
// RemoveVolume removes a volumes
-func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force, prune bool) error {
+func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error {
r.lock.Lock()
defer r.lock.Unlock()
@@ -35,10 +35,7 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force, prune bool
}
}
- v.lock.Lock()
- defer v.lock.Unlock()
-
- return r.removeVolume(ctx, v, force, prune)
+ return r.removeVolume(ctx, v, force)
}
// RemoveVolumes removes a slice of volumes or all with a force bool
@@ -64,7 +61,7 @@ func (r *Runtime) RemoveVolumes(ctx context.Context, volumes []string, all, forc
}
for _, vol := range vols {
- if err := r.RemoveVolume(ctx, vol, force, false); err != nil {
+ if err := r.RemoveVolume(ctx, vol, force); err != nil {
return deletedVols, err
}
logrus.Debugf("removed volume %s", vol.Name())
@@ -168,8 +165,8 @@ func (r *Runtime) PruneVolumes(ctx context.Context) ([]string, []error) {
}
for _, vol := range vols {
- if err := r.RemoveVolume(ctx, vol, false, true); err != nil {
- if err != ErrVolumeBeingUsed {
+ if err := r.RemoveVolume(ctx, vol, false); err != nil {
+ if errors.Cause(err) != ErrVolumeBeingUsed && errors.Cause(err) != ErrVolumeRemoved {
pruneErrors = append(pruneErrors, err)
}
continue
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index 0727cfedf..838c0167a 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -67,13 +67,6 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
}
volume.config.MountPoint = fullVolPath
- lock, err := r.lockManager.AllocateLock()
- if err != nil {
- return nil, errors.Wrapf(err, "error allocating lock for new volume")
- }
- volume.lock = lock
- volume.config.LockID = volume.lock.ID()
-
volume.valid = true
// Add the volume to state
@@ -85,9 +78,12 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
}
// removeVolume removes the specified volume from state as well tears down its mountpoint and storage
-func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force, prune bool) error {
+func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error {
if !v.valid {
- return ErrNoSuchVolume
+ if ok, _ := r.state.HasVolume(v.Name()); !ok {
+ return nil
+ }
+ return ErrVolumeRemoved
}
deps, err := r.state.VolumeInUse(v)
@@ -95,9 +91,6 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force, prune bool
return err
}
if len(deps) != 0 {
- if prune {
- return ErrVolumeBeingUsed
- }
depsStr := strings.Join(deps, ", ")
if !force {
return errors.Wrapf(ErrVolumeBeingUsed, "volume %s is being used by the following container(s): %s", v.Name(), depsStr)
@@ -112,18 +105,20 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force, prune bool
}
}
- // Delete the mountpoint path of the volume, that is delete the volume from /var/lib/containers/storage/volumes
- if err := v.teardownStorage(); err != nil {
- return errors.Wrapf(err, "error cleaning up volume storage for %q", v.Name())
- }
+ // Set volume as invalid so it can no longer be used
+ v.valid = false
// Remove the volume from the state
if err := r.state.RemoveVolume(v); err != nil {
return errors.Wrapf(err, "error removing volume %s", v.Name())
}
- // Set volume as invalid so it can no longer be used
- v.valid = false
+ // Delete the mountpoint path of the volume, that is delete the volume from /var/lib/containers/storage/volumes
+ if err := v.teardownStorage(); err != nil {
+ return errors.Wrapf(err, "error cleaning up volume storage for %q", v.Name())
+ }
+
+ logrus.Debugf("Removed volume %s", v.Name())
return nil
}
diff --git a/libpod/state.go b/libpod/state.go
index 88d89f673..98282fc83 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -97,6 +97,30 @@ type State interface {
// returned.
AllContainers() ([]*Container, error)
+ // PLEASE READ FULL DESCRIPTION BEFORE USING.
+ // Rewrite a container's configuration.
+ // This function breaks libpod's normal prohibition on a read-only
+ // configuration, and as such should be used EXTREMELY SPARINGLY and
+ // only in very specific circumstances.
+ // Specifically, it is ONLY safe to use thing function to make changes
+ // that result in a functionally identical configuration (migrating to
+ // newer, but identical, configuration fields), or during libpod init
+ // WHILE HOLDING THE ALIVE LOCK (to prevent other libpod instances from
+ // being initialized).
+ // Most things in config can be changed by this, but container ID and
+ // name ABSOLUTELY CANNOT BE ALTERED. If you do so, there is a high
+ // potential for database corruption.
+ // There are a lot of capital letters and conditions here, but the short
+ // answer is this: use this only very sparingly, and only if you really
+ // know what you're doing.
+ RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error
+ // PLEASE READ THE ABOVE DESCRIPTION BEFORE USING.
+ // This function is identical to RewriteContainerConfig, save for the
+ // fact that it is used with pods instead.
+ // It is subject to the same conditions as RewriteContainerConfig.
+ // Please do not use this unless you know what you're doing.
+ RewritePodConfig(pod *Pod, newCfg *PodConfig) error
+
// Accepts full ID of pod.
// If the pod given is not in the set namespace, an error will be
// returned.
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 4bd00ab55..be68a2d69 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -1298,6 +1298,78 @@ func TestCannotUseBadIDAsGenericDependency(t *testing.T) {
})
}
+func TestRewriteContainerConfigDoesNotExist(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ err := state.RewriteContainerConfig(&Container{}, &ContainerConfig{})
+ assert.Error(t, err)
+ })
+}
+
+func TestRewriteContainerConfigNotInState(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testCtr, err := getTestCtr1(manager)
+ assert.NoError(t, err)
+ err = state.RewriteContainerConfig(testCtr, &ContainerConfig{})
+ assert.Error(t, err)
+ })
+}
+
+func TestRewriteContainerConfigRewritesConfig(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testCtr, err := getTestCtr1(manager)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ testCtr.config.LogPath = "/another/path/"
+
+ err = state.RewriteContainerConfig(testCtr, testCtr.config)
+ assert.NoError(t, err)
+
+ testCtrFromState, err := state.Container(testCtr.ID())
+ assert.NoError(t, err)
+
+ testContainersEqual(t, testCtrFromState, testCtr, true)
+ })
+}
+
+func TestRewritePodConfigDoesNotExist(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ err := state.RewritePodConfig(&Pod{}, &PodConfig{})
+ assert.Error(t, err)
+ })
+}
+
+func TestRewritePodConfigNotInState(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testPod, err := getTestPod1(manager)
+ assert.NoError(t, err)
+ err = state.RewritePodConfig(testPod, &PodConfig{})
+ assert.Error(t, err)
+ })
+}
+
+func TestRewritePodConfigRewritesConfig(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testPod, err := getTestPod1(manager)
+ assert.NoError(t, err)
+
+ err = state.AddPod(testPod)
+ assert.NoError(t, err)
+
+ testPod.config.CgroupParent = "/another_cgroup_parent"
+
+ err = state.RewritePodConfig(testPod, testPod.config)
+ assert.NoError(t, err)
+
+ testPodFromState, err := state.Pod(testPod.ID())
+ assert.NoError(t, err)
+
+ testPodsEqual(t, testPodFromState, testPod, true)
+ })
+}
+
func TestGetPodDoesNotExist(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
_, err := state.Pod("doesnotexist")
diff --git a/libpod/volume.go b/libpod/volume.go
index 026a3bf49..74878b6a4 100644
--- a/libpod/volume.go
+++ b/libpod/volume.go
@@ -1,7 +1,5 @@
package libpod
-import "github.com/containers/libpod/libpod/lock"
-
// Volume is the type used to create named volumes
// TODO: all volumes should be created using this and the Volume API
type Volume struct {
@@ -9,7 +7,6 @@ type Volume struct {
valid bool
runtime *Runtime
- lock lock.Locker
}
// VolumeConfig holds the volume's config information
@@ -17,8 +14,6 @@ type Volume struct {
type VolumeConfig struct {
// Name of the volume
Name string `json:"name"`
- // ID of this volume's lock
- LockID uint32 `json:"lockID"`
Labels map[string]string `json:"labels"`
MountPoint string `json:"mountPoint"`
diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go
index 0de8a2350..35f0ca19d 100644
--- a/libpod/volume_internal.go
+++ b/libpod/volume_internal.go
@@ -18,8 +18,5 @@ func newVolume(runtime *Runtime) (*Volume, error) {
// teardownStorage deletes the volume from volumePath
func (v *Volume) teardownStorage() error {
- if !v.valid {
- return ErrNoSuchVolume
- }
return os.RemoveAll(filepath.Join(v.runtime.config.VolumePath, v.Name()))
}
diff --git a/libpod/adapter/client.go b/pkg/adapter/client.go
index 6512a5952..6512a5952 100644
--- a/libpod/adapter/client.go
+++ b/pkg/adapter/client.go
diff --git a/libpod/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 9623304e5..3f43a6905 100644
--- a/libpod/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -4,6 +4,7 @@ package adapter
import (
"encoding/json"
+ "github.com/containers/libpod/cmd/podman/shared"
iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
@@ -48,3 +49,33 @@ func (c *Container) Config() *libpod.ContainerConfig {
}
return c.Runtime.Config(c.ID())
}
+
+// Name returns the name of the container
+func (c *Container) Name() string {
+ return c.config.Name
+}
+
+// BatchContainerOp is wrapper func to mimic shared's function with a similar name meant for libpod
+func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) {
+ // TODO If pod ps ever shows container's sizes, re-enable this code; otherwise it isn't needed
+ // and would be a perf hit
+ //data, err := ctr.Inspect(true)
+ //if err != nil {
+ // return shared.BatchContainerStruct{}, err
+ //}
+ //
+ //size := new(shared.ContainerSize)
+ //size.RootFsSize = data.SizeRootFs
+ //size.RwSize = data.SizeRw
+
+ bcs := shared.BatchContainerStruct{
+ ConConfig: ctr.config,
+ ConState: ctr.state.State,
+ ExitCode: ctr.state.ExitCode,
+ Pid: ctr.state.PID,
+ StartedTime: ctr.state.StartedTime,
+ ExitedTime: ctr.state.FinishedTime,
+ //Size: size,
+ }
+ return bcs, nil
+}
diff --git a/libpod/adapter/images_remote.go b/pkg/adapter/images_remote.go
index e7b38dccc..e7b38dccc 100644
--- a/libpod/adapter/images_remote.go
+++ b/pkg/adapter/images_remote.go
diff --git a/libpod/adapter/info_remote.go b/pkg/adapter/info_remote.go
index 3b691ed17..3b691ed17 100644
--- a/libpod/adapter/info_remote.go
+++ b/pkg/adapter/info_remote.go
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
new file mode 100644
index 000000000..706a8fe96
--- /dev/null
+++ b/pkg/adapter/pods.go
@@ -0,0 +1,323 @@
+// +build !remoteclient
+
+package adapter
+
+import (
+ "context"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter/shortcuts"
+)
+
+// Pod ...
+type Pod struct {
+ *libpod.Pod
+}
+
+// RemovePods ...
+func (r *LocalRuntime) RemovePods(ctx context.Context, cli *cliconfig.PodRmValues) ([]string, []error) {
+ var (
+ errs []error
+ podids []string
+ )
+ pods, err := shortcuts.GetPodsByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
+ if err != nil {
+ errs = append(errs, err)
+ return nil, errs
+ }
+
+ for _, p := range pods {
+ if err := r.RemovePod(ctx, p, cli.Force, cli.Force); err != nil {
+ errs = append(errs, err)
+ } else {
+ podids = append(podids, p.ID())
+ }
+ }
+ return podids, errs
+}
+
+// GetLatestPod gets the latest pod and wraps it in an adapter pod
+func (r *LocalRuntime) GetLatestPod() (*Pod, error) {
+ pod := Pod{}
+ p, err := r.Runtime.GetLatestPod()
+ pod.Pod = p
+ return &pod, err
+}
+
+// GetAllPods gets all pods and wraps it in an adapter pod
+func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
+ var pods []*Pod
+ allPods, err := r.Runtime.GetAllPods()
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range allPods {
+ pod := Pod{}
+ pod.Pod = p
+ pods = append(pods, &pod)
+ }
+ return pods, nil
+}
+
+// LookupPod gets a pod by name or id and wraps it in an adapter pod
+func (r *LocalRuntime) LookupPod(nameOrID string) (*Pod, error) {
+ pod := Pod{}
+ p, err := r.Runtime.LookupPod(nameOrID)
+ pod.Pod = p
+ return &pod, err
+}
+
+// StopPods is a wrapper to libpod to stop pods based on a cli context
+func (r *LocalRuntime) StopPods(ctx context.Context, cli *cliconfig.PodStopValues) ([]string, []error) {
+ timeout := -1
+ if cli.Flags().Changed("timeout") {
+ timeout = int(cli.Timeout)
+ }
+ var (
+ errs []error
+ podids []string
+ )
+ pods, err := shortcuts.GetPodsByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
+ if err != nil {
+ errs = append(errs, err)
+ return nil, errs
+ }
+
+ for _, p := range pods {
+ stopped := true
+ conErrs, stopErr := p.StopWithTimeout(ctx, true, int(timeout))
+ if stopErr != nil {
+ errs = append(errs, stopErr)
+ stopped = false
+ }
+ if conErrs != nil {
+ stopped = false
+ for _, err := range conErrs {
+ errs = append(errs, err)
+ }
+ }
+ if stopped {
+ podids = append(podids, p.ID())
+ }
+ }
+ return podids, errs
+}
+
+// KillPods is a wrapper to libpod to start pods based on the cli context
+func (r *LocalRuntime) KillPods(ctx context.Context, cli *cliconfig.PodKillValues, signal uint) ([]string, []error) {
+ var (
+ errs []error
+ podids []string
+ )
+ pods, err := shortcuts.GetPodsByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
+ if err != nil {
+ errs = append(errs, err)
+ return nil, errs
+ }
+ for _, p := range pods {
+ killed := true
+ conErrs, killErr := p.Kill(signal)
+ if killErr != nil {
+ errs = append(errs, killErr)
+ killed = false
+ }
+ if conErrs != nil {
+ killed = false
+ for _, err := range conErrs {
+ errs = append(errs, err)
+ }
+ }
+ if killed {
+ podids = append(podids, p.ID())
+ }
+ }
+ return podids, errs
+}
+
+// StartPods is a wrapper to start pods based on the cli context
+func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartValues) ([]string, []error) {
+ var (
+ errs []error
+ podids []string
+ )
+ pods, err := shortcuts.GetPodsByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
+ if err != nil {
+ errs = append(errs, err)
+ return nil, errs
+ }
+ for _, p := range pods {
+ started := true
+ conErrs, startErr := p.Start(ctx)
+ if startErr != nil {
+ errs = append(errs, startErr)
+ started = false
+ }
+ if conErrs != nil {
+ started = false
+ for _, err := range conErrs {
+ errs = append(errs, err)
+ }
+ }
+ if started {
+ podids = append(podids, p.ID())
+ }
+ }
+ return podids, errs
+}
+
+// CreatePod is a wrapper for libpod and creating a new pod from the cli context
+func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) {
+ var (
+ options []libpod.PodCreateOption
+ err error
+ )
+
+ if cli.Flag("cgroup-parent").Changed {
+ options = append(options, libpod.WithPodCgroupParent(cli.CgroupParent))
+ }
+
+ if len(labels) != 0 {
+ options = append(options, libpod.WithPodLabels(labels))
+ }
+
+ if cli.Flag("name").Changed {
+ options = append(options, libpod.WithPodName(cli.Name))
+ }
+
+ if cli.Infra {
+ options = append(options, libpod.WithInfraContainer())
+ nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ","))
+ if err != nil {
+ return "", err
+ }
+ options = append(options, nsOptions...)
+ }
+
+ if len(cli.Publish) > 0 {
+ portBindings, err := shared.CreatePortBindings(cli.Publish)
+ if err != nil {
+ return "", err
+ }
+ options = append(options, libpod.WithInfraContainerPorts(portBindings))
+
+ }
+ // always have containers use pod cgroups
+ // User Opt out is not yet supported
+ options = append(options, libpod.WithPodCgroups())
+
+ pod, err := r.NewPod(ctx, options...)
+ if err != nil {
+ return "", err
+ }
+ return pod.ID(), nil
+}
+
+// GetPodStatus is a wrapper to get the status of a local libpod pod
+func (p *Pod) GetPodStatus() (string, error) {
+ return shared.GetPodStatus(p.Pod)
+}
+
+// BatchContainerOp is a wrapper for the shared function of the same name
+func BatchContainerOp(ctr *libpod.Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) {
+ return shared.BatchContainerOp(ctr, opts)
+}
+
+// PausePods is a wrapper for pausing pods via libpod
+func (r *LocalRuntime) PausePods(c *cliconfig.PodPauseValues) ([]string, map[string]error, []error) {
+ var (
+ pauseIDs []string
+ pauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ return nil, containerErrors, pauseErrors
+ }
+
+ for _, pod := range pods {
+ ctrErrs, err := pod.Pause()
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ continue
+ }
+ if ctrErrs != nil {
+ for ctr, err := range ctrErrs {
+ containerErrors[ctr] = err
+ }
+ continue
+ }
+ pauseIDs = append(pauseIDs, pod.ID())
+
+ }
+ return pauseIDs, containerErrors, pauseErrors
+}
+
+// UnpausePods is a wrapper for unpausing pods via libpod
+func (r *LocalRuntime) UnpausePods(c *cliconfig.PodUnpauseValues) ([]string, map[string]error, []error) {
+ var (
+ unpauseIDs []string
+ unpauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ return nil, containerErrors, unpauseErrors
+ }
+
+ for _, pod := range pods {
+ ctrErrs, err := pod.Unpause()
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ continue
+ }
+ if ctrErrs != nil {
+ for ctr, err := range ctrErrs {
+ containerErrors[ctr] = err
+ }
+ continue
+ }
+ unpauseIDs = append(unpauseIDs, pod.ID())
+
+ }
+ return unpauseIDs, containerErrors, unpauseErrors
+}
+
+// RestartPods is a wrapper to restart pods via libpod
+func (r *LocalRuntime) RestartPods(ctx context.Context, c *cliconfig.PodRestartValues) ([]string, map[string]error, []error) {
+ var (
+ restartIDs []string
+ restartErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ return nil, containerErrors, restartErrors
+ }
+
+ for _, pod := range pods {
+ ctrErrs, err := pod.Restart(ctx)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ continue
+ }
+ if ctrErrs != nil {
+ for ctr, err := range ctrErrs {
+ containerErrors[ctr] = err
+ }
+ continue
+ }
+ restartIDs = append(restartIDs, pod.ID())
+
+ }
+ return restartIDs, containerErrors, restartErrors
+
+}
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
new file mode 100644
index 000000000..220f7163f
--- /dev/null
+++ b/pkg/adapter/pods_remote.go
@@ -0,0 +1,401 @@
+// +build remoteclient
+
+package adapter
+
+import (
+ "context"
+ "encoding/json"
+ "strings"
+ "time"
+
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
+ "github.com/pkg/errors"
+ "github.com/ulule/deepcopier"
+)
+
+// Pod ...
+type Pod struct {
+ remotepod
+}
+
+type remotepod struct {
+ config *libpod.PodConfig
+ state *libpod.PodInspectState
+ containers []libpod.PodContainerInfo
+ Runtime *LocalRuntime
+}
+
+// RemovePods removes one or more based on the cli context.
+func (r *LocalRuntime) RemovePods(ctx context.Context, cli *cliconfig.PodRmValues) ([]string, []error) {
+ var (
+ rmErrs []error
+ rmPods []string
+ )
+ podIDs, err := iopodman.GetPodsByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
+ if err != nil {
+ rmErrs = append(rmErrs, err)
+ return nil, rmErrs
+ }
+
+ for _, p := range podIDs {
+ reply, err := iopodman.RemovePod().Call(r.Conn, p, cli.Force)
+ if err != nil {
+ rmErrs = append(rmErrs, err)
+ } else {
+ rmPods = append(rmPods, reply)
+ }
+ }
+ return rmPods, rmErrs
+}
+
+// Inspect looks up a pod by name or id and embeds its data into a remote pod
+// object.
+func (r *LocalRuntime) Inspect(nameOrID string) (*Pod, error) {
+ reply, err := iopodman.PodStateData().Call(r.Conn, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ data := libpod.PodInspect{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ pod := Pod{}
+ pod.Runtime = r
+ pod.config = data.Config
+ pod.state = data.State
+ pod.containers = data.Containers
+ return &pod, nil
+}
+
+// GetLatestPod gets the latest pod and wraps it in an adapter pod
+func (r *LocalRuntime) GetLatestPod() (*Pod, error) {
+ reply, err := iopodman.GetPodsByContext().Call(r.Conn, false, true, nil)
+ if err != nil {
+ return nil, err
+ }
+ if len(reply) > 0 {
+ return r.Inspect(reply[0])
+ }
+ return nil, errors.New("no pods exist")
+}
+
+// LookupPod gets a pod by name or ID and wraps it in an adapter pod
+func (r *LocalRuntime) LookupPod(nameOrID string) (*Pod, error) {
+ return r.Inspect(nameOrID)
+}
+
+// Inspect, like libpod pod inspect, returns a libpod.PodInspect object from
+// the data of a remotepod data struct
+func (p *Pod) Inspect() (*libpod.PodInspect, error) {
+ config := new(libpod.PodConfig)
+ deepcopier.Copy(p.remotepod.config).To(config)
+ inspectData := libpod.PodInspect{
+ Config: config,
+ State: p.remotepod.state,
+ Containers: p.containers,
+ }
+ return &inspectData, nil
+}
+
+// StopPods stops pods based on the cli context from the remote client.
+func (r *LocalRuntime) StopPods(ctx context.Context, cli *cliconfig.PodStopValues) ([]string, []error) {
+ var (
+ stopErrs []error
+ stopPods []string
+ )
+ var timeout int64 = -1
+ if cli.Flags().Changed("timeout") {
+ timeout = int64(cli.Timeout)
+ }
+ podIDs, err := iopodman.GetPodsByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ for _, p := range podIDs {
+ podID, err := iopodman.StopPod().Call(r.Conn, p, timeout)
+ if err != nil {
+ stopErrs = append(stopErrs, err)
+ } else {
+ stopPods = append(stopPods, podID)
+ }
+ }
+ return stopPods, stopErrs
+}
+
+// KillPods kills pods over varlink for the remoteclient
+func (r *LocalRuntime) KillPods(ctx context.Context, cli *cliconfig.PodKillValues, signal uint) ([]string, []error) {
+ var (
+ killErrs []error
+ killPods []string
+ )
+
+ podIDs, err := iopodman.GetPodsByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ for _, p := range podIDs {
+ podID, err := iopodman.KillPod().Call(r.Conn, p, int64(signal))
+ if err != nil {
+ killErrs = append(killErrs, err)
+ } else {
+ killPods = append(killPods, podID)
+ }
+ }
+ return killPods, killErrs
+}
+
+// StartPods starts pods for the remote client over varlink
+func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartValues) ([]string, []error) {
+ var (
+ startErrs []error
+ startPods []string
+ )
+
+ podIDs, err := iopodman.GetPodsByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ for _, p := range podIDs {
+ podID, err := iopodman.StartPod().Call(r.Conn, p)
+ if err != nil {
+ startErrs = append(startErrs, err)
+ } else {
+ startPods = append(startPods, podID)
+ }
+ }
+ return startPods, startErrs
+}
+
+// CreatePod creates a pod for the remote client over a varlink connection
+func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) {
+ pc := iopodman.PodCreate{
+ Name: cli.Name,
+ CgroupParent: cli.CgroupParent,
+ Labels: labels,
+ Share: strings.Split(cli.Share, ","),
+ Infra: cli.Infra,
+ InfraCommand: cli.InfraCommand,
+ InfraImage: cli.InfraCommand,
+ Publish: cli.Publish,
+ }
+
+ return iopodman.CreatePod().Call(r.Conn, pc)
+}
+
+// GetAllPods is a helper function that gets all pods for the remote client
+func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
+ var pods []*Pod
+ podIDs, err := iopodman.GetPodsByContext().Call(r.Conn, true, false, []string{})
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range podIDs {
+ pod, err := r.LookupPod(p)
+ if err != nil {
+ return nil, err
+ }
+ pods = append(pods, pod)
+ }
+ return pods, nil
+}
+
+// ID returns the id of a remote pod
+func (p *Pod) ID() string {
+ return p.config.ID
+}
+
+// Name returns the name of the remote pod
+func (p *Pod) Name() string {
+ return p.config.Name
+}
+
+// AllContainersByID returns a slice of a pod's container IDs
+func (p *Pod) AllContainersByID() ([]string, error) {
+ var containerIDs []string
+ for _, ctr := range p.containers {
+ containerIDs = append(containerIDs, ctr.ID)
+ }
+ return containerIDs, nil
+}
+
+// AllContainers returns a pods containers
+func (p *Pod) AllContainers() ([]*Container, error) {
+ var containers []*Container
+ for _, ctr := range p.containers {
+ container, err := p.Runtime.LookupContainer(ctr.ID)
+ if err != nil {
+ return nil, err
+ }
+ containers = append(containers, container)
+ }
+ return containers, nil
+}
+
+// Status ...
+func (p *Pod) Status() (map[string]libpod.ContainerStatus, error) {
+ ctrs := make(map[string]libpod.ContainerStatus)
+ for _, i := range p.containers {
+ var status libpod.ContainerStatus
+ switch i.State {
+ case "exited":
+ status = libpod.ContainerStateExited
+ case "stopped":
+ status = libpod.ContainerStateStopped
+ case "running":
+ status = libpod.ContainerStateRunning
+ case "paused":
+ status = libpod.ContainerStatePaused
+ case "created":
+ status = libpod.ContainerStateCreated
+ case "configured":
+ status = libpod.ContainerStateConfigured
+ default:
+ status = libpod.ContainerStateUnknown
+ }
+ ctrs[i.ID] = status
+ }
+ return ctrs, nil
+}
+
+// GetPodStatus is a wrapper to get the string version of the status
+func (p *Pod) GetPodStatus() (string, error) {
+ ctrStatuses, err := p.Status()
+ if err != nil {
+ return "", err
+ }
+ return shared.CreatePodStatusResults(ctrStatuses)
+}
+
+// InfraContainerID returns the ID of the infra container in a pod
+func (p *Pod) InfraContainerID() (string, error) {
+ return p.state.InfraContainerID, nil
+}
+
+// CreatedTime returns the time the container was created as a time.Time
+func (p *Pod) CreatedTime() time.Time {
+ return p.config.CreatedTime
+}
+
+// SharesPID ....
+func (p *Pod) SharesPID() bool {
+ return p.config.UsePodPID
+}
+
+// SharesIPC returns whether containers in pod
+// default to use IPC namespace of first container in pod
+func (p *Pod) SharesIPC() bool {
+ return p.config.UsePodIPC
+}
+
+// SharesNet returns whether containers in pod
+// default to use network namespace of first container in pod
+func (p *Pod) SharesNet() bool {
+ return p.config.UsePodNet
+}
+
+// SharesMount returns whether containers in pod
+// default to use PID namespace of first container in pod
+func (p *Pod) SharesMount() bool {
+ return p.config.UsePodMount
+}
+
+// SharesUser returns whether containers in pod
+// default to use user namespace of first container in pod
+func (p *Pod) SharesUser() bool {
+ return p.config.UsePodUser
+}
+
+// SharesUTS returns whether containers in pod
+// default to use UTS namespace of first container in pod
+func (p *Pod) SharesUTS() bool {
+ return p.config.UsePodUTS
+}
+
+// SharesCgroup returns whether containers in the pod will default to this pod's
+// cgroup instead of the default libpod parent
+func (p *Pod) SharesCgroup() bool {
+ return p.config.UsePodCgroup
+}
+
+// CgroupParent returns the pod's CGroup parent
+func (p *Pod) CgroupParent() string {
+ return p.config.CgroupParent
+}
+
+// PausePods pauses a pod using varlink and the remote client
+func (r *LocalRuntime) PausePods(c *cliconfig.PodPauseValues) ([]string, map[string]error, []error) {
+ var (
+ pauseIDs []string
+ pauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ return nil, containerErrors, pauseErrors
+ }
+ for _, pod := range pods {
+ reply, err := iopodman.PausePod().Call(r.Conn, pod)
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ continue
+ }
+ pauseIDs = append(pauseIDs, reply)
+ }
+ return pauseIDs, nil, pauseErrors
+}
+
+// UnpausePods unpauses a pod using varlink and the remote client
+func (r *LocalRuntime) UnpausePods(c *cliconfig.PodUnpauseValues) ([]string, map[string]error, []error) {
+ var (
+ unpauseIDs []string
+ unpauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ return nil, containerErrors, unpauseErrors
+ }
+ for _, pod := range pods {
+ reply, err := iopodman.UnpausePod().Call(r.Conn, pod)
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ continue
+ }
+ unpauseIDs = append(unpauseIDs, reply)
+ }
+ return unpauseIDs, nil, unpauseErrors
+}
+
+// RestartPods restarts pods using varlink and the remote client
+func (r *LocalRuntime) RestartPods(ctx context.Context, c *cliconfig.PodRestartValues) ([]string, map[string]error, []error) {
+ var (
+ restartIDs []string
+ restartErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ return nil, containerErrors, restartErrors
+ }
+ for _, pod := range pods {
+ reply, err := iopodman.RestartPod().Call(r.Conn, pod)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ continue
+ }
+ restartIDs = append(restartIDs, reply)
+ }
+ return restartIDs, nil, restartErrors
+}
diff --git a/libpod/adapter/runtime.go b/pkg/adapter/runtime.go
index 02ef9af07..4f5b98dbb 100644
--- a/libpod/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -310,3 +310,26 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti
func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
return r.Runtime.PruneVolumes(ctx)
}
+
+// SaveImage is a wrapper function for saving an image to the local filesystem
+func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) error {
+ source := c.InputArgs[0]
+ additionalTags := c.InputArgs[1:]
+
+ newImage, err := r.Runtime.ImageRuntime().NewFromLocal(source)
+ if err != nil {
+ return err
+ }
+ return newImage.Save(ctx, source, c.Format, c.Output, additionalTags, c.Quiet, c.Compress)
+}
+
+// LoadImage is a wrapper function for libpod PruneVolumes
+func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) {
+ var (
+ writer io.Writer
+ )
+ if !cli.Quiet {
+ writer = os.Stderr
+ }
+ return r.Runtime.LoadImage(ctx, name, cli.Input, writer, cli.SignaturePolicy)
+}
diff --git a/libpod/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index f63b5875d..ca2fad852 100644
--- a/libpod/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -20,6 +20,7 @@ import (
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -161,14 +162,30 @@ func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
// LoadFromArchiveReference creates an image from a local archive
func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) {
+ var iid string
// TODO We need to find a way to leak certDir, creds, and the tlsverify into this function, normally this would
// come from cli options but we don't want want those in here either.
tlsverify := true
- imageID, err := iopodman.PullImage().Call(r.Conn, srcRef.DockerReference().String(), "", "", signaturePolicyPath, &tlsverify)
+ reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, srcRef.DockerReference().String(), "", "", signaturePolicyPath, &tlsverify)
if err != nil {
return nil, err
}
- newImage, err := r.NewImageFromLocal(imageID)
+
+ for {
+ responses, flags, err := reply()
+ if err != nil {
+ return nil, err
+ }
+ for _, line := range responses.Logs {
+ fmt.Print(line)
+ }
+ iid = responses.Id
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+
+ newImage, err := r.NewImageFromLocal(iid)
if err != nil {
return nil, err
}
@@ -177,6 +194,7 @@ func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef type
// New calls into local storage to look for an image in local storage or to pull it
func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool, label *string) (*ContainerImage, error) {
+ var iid string
if label != nil {
return nil, errors.New("the remote client function does not support checking a remote image for a label")
}
@@ -194,11 +212,24 @@ func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authf
tlsVerifyPtr = &tlsVerify
}
- imageID, err := iopodman.PullImage().Call(r.Conn, name, dockeroptions.DockerCertPath, "", signaturePolicyPath, tlsVerifyPtr)
+ reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, name, dockeroptions.DockerCertPath, "", signaturePolicyPath, tlsVerifyPtr)
if err != nil {
return nil, err
}
- newImage, err := r.NewImageFromLocal(imageID)
+ for {
+ responses, flags, err := reply()
+ if err != nil {
+ return nil, err
+ }
+ for _, line := range responses.Logs {
+ fmt.Print(line)
+ }
+ iid = responses.Id
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+ newImage, err := r.NewImageFromLocal(iid)
if err != nil {
return nil, err
}
@@ -355,8 +386,11 @@ func (r *LocalRuntime) Export(name string, path string) error {
if err != nil {
return err
}
+ return r.GetFileFromRemoteHost(tempPath, path, true)
+}
- outputFile, err := os.Create(path)
+func (r *LocalRuntime) GetFileFromRemoteHost(remoteFilePath, outputPath string, delete bool) error {
+ outputFile, err := os.Create(outputPath)
if err != nil {
return err
}
@@ -365,7 +399,7 @@ func (r *LocalRuntime) Export(name string, path string) error {
writer := bufio.NewWriter(outputFile)
defer writer.Flush()
- reply, err := iopodman.ReceiveFile().Send(r.Conn, varlink.Upgrade, tempPath, true)
+ reply, err := iopodman.ReceiveFile().Send(r.Conn, varlink.Upgrade, remoteFilePath, delete)
if err != nil {
return err
}
@@ -379,7 +413,6 @@ func (r *LocalRuntime) Export(name string, path string) error {
if _, err := io.CopyN(writer, reader, length); err != nil {
return errors.Wrap(err, "file transer failed")
}
-
return nil
}
@@ -437,34 +470,23 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti
Squash: options.Squash,
}
// tar the file
- logrus.Debugf("creating tarball of context dir %s", options.ContextDirectory)
- input, err := archive.Tar(options.ContextDirectory, archive.Uncompressed)
- if err != nil {
- return errors.Wrapf(err, "unable to create tarball of context dir %s", options.ContextDirectory)
- }
-
- // Write the tarball to the fs
- // TODO we might considering sending this without writing to the fs for the sake of performance
- // under given conditions like memory availability.
outputFile, err := ioutil.TempFile("", "varlink_tar_send")
if err != nil {
return err
}
defer outputFile.Close()
- logrus.Debugf("writing context dir tarball to %s", outputFile.Name())
+ defer os.Remove(outputFile.Name())
- _, err = io.Copy(outputFile, input)
- if err != nil {
+ // Create the tarball of the context dir to a tempfile
+ if err := utils.TarToFilesystem(options.ContextDirectory, outputFile); err != nil {
return err
}
-
- logrus.Debugf("completed writing context dir tarball %s", outputFile.Name())
// Send the context dir tarball over varlink.
tempFile, err := r.SendFileOverVarlink(outputFile.Name())
if err != nil {
return err
}
- buildinfo.ContextDir = strings.Replace(tempFile, ":", "", -1)
+ buildinfo.ContextDir = tempFile
reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo)
if err != nil {
@@ -527,7 +549,7 @@ func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) {
}
- return tempFile, nil
+ return strings.Replace(tempFile, ":", "", -1), nil
}
// GetAllVolumes retrieves all the volumes
@@ -672,3 +694,105 @@ func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
}
return prunedNames, errs
}
+
+// SaveImage is a wrapper function for saving an image to the local filesystem
+func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) error {
+ source := c.InputArgs[0]
+ additionalTags := c.InputArgs[1:]
+
+ options := iopodman.ImageSaveOptions{
+ Name: source,
+ Format: c.Format,
+ Output: c.Output,
+ MoreTags: additionalTags,
+ Quiet: c.Quiet,
+ Compress: c.Compress,
+ }
+ reply, err := iopodman.ImageSave().Send(r.Conn, varlink.More, options)
+ if err != nil {
+ return err
+ }
+
+ var fetchfile string
+ for {
+ responses, flags, err := reply()
+ if err != nil {
+ return err
+ }
+ if len(responses.Id) > 0 {
+ fetchfile = responses.Id
+ }
+ for _, line := range responses.Logs {
+ fmt.Print(line)
+ }
+ if flags&varlink.Continues == 0 {
+ break
+ }
+
+ }
+ if err != nil {
+ return err
+ }
+
+ outputToDir := false
+ outfile := c.Output
+ var outputFile *os.File
+ // If the result is supposed to be a dir, then we need to put the tarfile
+ // from the host in a temporary file
+ if options.Format != "oci-archive" && options.Format != "docker-archive" {
+ outputToDir = true
+ outputFile, err = ioutil.TempFile("", "saveimage_tempfile")
+ if err != nil {
+ return err
+ }
+ outfile = outputFile.Name()
+ defer outputFile.Close()
+ defer os.Remove(outputFile.Name())
+ }
+ // We now need to fetch the tarball result back to the more system
+ if err := r.GetFileFromRemoteHost(fetchfile, outfile, true); err != nil {
+ return err
+ }
+
+ // If the result is a tarball, we're done
+ // If it is a dir, we need to untar the temporary file into the dir
+ if outputToDir {
+ if err := utils.UntarToFileSystem(c.Output, outputFile, &archive.TarOptions{}); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// LoadImage loads a container image from a remote client's filesystem
+func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) {
+ var names string
+ remoteTempFile, err := r.SendFileOverVarlink(cli.Input)
+ if err != nil {
+ return "", nil
+ }
+ more := varlink.More
+ if cli.Quiet {
+ more = 0
+ }
+ reply, err := iopodman.LoadImage().Send(r.Conn, uint64(more), name, remoteTempFile, cli.Quiet, true)
+ if err != nil {
+ return "", err
+ }
+
+ for {
+ responses, flags, err := reply()
+ if err != nil {
+ logrus.Error(err)
+ return "", err
+ }
+ for _, line := range responses.Logs {
+ fmt.Print(line)
+ }
+ names = responses.Id
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+ return names, nil
+}
diff --git a/pkg/adapter/shortcuts/shortcuts.go b/pkg/adapter/shortcuts/shortcuts.go
new file mode 100644
index 000000000..0633399ae
--- /dev/null
+++ b/pkg/adapter/shortcuts/shortcuts.go
@@ -0,0 +1,27 @@
+package shortcuts
+
+import "github.com/containers/libpod/libpod"
+
+// GetPodsByContext gets pods whether all, latest, or a slice of names/ids
+func GetPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) {
+ var outpods []*libpod.Pod
+ if all {
+ return runtime.GetAllPods()
+ }
+ if latest {
+ p, err := runtime.GetLatestPod()
+ if err != nil {
+ return nil, err
+ }
+ outpods = append(outpods, p)
+ return outpods, nil
+ }
+ for _, p := range pods {
+ pod, err := runtime.LookupPod(p)
+ if err != nil {
+ return nil, err
+ }
+ outpods = append(outpods, pod)
+ }
+ return outpods, nil
+}
diff --git a/libpod/adapter/volumes_remote.go b/pkg/adapter/volumes_remote.go
index beacd943a..beacd943a 100644
--- a/libpod/adapter/volumes_remote.go
+++ b/pkg/adapter/volumes_remote.go
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index 279a03d3f..dfbc7fe33 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -12,6 +12,7 @@
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
+#include <stdbool.h>
static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces";
static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone";
@@ -108,6 +109,13 @@ reexec_userns_join (int userns, int mountns)
char uid[16];
char **argv;
int pid;
+ char *cwd = getcwd (NULL, 0);
+
+ if (cwd == NULL)
+ {
+ fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
sprintf (uid, "%d", geteuid ());
@@ -153,6 +161,13 @@ reexec_userns_join (int userns, int mountns)
_exit (EXIT_FAILURE);
}
+ if (chdir (cwd) < 0)
+ {
+ fprintf (stderr, "cannot chdir: %s\n", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+ free (cwd);
+
execvp (argv[0], argv);
_exit (EXIT_FAILURE);
@@ -186,6 +201,25 @@ reexec_in_user_namespace (int ready)
pid_t ppid = getpid ();
char **argv;
char uid[16];
+ char *listen_fds = NULL;
+ char *listen_pid = NULL;
+ bool do_socket_activation = false;
+ char *cwd = getcwd (NULL, 0);
+
+ if (cwd == NULL)
+ {
+ fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+
+ listen_pid = getenv("LISTEN_PID");
+ listen_fds = getenv("LISTEN_FDS");
+
+ if (listen_pid != NULL && listen_fds != NULL) {
+ if (strtol(listen_pid, NULL, 10) == getpid()) {
+ do_socket_activation = true;
+ }
+ }
sprintf (uid, "%d", geteuid ());
@@ -197,8 +231,22 @@ reexec_in_user_namespace (int ready)
check_proc_sys_userns_file (_max_user_namespaces);
check_proc_sys_userns_file (_unprivileged_user_namespaces);
}
- if (pid)
+ if (pid) {
+ if (do_socket_activation) {
+ long num_fds;
+ num_fds = strtol(listen_fds, NULL, 10);
+ if (num_fds != LONG_MIN && num_fds != LONG_MAX) {
+ long i;
+ for (i = 0; i < num_fds; i++) {
+ close(3+i);
+ }
+ }
+ unsetenv("LISTEN_PID");
+ unsetenv("LISTEN_FDS");
+ unsetenv("LISTEN_FDNAMES");
+ }
return pid;
+ }
argv = get_cmd_line_args (ppid);
if (argv == NULL)
@@ -207,6 +255,12 @@ reexec_in_user_namespace (int ready)
_exit (EXIT_FAILURE);
}
+ if (do_socket_activation) {
+ char s[32];
+ sprintf(s, "%d", getpid());
+ setenv("LISTEN_PID", s, true);
+ }
+
setenv ("_LIBPOD_USERNS_CONFIGURED", "init", 1);
setenv ("_LIBPOD_ROOTLESS_UID", uid, 1);
@@ -232,6 +286,13 @@ reexec_in_user_namespace (int ready)
_exit (EXIT_FAILURE);
}
+ if (chdir (cwd) < 0)
+ {
+ fprintf (stderr, "cannot chdir: %s\n", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+ free (cwd);
+
execvp (argv[0], argv);
_exit (EXIT_FAILURE);
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 8da44a2f0..50e07ee74 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -446,7 +446,15 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
}
if IsNS(string(c.NetMode)) {
- // pass
+ split := strings.SplitN(string(c.NetMode), ":", 2)
+ if len(split[0]) != 2 {
+ return nil, errors.Errorf("invalid user defined network namespace %q", c.NetMode.UserDefined())
+ }
+ _, err := os.Stat(split[1])
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithNetNS(portBindings, false, string(c.NetMode), networks))
} else if c.NetMode.IsContainer() {
connectedCtr, err := c.Runtime.LookupContainer(c.NetMode.Container())
if err != nil {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 76b8963ff..28a636fa6 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -3,10 +3,12 @@ package createconfig
import (
"os"
"path"
+ "path/filepath"
"strings"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage/pkg/mount"
+ pmount "github.com/containers/storage/pkg/mount"
"github.com/docker/docker/daemon/caps"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user"
@@ -392,9 +394,65 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
configSpec.Linux.Resources = &spec.LinuxResources{}
}
+ // Make sure that the bind mounts keep options like nosuid, noexec, nodev.
+ mounts, err := pmount.GetMounts()
+ if err != nil {
+ return nil, err
+ }
+ for i := range configSpec.Mounts {
+ m := &configSpec.Mounts[i]
+ isBind := false
+ for _, o := range m.Options {
+ if o == "bind" || o == "rbind" {
+ isBind = true
+ break
+ }
+ }
+ if !isBind {
+ continue
+ }
+ mount, err := findMount(m.Source, mounts)
+ if err != nil {
+ return nil, err
+ }
+ if mount == nil {
+ continue
+ }
+ next_option:
+ for _, o := range strings.Split(mount.Opts, ",") {
+ if o == "nosuid" || o == "noexec" || o == "nodev" {
+ for _, e := range m.Options {
+ if e == o {
+ continue next_option
+ }
+ }
+ m.Options = append(m.Options, o)
+ }
+ }
+ }
+
return configSpec, nil
}
+func findMount(target string, mounts []*pmount.Info) (*pmount.Info, error) {
+ var err error
+ target, err = filepath.Abs(target)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot resolve %s", target)
+ }
+ var bestSoFar *pmount.Info
+ for _, i := range mounts {
+ if bestSoFar != nil && len(bestSoFar.Mountpoint) > len(i.Mountpoint) {
+ // Won't be better than what we have already found
+ continue
+ }
+ if strings.HasPrefix(target, i.Mountpoint) {
+ bestSoFar = i
+ }
+ }
+ return bestSoFar, nil
+}
+
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
if config.PidMode.IsHost() && rootless.IsRootless() {
return
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 7345a1dd8..ad9f107a7 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -47,7 +47,7 @@ func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error {
func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
ctr, err := i.Runtime.LookupContainer(id)
if err != nil {
- return call.ReplyContainerNotFound(id)
+ return call.ReplyContainerNotFound(id, err.Error())
}
opts := shared.PsOptions{
Namespace: true,
@@ -64,7 +64,7 @@ func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
inspectInfo, err := ctr.Inspect(true)
if err != nil {
@@ -90,7 +90,7 @@ func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) err
func (i *LibpodAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
containerState, err := ctr.State()
if err != nil {
@@ -118,7 +118,7 @@ func (i *LibpodAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) err
var logs []string
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
logPath := ctr.LogPath()
@@ -198,7 +198,7 @@ func (i *LibpodAPI) ListContainerChanges(call iopodman.VarlinkCall, name string)
func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
outputFile, err := ioutil.TempFile("", "varlink_recv")
if err != nil {
@@ -220,7 +220,7 @@ func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath str
func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
containerStats, err := ctr.GetContainerStats(&libpod.ContainerStats{})
if err != nil {
@@ -251,7 +251,7 @@ func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) er
func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
state, err := ctr.State()
if err != nil {
@@ -270,7 +270,7 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error
func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.StopWithTimeout(uint(timeout)); err != nil && err != libpod.ErrCtrStopped {
return call.ReplyErrorOccurred(err.Error())
@@ -282,7 +282,7 @@ func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeou
func (i *LibpodAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.RestartWithTimeout(getContext(), uint(timeout)); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -311,7 +311,7 @@ func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal
}
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Kill(killSignal); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -323,7 +323,7 @@ func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal
func (i *LibpodAPI) PauseContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Pause(); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -335,7 +335,7 @@ func (i *LibpodAPI) PauseContainer(call iopodman.VarlinkCall, name string) error
func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Unpause(); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -347,7 +347,7 @@ func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) err
func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
exitCode, err := ctr.Wait()
if err != nil {
@@ -362,7 +362,7 @@ func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, forc
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -398,7 +398,7 @@ func (i *LibpodAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error {
func (i *LibpodAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
status, err := ctr.State()
@@ -427,7 +427,7 @@ func (i *LibpodAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string,
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
options := libpod.ContainerCheckpointOptions{
@@ -446,7 +446,7 @@ func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, kee
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
options := libpod.ContainerCheckpointOptions{
@@ -475,7 +475,7 @@ func getArtifact(ctr *libpod.Container) (*cc.CreateConfig, error) {
func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyErrorOccurred(err.Error())
+ return call.ReplyContainerNotFound(name, err.Error())
}
config := ctr.Config()
b, err := json.Marshal(config)
@@ -489,7 +489,7 @@ func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) erro
func (i *LibpodAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyErrorOccurred(err.Error())
+ return call.ReplyContainerNotFound(name, err.Error())
}
artifacts, err := ctr.GetArtifact(artifactName)
if err != nil {
@@ -506,7 +506,7 @@ func (i *LibpodAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifact
func (i *LibpodAPI) ContainerInspectData(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyErrorOccurred(err.Error())
+ return call.ReplyContainerNotFound(name, err.Error())
}
data, err := ctr.Inspect(true)
if err != nil {
@@ -524,7 +524,7 @@ func (i *LibpodAPI) ContainerInspectData(call iopodman.VarlinkCall, name string)
func (i *LibpodAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyErrorOccurred(err.Error())
+ return call.ReplyContainerNotFound(name, err.Error())
}
data, err := ctr.ContainerState()
if err != nil {
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 8deb4cbe2..210f139ce 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
- "github.com/containers/image/docker"
dockerarchive "github.com/containers/image/docker/archive"
"github.com/containers/image/manifest"
"github.com/containers/image/transports/alltransports"
@@ -22,7 +21,6 @@ import (
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
- sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
@@ -75,7 +73,7 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error {
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(id)
if err != nil {
- return call.ReplyImageNotFound(id)
+ return call.ReplyImageNotFound(id, err.Error())
}
labels, err := newImage.Labels(getContext())
if err != nil {
@@ -137,7 +135,7 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
logrus.Debugf("untar of %s successful", contextDir)
// All output (stdout, stderr) is captured in output as well
- output := bytes.NewBuffer([]byte{})
+ var output bytes.Buffer
commonOpts := &buildah.CommonBuildOptions{
AddHost: config.BuildOptions.AddHosts,
@@ -170,20 +168,20 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
Compression: stringCompressionToArchiveType(config.Compression),
ContextDirectory: newContextDir,
DefaultMountsFilePath: config.DefaultsMountFilePath,
- Err: output,
+ Err: &output,
ForceRmIntermediateCtrs: config.ForceRmIntermediateCtrs,
IIDFile: config.Iidfile,
Labels: config.Label,
Layers: config.Layers,
NoCache: config.Nocache,
- Out: output,
+ Out: &output,
Output: config.Output,
NamespaceOptions: namespace,
OutputFormat: config.OutputFormat,
PullPolicy: stringPullPolicyToType(config.PullPolicy),
Quiet: config.Quiet,
RemoveIntermediateCtrs: config.RemoteIntermediateCtrs,
- ReportWriter: output,
+ ReportWriter: &output,
RuntimeArgs: config.RuntimeArgs,
SignaturePolicyPath: config.SignaturePolicyPath,
Squash: config.Squash,
@@ -208,7 +206,13 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
newPathDockerFiles = append(newPathDockerFiles, filepath.Join(newContextDir, base))
}
- c := build(i.Runtime, options, newPathDockerFiles)
+ c := make(chan error)
+ go func() {
+ err := i.Runtime.Build(getContext(), options, newPathDockerFiles...)
+ c <- err
+ close(c)
+ }()
+
var log []string
done := false
for {
@@ -257,23 +261,12 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
return call.ReplyBuildImage(br)
}
-func build(runtime *libpod.Runtime, options imagebuildah.BuildOptions, dockerfiles []string) chan error {
- c := make(chan error)
- go func() {
- err := runtime.Build(getContext(), options, dockerfiles...)
- c <- err
- close(c)
- }()
-
- return c
-}
-
// InspectImage returns an image's inspect information as a string that can be serialized.
// Requires an image ID or name
func (i *LibpodAPI) InspectImage(call iopodman.VarlinkCall, name string) error {
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
- return call.ReplyImageNotFound(name)
+ return call.ReplyImageNotFound(name, err.Error())
}
inspectInfo, err := newImage.Inspect(getContext())
if err != nil {
@@ -291,7 +284,7 @@ func (i *LibpodAPI) InspectImage(call iopodman.VarlinkCall, name string) error {
func (i *LibpodAPI) HistoryImage(call iopodman.VarlinkCall, name string) error {
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
- return call.ReplyImageNotFound(name)
+ return call.ReplyImageNotFound(name, err.Error())
}
history, err := newImage.History(getContext())
if err != nil {
@@ -320,7 +313,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVe
)
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
- return call.ReplyImageNotFound(err.Error())
+ return call.ReplyImageNotFound(name, err.Error())
}
destname := name
if tag != "" {
@@ -416,7 +409,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVe
func (i *LibpodAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error {
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
- return call.ReplyImageNotFound(name)
+ return call.ReplyImageNotFound(name, err.Error())
}
if err := newImage.TagImage(tag); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -430,7 +423,7 @@ func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bo
ctx := getContext()
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
- return call.ReplyImageNotFound(name)
+ return call.ReplyImageNotFound(name, err.Error())
}
_, err = i.Runtime.RemoveImage(ctx, newImage, force)
if err != nil {
@@ -441,54 +434,53 @@ func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bo
// SearchImages searches all registries configured in /etc/containers/registries.conf for an image
// Requires an image name and a search limit as int
-func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, tlsVerify *bool) error {
- sc := image.GetSystemContext("", "", false)
+func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, tlsVerify *bool, filter iopodman.ImageSearchFilter) error {
+ // Transform all arguments to proper types first
+ argLimit := 0
+ argTLSVerify := types.OptionalBoolUndefined
+ argIsOfficial := types.OptionalBoolUndefined
+ argIsAutomated := types.OptionalBoolUndefined
+ if limit != nil {
+ argLimit = int(*limit)
+ }
if tlsVerify != nil {
- sc.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!*tlsVerify)
+ argTLSVerify = types.NewOptionalBool(!*tlsVerify)
+ }
+ if filter.Is_official != nil {
+ argIsOfficial = types.NewOptionalBool(*filter.Is_official)
+ }
+ if filter.Is_automated != nil {
+ argIsAutomated = types.NewOptionalBool(*filter.Is_automated)
}
- var registries []string
- // Check if search query has a registry in it
- registry, err := sysreg.GetRegistry(query)
- if err != nil {
- return call.ReplyErrorOccurred(fmt.Sprintf("error getting registry from %q: %q", query, err))
+ // Transform a SearchFilter the backend can deal with
+ sFilter := image.SearchFilter{
+ IsOfficial: argIsOfficial,
+ IsAutomated: argIsAutomated,
+ Stars: int(filter.Star_count),
}
- if registry != "" {
- registries = append(registries, registry)
- query = query[len(registry)+1:]
- } else {
- registries, err = sysreg.GetRegistries()
- if err != nil {
- return call.ReplyErrorOccurred(fmt.Sprintf("unable to get system registries: %q", err))
- }
+
+ searchOptions := image.SearchOptions{
+ Limit: argLimit,
+ Filter: sFilter,
+ InsecureSkipTLSVerify: argTLSVerify,
+ }
+ results, err := image.SearchImages(query, searchOptions)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
}
+
var imageResults []iopodman.ImageSearchResult
- for _, reg := range registries {
- var lim = 1000
- if limit != nil {
- lim = int(*limit)
- }
- results, err := docker.SearchRegistry(getContext(), sc, reg, query, lim)
- if err != nil {
- // If we are searching multiple registries, don't make something like an
- // auth error fatal. Unfortunately we cannot differentiate between auth
- // errors and other possibles errors
- if len(registries) > 1 {
- continue
- }
- return call.ReplyErrorOccurred(err.Error())
- }
- for _, result := range results {
- i := iopodman.ImageSearchResult{
- Registry: reg,
- Description: result.Description,
- Is_official: result.IsOfficial,
- Is_automated: result.IsAutomated,
- Name: result.Name,
- Star_count: int64(result.StarCount),
- }
- imageResults = append(imageResults, i)
+ for _, result := range results {
+ i := iopodman.ImageSearchResult{
+ Registry: result.Index,
+ Description: result.Description,
+ Is_official: result.Official == "[OK]",
+ Is_automated: result.Automated == "[OK]",
+ Name: result.Name,
+ Star_count: int64(result.Stars),
}
+ imageResults = append(imageResults, i)
}
return call.ReplySearchImages(imageResults)
}
@@ -520,7 +512,7 @@ func (i *LibpodAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error {
func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(name, err.Error())
}
sc := image.GetSystemContext(i.Runtime.GetConfig().SignaturePolicyPath, "", false)
var mimeType string
@@ -584,7 +576,7 @@ func (i *LibpodAPI) ImportImage(call iopodman.VarlinkCall, source, reference, me
func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination string, compress bool, tags []string) error {
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
- return call.ReplyImageNotFound(name)
+ return call.ReplyImageNotFound(name, err.Error())
}
additionalTags, err := image.GetAdditionalTags(tags)
@@ -622,24 +614,74 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, certDir, c
so := image.SigningOptions{}
- if strings.HasPrefix(name, dockerarchive.Transport.Name()+":") {
- srcRef, err := alltransports.ParseImageName(name)
- if err != nil {
- return errors.Wrapf(err, "error parsing %q", name)
+ if call.WantsMore() {
+ call.Continues = true
+ }
+ output := bytes.NewBuffer([]byte{})
+ c := make(chan error)
+ go func() {
+ //err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", signaturePolicy, output, compress, so, &dockerRegistryOptions, nil)
+ if strings.HasPrefix(name, dockerarchive.Transport.Name()+":") {
+ srcRef, err := alltransports.ParseImageName(name)
+ if err != nil {
+ c <- errors.Wrapf(err, "error parsing %q", name)
+ }
+ newImage, err := i.Runtime.ImageRuntime().LoadFromArchiveReference(getContext(), srcRef, signaturePolicy, output)
+ if err != nil {
+ c <- errors.Wrapf(err, "error pulling image from %q", name)
+ }
+ imageID = newImage[0].ID()
+ } else {
+ newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, signaturePolicy, "", output, &dockerRegistryOptions, so, false, nil)
+ if err != nil {
+ c <- errors.Wrapf(err, "unable to pull %s", name)
+ }
+ imageID = newImage.ID()
}
- newImage, err := i.Runtime.ImageRuntime().LoadFromArchiveReference(getContext(), srcRef, signaturePolicy, nil)
- if err != nil {
- return errors.Wrapf(err, "error pulling image from %q", name)
+ c <- nil
+ close(c)
+ }()
+
+ var log []string
+ done := false
+ for {
+ line, err := output.ReadString('\n')
+ if err == nil {
+ log = append(log, line)
+ continue
+ } else if err == io.EOF {
+ select {
+ case err := <-c:
+ if err != nil {
+ logrus.Errorf("reading of output during pull failed for %s", name)
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ done = true
+ default:
+ if !call.WantsMore() {
+ time.Sleep(1 * time.Second)
+ break
+ }
+ br := iopodman.MoreResponse{
+ Logs: log,
+ }
+ call.ReplyPullImage(br)
+ log = []string{}
+ }
+ } else {
+ return call.ReplyErrorOccurred(err.Error())
}
- imageID = newImage[0].ID()
- } else {
- newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, signaturePolicy, "", nil, &dockerRegistryOptions, so, false, nil)
- if err != nil {
- return call.ReplyErrorOccurred(fmt.Sprintf("unable to pull %s: %s", name, err.Error()))
+ if done {
+ break
}
- imageID = newImage.ID()
}
- return call.ReplyPullImage(imageID)
+ call.Continues = false
+
+ br := iopodman.MoreResponse{
+ Logs: log,
+ Id: imageID,
+ }
+ return call.ReplyPullImage(br)
}
// ImageExists returns bool as to whether the input image exists in local storage
@@ -694,3 +736,172 @@ func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error {
}
return call.ReplyImagesPrune(prunedImages)
}
+
+// ImageSave ....
+func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageSaveOptions) error {
+ newImage, err := i.Runtime.ImageRuntime().NewFromLocal(options.Name)
+ if err != nil {
+ if errors.Cause(err) == libpod.ErrNoSuchImage {
+ return call.ReplyImageNotFound(options.Name, err.Error())
+ }
+ return call.ReplyErrorOccurred(err.Error())
+ }
+
+ // Determine if we are dealing with a tarball or dir
+ var output string
+ outputToDir := false
+ if options.Format == "oci-archive" || options.Format == "docker-archive" {
+ tempfile, err := ioutil.TempFile("", "varlink_send")
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ output = tempfile.Name()
+ tempfile.Close()
+ } else {
+ var err error
+ outputToDir = true
+ output, err = ioutil.TempDir("", "varlink_send")
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ }
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if call.WantsMore() {
+ call.Continues = true
+ }
+
+ saveOutput := bytes.NewBuffer([]byte{})
+ c := make(chan error)
+ go func() {
+ err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress)
+ c <- err
+ close(c)
+ }()
+ var log []string
+ done := false
+ for {
+ line, err := saveOutput.ReadString('\n')
+ if err == nil {
+ log = append(log, line)
+ continue
+ } else if err == io.EOF {
+ select {
+ case err := <-c:
+ if err != nil {
+ logrus.Errorf("reading of output during save failed for %s", newImage.ID())
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ done = true
+ default:
+ if !call.WantsMore() {
+ time.Sleep(1 * time.Second)
+ break
+ }
+ br := iopodman.MoreResponse{
+ Logs: log,
+ }
+ call.ReplyImageSave(br)
+ log = []string{}
+ }
+ } else {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if done {
+ break
+ }
+ }
+ call.Continues = false
+
+ sendfile := output
+ // Image has been saved to `output`
+ if outputToDir {
+ // If the output is a directory, we need to tar up the directory to send it back
+ //Create a tempfile for the directory tarball
+ outputFile, err := ioutil.TempFile("", "varlink_save_dir")
+ if err != nil {
+ return err
+ }
+ defer outputFile.Close()
+ if err := utils.TarToFilesystem(output, outputFile); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ sendfile = outputFile.Name()
+ }
+ br := iopodman.MoreResponse{
+ Logs: log,
+ Id: sendfile,
+ }
+ return call.ReplyPushImage(br)
+}
+
+// LoadImage ...
+func (i *LibpodAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, deleteInputFile, quiet bool) error {
+ var (
+ names string
+ writer io.Writer
+ err error
+ )
+ if !quiet {
+ writer = os.Stderr
+ }
+
+ if call.WantsMore() {
+ call.Continues = true
+ }
+ output := bytes.NewBuffer([]byte{})
+
+ c := make(chan error)
+ go func() {
+ names, err = i.Runtime.LoadImage(getContext(), name, inputFile, writer, "")
+ c <- err
+ close(c)
+ }()
+
+ var log []string
+ done := false
+ for {
+ line, err := output.ReadString('\n')
+ if err == nil {
+ log = append(log, line)
+ continue
+ } else if err == io.EOF {
+ select {
+ case err := <-c:
+ if err != nil {
+ logrus.Error(err)
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ done = true
+ default:
+ if !call.WantsMore() {
+ time.Sleep(1 * time.Second)
+ break
+ }
+ br := iopodman.MoreResponse{
+ Logs: log,
+ }
+ call.ReplyLoadImage(br)
+ log = []string{}
+ }
+ } else {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if done {
+ break
+ }
+ }
+ call.Continues = false
+
+ br := iopodman.MoreResponse{
+ Logs: log,
+ Id: names,
+ }
+ if deleteInputFile {
+ if err := os.Remove(inputFile); err != nil {
+ logrus.Errorf("unable to delete input file %s", inputFile)
+ }
+ }
+ return call.ReplyLoadImage(br)
+}
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index 6e758786a..4ca4c4270 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -2,6 +2,7 @@ package varlinkapi
import (
"encoding/json"
+ "github.com/containers/libpod/pkg/adapter/shortcuts"
"github.com/containers/libpod/pkg/rootless"
"syscall"
@@ -13,10 +14,6 @@ import (
// CreatePod ...
func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error {
var options []libpod.PodCreateOption
-
- if create.InfraCommand != "" || create.InfraImage != "" {
- return call.ReplyErrorOccurred("the infra-command and infra-image options are not supported yet")
- }
if create.CgroupParent != "" {
options = append(options, libpod.WithPodCgroupParent(create.CgroupParent))
}
@@ -89,7 +86,7 @@ func (i *LibpodAPI) ListPods(call iopodman.VarlinkCall) error {
func (i *LibpodAPI) GetPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
opts := shared.PsOptions{}
@@ -105,7 +102,7 @@ func (i *LibpodAPI) GetPod(call iopodman.VarlinkCall, name string) error {
func (i *LibpodAPI) InspectPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
inspectData, err := pod.Inspect()
if err != nil {
@@ -122,7 +119,7 @@ func (i *LibpodAPI) InspectPod(call iopodman.VarlinkCall, name string) error {
func (i *LibpodAPI) StartPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
ctnrs, err := pod.AllContainers()
if err != nil {
@@ -143,7 +140,7 @@ func (i *LibpodAPI) StartPod(call iopodman.VarlinkCall, name string) error {
func (i *LibpodAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.StopWithTimeout(getContext(), true, int(timeout))
callErr := handlePodCall(call, pod, ctrErrs, err)
@@ -157,7 +154,7 @@ func (i *LibpodAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int6
func (i *LibpodAPI) RestartPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
ctnrs, err := pod.AllContainers()
if err != nil {
@@ -184,7 +181,7 @@ func (i *LibpodAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.Kill(killSignal)
callErr := handlePodCall(call, pod, ctrErrs, err)
@@ -198,7 +195,7 @@ func (i *LibpodAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64
func (i *LibpodAPI) PausePod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.Pause()
callErr := handlePodCall(call, pod, ctrErrs, err)
@@ -212,7 +209,7 @@ func (i *LibpodAPI) PausePod(call iopodman.VarlinkCall, name string) error {
func (i *LibpodAPI) UnpausePod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.Unpause()
callErr := handlePodCall(call, pod, ctrErrs, err)
@@ -227,7 +224,7 @@ func (i *LibpodAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool
ctx := getContext()
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
if err = i.Runtime.RemovePod(ctx, pod, force, force); err != nil {
return call.ReplyErrorOccurred(err.Error())
@@ -240,7 +237,7 @@ func (i *LibpodAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool
func (i *LibpodAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
- return call.ReplyPodNotFound(name)
+ return call.ReplyPodNotFound(name, err.Error())
}
prevStats := make(map[string]*libpod.ContainerStats)
podStats, err := pod.GetPodStats(prevStats)
@@ -271,3 +268,34 @@ func (i *LibpodAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
}
return call.ReplyGetPodStats(pod.ID(), containersStats)
}
+
+// GetPodsByContext returns a slice of pod ids based on all, latest, or a list
+func (i *LibpodAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
+ var podids []string
+
+ pods, err := shortcuts.GetPodsByContext(all, latest, input, i.Runtime)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ for _, p := range pods {
+ podids = append(podids, p.ID())
+ }
+ return call.ReplyGetPodsByContext(podids)
+}
+
+// PodStateData returns a container's state data in string format
+func (i *LibpodAPI) PodStateData(call iopodman.VarlinkCall, name string) error {
+ pod, err := i.Runtime.LookupPod(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ data, err := pod.Inspect()
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to obtain pod state")
+ }
+ b, err := json.Marshal(data)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to serialize pod inspect data")
+ }
+ return call.ReplyPodStateData(string(b))
+}
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 4b4baa93c..33e05b872 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -28,8 +28,8 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration
}
// PodmanAsUser is the exec call to podman on the filesystem with the specified uid/gid and environment
-func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, env []string) *PodmanSessionIntegration {
- podmanSession := p.PodmanAsUserBase(args, uid, gid, env)
+func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd string, env []string) *PodmanSessionIntegration {
+ podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env)
return &PodmanSessionIntegration{podmanSession}
}
diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go
index 423f99ac8..c85810454 100644
--- a/test/e2e/load_test.go
+++ b/test/e2e/load_test.go
@@ -199,11 +199,9 @@ var _ = Describe("Podman load", func() {
It("podman load localhost registry from scratch and :latest", func() {
outfile := filepath.Join(podmanTest.TempDir, "load_test.tar.gz")
- setup := podmanTest.Podman([]string{"pull", fedoraMinimal})
- setup.WaitWithDefaultTimeout()
- Expect(setup.ExitCode()).To(Equal(0))
+ podmanTest.RestoreArtifact("fedora-minimal:latest")
- setup = podmanTest.Podman([]string{"tag", "fedora-minimal", "hello"})
+ setup := podmanTest.Podman([]string{"tag", "fedora-minimal", "hello"})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
diff --git a/test/e2e/pod_start_test.go b/test/e2e/pod_start_test.go
index 77e8b586d..ce693012d 100644
--- a/test/e2e/pod_start_test.go
+++ b/test/e2e/pod_start_test.go
@@ -136,6 +136,5 @@ var _ = Describe("Podman pod start", func() {
session = podmanTest.Podman([]string{"pod", "start", podid, "doesnotexist"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(125))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
})
})
diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go
index b3d7df252..38f118964 100644
--- a/test/e2e/pod_stop_test.go
+++ b/test/e2e/pod_stop_test.go
@@ -137,6 +137,5 @@ var _ = Describe("Podman pod stop", func() {
session = podmanTest.Podman([]string{"pod", "stop", podid1, "doesnotexist"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(125))
- Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
})
})
diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go
index bc1431bce..71dacfa80 100644
--- a/test/e2e/rm_test.go
+++ b/test/e2e/rm_test.go
@@ -128,4 +128,9 @@ var _ = Describe("Podman rm", func() {
Expect(podmanTest.NumberOfContainers()).To(Equal(1))
})
+ It("podman rm bogus container", func() {
+ session := podmanTest.Podman([]string{"rm", "bogus"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ })
})
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index c160e1bc5..dcbda2df4 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -36,7 +36,7 @@ var _ = Describe("Podman rmi", func() {
It("podman rmi bogus image", func() {
session := podmanTest.Podman([]string{"rmi", "debian:6.0.10"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
})
diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go
index 2b84d34c9..aa8ed6faa 100644
--- a/test/e2e/rootless_test.go
+++ b/test/e2e/rootless_test.go
@@ -60,7 +60,7 @@ var _ = Describe("Podman rootless", func() {
for _, v := range commands {
env := os.Environ()
env = append(env, "USER=foo")
- cmd := podmanTest.PodmanAsUser([]string{v}, 1000, 1000, env)
+ cmd := podmanTest.PodmanAsUser([]string{v}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
}
@@ -128,13 +128,13 @@ var _ = Describe("Podman rootless", func() {
env = append(env, "PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS=1")
env = append(env, "USER=foo")
- cmd := rootlessTest.PodmanAsUser([]string{"pod", "create", "--infra=false"}, 1000, 1000, env)
+ cmd := rootlessTest.PodmanAsUser([]string{"pod", "create", "--infra=false"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
podId := cmd.OutputToString()
args := []string{"run", "--pod", podId, "--rootfs", mountPath, "echo", "hello"}
- cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser(args, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
Expect(cmd.LineInOutputContains("hello")).To(BeTrue())
@@ -158,7 +158,7 @@ var _ = Describe("Podman rootless", func() {
env = append(env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", xdgRuntimeDir))
env = append(env, fmt.Sprintf("HOME=%s", home))
env = append(env, "USER=foo")
- cmd := podmanTest.PodmanAsUser([]string{"search", "docker.io/busybox"}, 1000, 1000, env)
+ cmd := podmanTest.PodmanAsUser([]string{"search", "docker.io/busybox"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
})
@@ -175,65 +175,65 @@ var _ = Describe("Podman rootless", func() {
allArgs := append([]string{"run"}, args...)
allArgs = append(allArgs, "--rootfs", mountPath, "echo", "hello")
- cmd := rootlessTest.PodmanAsUser(allArgs, 1000, 1000, env)
+ cmd := rootlessTest.PodmanAsUser(allArgs, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
Expect(cmd.LineInOutputContains("hello")).To(BeTrue())
- cmd = rootlessTest.PodmanAsUser([]string{"rm", "-l", "-f"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"rm", "-l", "-f"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
allArgs = append([]string{"run", "-d"}, args...)
allArgs = append(allArgs, "--security-opt", "seccomp=unconfined", "--rootfs", mountPath, "top")
- cmd = rootlessTest.PodmanAsUser(allArgs, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser(allArgs, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
- cmd = rootlessTest.PodmanAsUser([]string{"restart", "-l", "-t", "0"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"restart", "-l", "-t", "0"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
canUseExec := canExec()
if canUseExec {
- cmd = rootlessTest.PodmanAsUser([]string{"top", "-l"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"top", "-l"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
}
- cmd = rootlessTest.PodmanAsUser([]string{"rm", "-l", "-f"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"rm", "-l", "-f"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
allArgs = append([]string{"run", "-d"}, args...)
allArgs = append(allArgs, "--security-opt", "seccomp=unconfined", "--rootfs", mountPath, "unshare", "-r", "unshare", "-r", "top")
- cmd = rootlessTest.PodmanAsUser(allArgs, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser(allArgs, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
- cmd = rootlessTest.PodmanAsUser([]string{"stop", "-l", "-t", "0"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"stop", "-l", "-t", "0"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
- cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l", "--type", "container", "--format", "{{ .State.Status }}"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l", "--type", "container", "--format", "{{ .State.Status }}"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.LineInOutputContains("exited")).To(BeTrue())
- cmd = rootlessTest.PodmanAsUser([]string{"start", "-l"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"start", "-l"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
- cmd = rootlessTest.PodmanAsUser([]string{"stop", "-l", "-t", "0"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"stop", "-l", "-t", "0"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
- cmd = rootlessTest.PodmanAsUser([]string{"start", "-l"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"start", "-l"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
if len(args) == 0 {
- cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"inspect", "-l"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
data := cmd.InspectContainerToJSON()
@@ -244,24 +244,23 @@ var _ = Describe("Podman rootless", func() {
Skip("ioctl(NS_GET_PARENT) not supported.")
}
- cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "echo", "hello"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "echo", "hello"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
Expect(cmd.LineInOutputContains("hello")).To(BeTrue())
- cmd = rootlessTest.PodmanAsUser([]string{"ps", "-l", "-q"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"ps", "-l", "-q"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
cid := cmd.OutputToString()
- cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "sh", "-c", "echo SeCreTMessage > /file"}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "sh", "-c", "echo SeCreTMessage > /file"}, 1000, 1000, "", env)
cmd.WaitWithDefaultTimeout()
Expect(cmd.ExitCode()).To(Equal(0))
- path := filepath.Join(home, "export.tar")
- cmd = rootlessTest.PodmanAsUser([]string{"export", "-o", path, cid}, 1000, 1000, env)
+ cmd = rootlessTest.PodmanAsUser([]string{"export", "-o", "export.tar", cid}, 1000, 1000, home, env)
cmd.WaitWithDefaultTimeout()
- content, err := ioutil.ReadFile(path)
+ content, err := ioutil.ReadFile(filepath.Join(home, "export.tar"))
Expect(err).To(BeNil())
Expect(strings.Contains(string(content), "SeCreTMessage")).To(BeTrue())
}
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 1c09a4d0b..a07e4d047 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -36,19 +36,19 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network connection with default bridge", func() {
- session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "wget", "www.projectatomic.io"})
+ session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expect(session.ExitCode()).To(Equal(0))
})
It("podman run network connection with host", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.projectatomic.io"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expect(session.ExitCode()).To(Equal(0))
})
It("podman run network connection with loopback", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.projectatomic.io"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expect(session.ExitCode()).To(Equal(0))
})
@@ -178,4 +178,37 @@ var _ = Describe("Podman run networking", func() {
Expect(exec4.ExitCode()).To(Equal(0))
Expect(exec4.OutputToString()).To(ContainSubstring("192.0.2.2 test1"))
})
+
+ It("podman run network in user created network namespace", func() {
+ if Containerized() {
+ Skip("Can not be run within a container.")
+ }
+ SystemExec("ip", []string{"netns", "add", "xxx"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxx", ALPINE, "wget", "www.podman.io"})
+ session.Wait(90)
+ Expect(session.ExitCode()).To(Equal(0))
+ SystemExec("ip", []string{"netns", "delete", "xxx"})
+ })
+
+ It("podman run n user created network namespace with resolv.conf", func() {
+ if Containerized() {
+ Skip("Can not be run within a container.")
+ }
+ SystemExec("ip", []string{"netns", "add", "xxx"})
+ SystemExec("mkdir", []string{"-p", "/etc/netns/xxx"})
+ SystemExec("bash", []string{"-c", "echo nameserver 11.11.11.11 > /etc/netns/xxx/resolv.conf"})
+ session := podmanTest.Podman([]string{"run", "--net", "ns:/run/netns/xxx", ALPINE, "cat", "/etc/resolv.conf"})
+ session.Wait(90)
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("11.11.11.11"))
+ SystemExec("ip", []string{"netns", "delete", "xxx"})
+ SystemExec("rm", []string{"-rf", "/etc/netns/xxx"})
+ })
+
+ It("podman run network in bogus user created network namespace", func() {
+ session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxy", ALPINE, "wget", "www.podman.io"})
+ session.Wait(90)
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("stat /run/netns/xxy: no such file or directory"))
+ })
})
diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go
index b354492b8..9f64e49a7 100644
--- a/test/e2e/save_test.go
+++ b/test/e2e/save_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/utils/podmantest_test.go b/test/utils/podmantest_test.go
index 60e3e2a97..28f294a94 100644
--- a/test/utils/podmantest_test.go
+++ b/test/utils/podmantest_test.go
@@ -23,7 +23,7 @@ var _ = Describe("PodmanTest test", func() {
FakeOutputs["check"] = []string{"check"}
os.Setenv("HOOK_OPTION", "hook_option")
env := os.Environ()
- session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, env)
+ session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env)
os.Unsetenv("HOOK_OPTION")
session.WaitWithDefaultTimeout()
Expect(session.Command.Process).ShouldNot(BeNil())
diff --git a/test/utils/utils.go b/test/utils/utils.go
index aace018cd..098779321 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -61,7 +61,7 @@ func (p *PodmanTest) MakeOptions(args []string) []string {
// PodmanAsUserBase exec podman as user. uid and gid is set for credentials useage. env is used
// to record the env for debugging
-func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, env []string) *PodmanSession {
+func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string) *PodmanSession {
var command *exec.Cmd
podmanOptions := p.MakeOptions(args)
podmanBinary := p.PodmanBinary
@@ -74,14 +74,18 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, env []stri
fmt.Printf("Running: (env: %v) %s %s\n", env, podmanBinary, strings.Join(podmanOptions, " "))
}
if uid != 0 || gid != 0 {
- nsEnterOpts := append([]string{"--userspec", fmt.Sprintf("%d:%d", uid, gid), "/", podmanBinary}, podmanOptions...)
- command = exec.Command("chroot", nsEnterOpts...)
+ pythonCmd := fmt.Sprintf("import os; import sys; uid = %d; gid = %d; cwd = '%s'; os.setgid(gid); os.setuid(uid); os.chdir(cwd) if len(cwd)>0 else True; os.execv(sys.argv[1], sys.argv[1:])", gid, uid, cwd)
+ nsEnterOpts := append([]string{"-c", pythonCmd, podmanBinary}, podmanOptions...)
+ command = exec.Command("python", nsEnterOpts...)
} else {
command = exec.Command(podmanBinary, podmanOptions...)
}
if env != nil {
command.Env = env
}
+ if cwd != "" {
+ command.Dir = cwd
+ }
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
if err != nil {
@@ -92,7 +96,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, env []stri
// PodmanBase exec podman with default env.
func (p *PodmanTest) PodmanBase(args []string) *PodmanSession {
- return p.PodmanAsUserBase(args, 0, 0, nil)
+ return p.PodmanAsUserBase(args, 0, 0, "", nil)
}
// WaitForContainer waits on a started container
diff --git a/utils/utils.go b/utils/utils.go
index 4a91b304f..33b0eb1c5 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -4,12 +4,15 @@ import (
"bytes"
"fmt"
"io"
+ "os"
"os/exec"
"strings"
+ "github.com/containers/storage/pkg/archive"
systemdDbus "github.com/coreos/go-systemd/dbus"
"github.com/godbus/dbus"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// ExecCmd executes a command with args and returns its output as a string along
@@ -139,3 +142,30 @@ func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, e
}
return written, err
}
+
+// UntarToFileSystem untars an os.file of a tarball to a destination in the filesystem
+func UntarToFileSystem(dest string, tarball *os.File, options *archive.TarOptions) error {
+ logrus.Debugf("untarring %s", tarball.Name())
+ return archive.Untar(tarball, dest, options)
+}
+
+// TarToFilesystem creates a tarball from source and writes to an os.file
+// provided
+func TarToFilesystem(source string, tarball *os.File) error {
+ tb, err := Tar(source)
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(tarball, tb)
+ if err != nil {
+ return err
+ }
+ logrus.Debugf("wrote tarball file %s", tarball.Name())
+ return nil
+}
+
+// Tar creates a tarball from source and returns a readcloser of it
+func Tar(source string) (io.ReadCloser, error) {
+ logrus.Debugf("creating tarball of %s", source)
+ return archive.Tar(source, archive.Uncompressed)
+}
diff --git a/vendor.conf b/vendor.conf
index 309189c05..445f0844a 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -15,8 +15,11 @@ github.com/containerd/cgroups 39b18af02c4120960f517a3a4c2588fabb61d02c
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
github.com/containernetworking/cni v0.7.0-alpha1
github.com/containernetworking/plugins v0.7.4
-github.com/containers/image 93bced01015eb94bec4821df1876314be8197680
-github.com/containers/storage 06b6c2e4cf254f5922a79da058c94ac2a65bb92f
+github.com/containers/image v1.4
+github.com/vbauerster/mpb v3.3.4
+github.com/mattn/go-isatty v0.0.4
+github.com/VividCortex/ewma v1.1.1
+github.com/containers/storage v1.10
github.com/containers/psgo v1.1
github.com/coreos/go-systemd v14
github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c
@@ -46,7 +49,6 @@ github.com/imdario/mergo v0.3.6
github.com/json-iterator/go 1.1.5
github.com/modern-go/concurrent 1.0.3
github.com/modern-go/reflect2 v1.0.1
-github.com/mattn/go-runewidth v0.0.4
github.com/mistifyio/go-zfs v2.1.1
github.com/mtrmac/gpgme b2432428689ca58c2b8e8dea9449d3295cf96fc9
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
@@ -83,7 +85,6 @@ golang.org/x/sys 7fbe1cd0fcc20051e1fcb87fbabec4a1bacaaeba https://github.com/gol
golang.org/x/text e6919f6577db79269a6443b9dc46d18f2238fb5d https://github.com/golang/text
golang.org/x/time 85acf8d2951cb2a3bde7632f9ff273ef0379bcbd https://github.com/golang/time
golang.org/x/sync 37e7f081c4d4c64e13b10787722085407fe5d15f https://github.com/golang/sync
-gopkg.in/cheggaaa/pb.v1 v1.0.27
gopkg.in/inf.v0 v0.9.1
gopkg.in/mgo.v2 v2
gopkg.in/yaml.v2 v2.2.2
@@ -92,13 +93,13 @@ k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apim
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
github.com/varlink/go 3ac79db6fd6aec70924193b090962f92985fe199
-github.com/containers/buildah 973bb88ef1861a5b782c722c471dd79b6732852c
+github.com/containers/buildah v1.7
# TODO: Gotty has not been updated since 2012. Can we find replacement?
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
# do not go beyond the below commit as the next one requires a more recent
# docker which is in conflict with openshift/imagebuilder
github.com/fsouza/go-dockerclient 29c1814d12c072344bb91aac5d2ff719db39c523
-github.com/openshift/imagebuilder 474d0f9df2cbabf006bd2b1c263a7b0789e228e0
+github.com/openshift/imagebuilder 36823496a6868f72bc36282cc475eb8a070c0934
github.com/ulikunitz/xz v0.5.5
github.com/coreos/go-iptables v0.4.0
github.com/google/shlex c34317bd91bf98fab745d77b03933cf8769299fe
diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/VividCortex/ewma/LICENSE
index 91b5cef30..a78d643ed 100644
--- a/vendor/github.com/mattn/go-runewidth/LICENSE
+++ b/vendor/github.com/VividCortex/ewma/LICENSE
@@ -1,6 +1,6 @@
-The MIT License (MIT)
+The MIT License
-Copyright (c) 2016 Yasuhiro Matsumoto
+Copyright (c) 2013 VividCortex
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/VividCortex/ewma/README.md b/vendor/github.com/VividCortex/ewma/README.md
new file mode 100644
index 000000000..7aab61b87
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/README.md
@@ -0,0 +1,140 @@
+# EWMA [![GoDoc](https://godoc.org/github.com/VividCortex/ewma?status.svg)](https://godoc.org/github.com/VividCortex/ewma) ![Build Status](https://circleci.com/gh/VividCortex/moving_average.png?circle-token=1459fa37f9ca0e50cef05d1963146d96d47ea523)
+
+This repo provides Exponentially Weighted Moving Average algorithms, or EWMAs for short, [based on our
+Quantifying Abnormal Behavior talk](https://vividcortex.com/blog/2013/07/23/a-fast-go-library-for-exponential-moving-averages/).
+
+### Exponentially Weighted Moving Average
+
+An exponentially weighted moving average is a way to continuously compute a type of
+average for a series of numbers, as the numbers arrive. After a value in the series is
+added to the average, its weight in the average decreases exponentially over time. This
+biases the average towards more recent data. EWMAs are useful for several reasons, chiefly
+their inexpensive computational and memory cost, as well as the fact that they represent
+the recent central tendency of the series of values.
+
+The EWMA algorithm requires a decay factor, alpha. The larger the alpha, the more the average
+is biased towards recent history. The alpha must be between 0 and 1, and is typically
+a fairly small number, such as 0.04. We will discuss the choice of alpha later.
+
+The algorithm works thus, in pseudocode:
+
+1. Multiply the next number in the series by alpha.
+2. Multiply the current value of the average by 1 minus alpha.
+3. Add the result of steps 1 and 2, and store it as the new current value of the average.
+4. Repeat for each number in the series.
+
+There are special-case behaviors for how to initialize the current value, and these vary
+between implementations. One approach is to start with the first value in the series;
+another is to average the first 10 or so values in the series using an arithmetic average,
+and then begin the incremental updating of the average. Each method has pros and cons.
+
+It may help to look at it pictorially. Suppose the series has five numbers, and we choose
+alpha to be 0.50 for simplicity. Here's the series, with numbers in the neighborhood of 300.
+
+![Data Series](https://user-images.githubusercontent.com/279875/28242350-463289a2-6977-11e7-88ca-fd778ccef1f0.png)
+
+Now let's take the moving average of those numbers. First we set the average to the value
+of the first number.
+
+![EWMA Step 1](https://user-images.githubusercontent.com/279875/28242353-464c96bc-6977-11e7-9981-dc4e0789c7ba.png)
+
+Next we multiply the next number by alpha, multiply the current value by 1-alpha, and add
+them to generate a new value.
+
+![EWMA Step 2](https://user-images.githubusercontent.com/279875/28242351-464abefa-6977-11e7-95d0-43900f29bef2.png)
+
+This continues until we are done.
+
+![EWMA Step N](https://user-images.githubusercontent.com/279875/28242352-464c58f0-6977-11e7-8cd0-e01e4efaac7f.png)
+
+Notice how each of the values in the series decays by half each time a new value
+is added, and the top of the bars in the lower portion of the image represents the
+size of the moving average. It is a smoothed, or low-pass, average of the original
+series.
+
+For further reading, see [Exponentially weighted moving average](http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) on wikipedia.
+
+### Choosing Alpha
+
+Consider a fixed-size sliding-window moving average (not an exponentially weighted moving average)
+that averages over the previous N samples. What is the average age of each sample? It is N/2.
+
+Now suppose that you wish to construct a EWMA whose samples have the same average age. The formula
+to compute the alpha required for this is: alpha = 2/(N+1). Proof is in the book
+"Production and Operations Analysis" by Steven Nahmias.
+
+So, for example, if you have a time-series with samples once per second, and you want to get the
+moving average over the previous minute, you should use an alpha of .032786885. This, by the way,
+is the constant alpha used for this repository's SimpleEWMA.
+
+### Implementations
+
+This repository contains two implementations of the EWMA algorithm, with different properties.
+
+The implementations all conform to the MovingAverage interface, and the constructor returns
+that type.
+
+Current implementations assume an implicit time interval of 1.0 between every sample added.
+That is, the passage of time is treated as though it's the same as the arrival of samples.
+If you need time-based decay when samples are not arriving precisely at set intervals, then
+this package will not support your needs at present.
+
+#### SimpleEWMA
+
+A SimpleEWMA is designed for low CPU and memory consumption. It **will** have different behavior than the VariableEWMA
+for multiple reasons. It has no warm-up period and it uses a constant
+decay. These properties let it use less memory. It will also behave
+differently when it's equal to zero, which is assumed to mean
+uninitialized, so if a value is likely to actually become zero over time,
+then any non-zero value will cause a sharp jump instead of a small change.
+
+#### VariableEWMA
+
+Unlike SimpleEWMA, this supports a custom age which must be stored, and thus uses more memory.
+It also has a "warmup" time when you start adding values to it. It will report a value of 0.0
+until you have added the required number of samples to it. It uses some memory to store the
+number of samples added to it. As a result it uses a little over twice the memory of SimpleEWMA.
+
+## Usage
+
+### API Documentation
+
+View the GoDoc generated documentation [here](http://godoc.org/github.com/VividCortex/ewma).
+
+```go
+package main
+import "github.com/VividCortex/ewma"
+
+func main() {
+ samples := [100]float64{
+ 4599, 5711, 4746, 4621, 5037, 4218, 4925, 4281, 5207, 5203, 5594, 5149,
+ }
+
+ e := ewma.NewMovingAverage() //=> Returns a SimpleEWMA if called without params
+ a := ewma.NewMovingAverage(5) //=> returns a VariableEWMA with a decay of 2 / (5 + 1)
+
+ for _, f := range samples {
+ e.Add(f)
+ a.Add(f)
+ }
+
+ e.Value() //=> 13.577404704631077
+ a.Value() //=> 1.5806140565521463e-12
+}
+```
+
+## Contributing
+
+We only accept pull requests for minor fixes or improvements. This includes:
+
+* Small bug fixes
+* Typos
+* Documentation or comments
+
+Please open issues to discuss new features. Pull requests for new features will be rejected,
+so we recommend forking the repository and making changes in your fork for your use case.
+
+## License
+
+This repository is Copyright (c) 2013 VividCortex, Inc. All rights reserved.
+It is licensed under the MIT license. Please see the LICENSE file for applicable license terms.
diff --git a/vendor/github.com/VividCortex/ewma/ewma.go b/vendor/github.com/VividCortex/ewma/ewma.go
new file mode 100644
index 000000000..44d5d53e3
--- /dev/null
+++ b/vendor/github.com/VividCortex/ewma/ewma.go
@@ -0,0 +1,126 @@
+// Package ewma implements exponentially weighted moving averages.
+package ewma
+
+// Copyright (c) 2013 VividCortex, Inc. All rights reserved.
+// Please see the LICENSE file for applicable license terms.
+
+const (
+ // By default, we average over a one-minute period, which means the average
+ // age of the metrics in the period is 30 seconds.
+ AVG_METRIC_AGE float64 = 30.0
+
+ // The formula for computing the decay factor from the average age comes
+ // from "Production and Operations Analysis" by Steven Nahmias.
+ DECAY float64 = 2 / (float64(AVG_METRIC_AGE) + 1)
+
+ // For best results, the moving average should not be initialized to the
+ // samples it sees immediately. The book "Production and Operations
+ // Analysis" by Steven Nahmias suggests initializing the moving average to
+ // the mean of the first 10 samples. Until the VariableEwma has seen this
+ // many samples, it is not "ready" to be queried for the value of the
+ // moving average. This adds some memory cost.
+ WARMUP_SAMPLES uint8 = 10
+)
+
+// MovingAverage is the interface that computes a moving average over a time-
+// series stream of numbers. The average may be over a window or exponentially
+// decaying.
+type MovingAverage interface {
+ Add(float64)
+ Value() float64
+ Set(float64)
+}
+
+// NewMovingAverage constructs a MovingAverage that computes an average with the
+// desired characteristics in the moving window or exponential decay. If no
+// age is given, it constructs a default exponentially weighted implementation
+// that consumes minimal memory. The age is related to the decay factor alpha
+// by the formula given for the DECAY constant. It signifies the average age
+// of the samples as time goes to infinity.
+func NewMovingAverage(age ...float64) MovingAverage {
+ if len(age) == 0 || age[0] == AVG_METRIC_AGE {
+ return new(SimpleEWMA)
+ }
+ return &VariableEWMA{
+ decay: 2 / (age[0] + 1),
+ }
+}
+
+// A SimpleEWMA represents the exponentially weighted moving average of a
+// series of numbers. It WILL have different behavior than the VariableEWMA
+// for multiple reasons. It has no warm-up period and it uses a constant
+// decay. These properties let it use less memory. It will also behave
+// differently when it's equal to zero, which is assumed to mean
+// uninitialized, so if a value is likely to actually become zero over time,
+// then any non-zero value will cause a sharp jump instead of a small change.
+// However, note that this takes a long time, and the value may just
+// decays to a stable value that's close to zero, but which won't be mistaken
+// for uninitialized. See http://play.golang.org/p/litxBDr_RC for example.
+type SimpleEWMA struct {
+ // The current value of the average. After adding with Add(), this is
+ // updated to reflect the average of all values seen thus far.
+ value float64
+}
+
+// Add adds a value to the series and updates the moving average.
+func (e *SimpleEWMA) Add(value float64) {
+ if e.value == 0 { // this is a proxy for "uninitialized"
+ e.value = value
+ } else {
+ e.value = (value * DECAY) + (e.value * (1 - DECAY))
+ }
+}
+
+// Value returns the current value of the moving average.
+func (e *SimpleEWMA) Value() float64 {
+ return e.value
+}
+
+// Set sets the EWMA's value.
+func (e *SimpleEWMA) Set(value float64) {
+ e.value = value
+}
+
+// VariableEWMA represents the exponentially weighted moving average of a series of
+// numbers. Unlike SimpleEWMA, it supports a custom age, and thus uses more memory.
+type VariableEWMA struct {
+ // The multiplier factor by which the previous samples decay.
+ decay float64
+ // The current value of the average.
+ value float64
+ // The number of samples added to this instance.
+ count uint8
+}
+
+// Add adds a value to the series and updates the moving average.
+func (e *VariableEWMA) Add(value float64) {
+ switch {
+ case e.count < WARMUP_SAMPLES:
+ e.count++
+ e.value += value
+ case e.count == WARMUP_SAMPLES:
+ e.count++
+ e.value = e.value / float64(WARMUP_SAMPLES)
+ e.value = (value * e.decay) + (e.value * (1 - e.decay))
+ default:
+ e.value = (value * e.decay) + (e.value * (1 - e.decay))
+ }
+}
+
+// Value returns the current value of the average, or 0.0 if the series hasn't
+// warmed up yet.
+func (e *VariableEWMA) Value() float64 {
+ if e.count <= WARMUP_SAMPLES {
+ return 0.0
+ }
+
+ return e.value
+}
+
+// Set sets the EWMA's value.
+func (e *VariableEWMA) Set(value float64) {
+ e.value = value
+ if e.count <= WARMUP_SAMPLES {
+ e.count = WARMUP_SAMPLES + 1
+ }
+}
diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go
index 8b9baea12..755bc348e 100644
--- a/vendor/github.com/containers/buildah/buildah.go
+++ b/vendor/github.com/containers/buildah/buildah.go
@@ -26,7 +26,7 @@ const (
Package = "buildah"
// Version for the Package. Bump version in contrib/rpm/buildah.spec
// too.
- Version = "1.7-dev"
+ Version = "1.7"
// The value we use to identify what type of information, currently a
// serialized Builder structure, we are using as per-container state.
// This should only be changed when we make incompatible changes to
@@ -336,11 +336,6 @@ type BuilderOptions struct {
// needs to be pulled and the image name alone can not be resolved to a
// reference to a source image. No separator is implicitly added.
Registry string
- // Transport is a value which is prepended to the image's name, if it
- // needs to be pulled and the image name alone, or the image name and
- // the registry together, can not be resolved to a reference to a
- // source image. No separator is implicitly added.
- Transport string
// PullBlobDirectory is the name of a directory in which we'll attempt
// to store copies of layer blobs that we pull down, if any. It should
// already exist.
diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go
index 56ab7aa57..d69eab52f 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/build.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/build.go
@@ -62,11 +62,6 @@ type BuildOptions struct {
// needs to be pulled and the image name alone can not be resolved to a
// reference to a source image. No separator is implicitly added.
Registry string
- // Transport is a value which is prepended to the image's name, if it
- // needs to be pulled and the image name alone, or the image name and
- // the registry together, can not be resolved to a reference to a
- // source image. No separator is implicitly added.
- Transport string
// IgnoreUnrecognizedInstructions tells us to just log instructions we
// don't recognize, and try to keep going.
IgnoreUnrecognizedInstructions bool
@@ -171,6 +166,8 @@ type BuildOptions struct {
ForceRmIntermediateCtrs bool
// BlobDirectory is a directory which we'll use for caching layer blobs.
BlobDirectory string
+ // Target the targeted FROM in the Dockerfile to build
+ Target string
}
// Executor is a buildah-based implementation of the imagebuilder.Executor
@@ -184,7 +181,6 @@ type Executor struct {
builder *buildah.Builder
pullPolicy buildah.PullPolicy
registry string
- transport string
ignoreUnrecognizedInstructions bool
quiet bool
runtime string
@@ -580,7 +576,6 @@ func NewExecutor(store storage.Store, options BuildOptions) (*Executor, error) {
contextDir: options.ContextDirectory,
pullPolicy: options.PullPolicy,
registry: options.Registry,
- transport: options.Transport,
ignoreUnrecognizedInstructions: options.IgnoreUnrecognizedInstructions,
quiet: options.Quiet,
runtime: options.Runtime,
@@ -670,7 +665,6 @@ func (b *Executor) Prepare(ctx context.Context, stage imagebuilder.Stage, from s
FromImage: from,
PullPolicy: b.pullPolicy,
Registry: b.registry,
- Transport: b.transport,
PullBlobDirectory: b.blobDirectory,
SignaturePolicyPath: b.signaturePolicyPath,
ReportWriter: b.reportWriter,
@@ -783,7 +777,7 @@ func (b *Executor) resolveNameToImageRef() (types.ImageReference, error) {
if b.output != "" {
imageRef, err = alltransports.ParseImageName(b.output)
if err != nil {
- candidates, _, err := util.ResolveName(b.output, "", b.systemContext, b.store)
+ candidates, _, _, err := util.ResolveName(b.output, "", b.systemContext, b.store)
if err != nil {
return nil, errors.Wrapf(err, "error parsing target image name %q", b.output)
}
@@ -1441,6 +1435,13 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
if err != nil {
return "", nil, errors.Wrap(err, "error reading multiple stages")
}
+ if options.Target != "" {
+ stagesTargeted, ok := stages.ThroughTarget(options.Target)
+ if !ok {
+ return "", nil, errors.Errorf("The target %q was not found in the provided Dockerfile", options.Target)
+ }
+ stages = stagesTargeted
+ }
return exec.Build(ctx, stages)
}
diff --git a/vendor/github.com/containers/buildah/new.go b/vendor/github.com/containers/buildah/new.go
index 7e7f97e49..01c2e733f 100644
--- a/vendor/github.com/containers/buildah/new.go
+++ b/vendor/github.com/containers/buildah/new.go
@@ -28,15 +28,14 @@ const (
minimumTruncatedIDLength = 3
)
-func pullAndFindImage(ctx context.Context, store storage.Store, imageName string, options BuilderOptions, sc *types.SystemContext) (*storage.Image, types.ImageReference, error) {
+func pullAndFindImage(ctx context.Context, store storage.Store, transport string, imageName string, options BuilderOptions, sc *types.SystemContext) (*storage.Image, types.ImageReference, error) {
pullOptions := PullOptions{
ReportWriter: options.ReportWriter,
Store: store,
SystemContext: options.SystemContext,
- Transport: options.Transport,
BlobDirectory: options.PullBlobDirectory,
}
- ref, err := pullImage(ctx, store, imageName, pullOptions, sc)
+ ref, err := pullImage(ctx, store, transport, imageName, pullOptions, sc)
if err != nil {
logrus.Debugf("error pulling image %q: %v", imageName, err)
return nil, nil, err
@@ -101,16 +100,16 @@ func newContainerIDMappingOptions(idmapOptions *IDMappingOptions) storage.IDMapp
return options
}
-func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, *storage.Image, error) {
+func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) {
type failure struct {
resolvedImageName string
err error
}
-
- candidates, searchRegistriesWereUsedButEmpty, err := util.ResolveName(options.FromImage, options.Registry, systemContext, store)
+ candidates, transport, searchRegistriesWereUsedButEmpty, err := util.ResolveName(options.FromImage, options.Registry, systemContext, store)
if err != nil {
- return nil, nil, errors.Wrapf(err, "error parsing reference to image %q", options.FromImage)
+ return nil, "", nil, errors.Wrapf(err, "error parsing reference to image %q", options.FromImage)
}
+
failures := []failure{}
for _, image := range candidates {
var err error
@@ -118,25 +117,25 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store
if img, err := store.Image(image); err == nil && img != nil && strings.HasPrefix(img.ID, image) {
ref, err := is.Transport.ParseStoreReference(store, img.ID)
if err != nil {
- return nil, nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID)
+ return nil, "", nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID)
}
- return ref, img, nil
+ return ref, transport, img, nil
}
}
if options.PullPolicy == PullAlways {
- pulledImg, pulledReference, err := pullAndFindImage(ctx, store, image, options, systemContext)
+ pulledImg, pulledReference, err := pullAndFindImage(ctx, store, transport, image, options, systemContext)
if err != nil {
logrus.Debugf("unable to pull and read image %q: %v", image, err)
failures = append(failures, failure{resolvedImageName: image, err: err})
continue
}
- return pulledReference, pulledImg, nil
+ return pulledReference, transport, pulledImg, nil
}
srcRef, err := alltransports.ParseImageName(image)
if err != nil {
- if options.Transport == "" {
+ if transport == "" {
logrus.Debugf("error parsing image name %q: %v", image, err)
failures = append(failures, failure{
resolvedImageName: image,
@@ -144,12 +143,13 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store
})
continue
}
- logrus.Debugf("error parsing image name %q as given, trying with transport %q: %v", image, options.Transport, err)
- transport := options.Transport
+ logrus.Debugf("error parsing image name %q as given, trying with transport %q: %v", image, transport, err)
+
+ trans := transport
if transport != util.DefaultTransport {
- transport = transport + ":"
+ trans = trans + ":"
}
- srcRef2, err := alltransports.ParseImageName(transport + image)
+ srcRef2, err := alltransports.ParseImageName(trans + image)
if err != nil {
logrus.Debugf("error parsing image name %q: %v", transport+image, err)
failures = append(failures, failure{
@@ -163,19 +163,19 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store
destImage, err := localImageNameForReference(ctx, store, srcRef, options.FromImage)
if err != nil {
- return nil, nil, errors.Wrapf(err, "error computing local image name for %q", transports.ImageName(srcRef))
+ return nil, "", nil, errors.Wrapf(err, "error computing local image name for %q", transports.ImageName(srcRef))
}
if destImage == "" {
- return nil, nil, errors.Errorf("error computing local image name for %q", transports.ImageName(srcRef))
+ return nil, "", nil, errors.Errorf("error computing local image name for %q", transports.ImageName(srcRef))
}
ref, err := is.Transport.ParseStoreReference(store, destImage)
if err != nil {
- return nil, nil, errors.Wrapf(err, "error parsing reference to image %q", destImage)
+ return nil, "", nil, errors.Wrapf(err, "error parsing reference to image %q", destImage)
}
img, err := is.Transport.GetStoreImage(store, ref)
if err == nil {
- return ref, img, nil
+ return ref, transport, img, nil
}
if errors.Cause(err) == storage.ErrImageUnknown && options.PullPolicy != PullIfMissing {
@@ -187,26 +187,26 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store
continue
}
- pulledImg, pulledReference, err := pullAndFindImage(ctx, store, image, options, systemContext)
+ pulledImg, pulledReference, err := pullAndFindImage(ctx, store, transport, image, options, systemContext)
if err != nil {
logrus.Debugf("unable to pull and read image %q: %v", image, err)
failures = append(failures, failure{resolvedImageName: image, err: err})
continue
}
- return pulledReference, pulledImg, nil
+ return pulledReference, transport, pulledImg, nil
}
if len(failures) != len(candidates) {
- return nil, nil, fmt.Errorf("internal error: %d candidates (%#v) vs. %d failures (%#v)", len(candidates), candidates, len(failures), failures)
+ return nil, "", nil, fmt.Errorf("internal error: %d candidates (%#v) vs. %d failures (%#v)", len(candidates), candidates, len(failures), failures)
}
registriesConfPath := sysregistries.RegistriesConfPath(systemContext)
switch len(failures) {
case 0:
if searchRegistriesWereUsedButEmpty {
- return nil, nil, errors.Errorf("image name %q is a short name and no search registries are defined in %s.", options.FromImage, registriesConfPath)
+ return nil, "", nil, errors.Errorf("image name %q is a short name and no search registries are defined in %s.", options.FromImage, registriesConfPath)
}
- return nil, nil, fmt.Errorf("internal error: no pull candidates were available for %q for an unknown reason", options.FromImage)
+ return nil, "", nil, fmt.Errorf("internal error: no pull candidates were available for %q for an unknown reason", options.FromImage)
case 1:
err := failures[0].err
@@ -216,7 +216,7 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store
if searchRegistriesWereUsedButEmpty {
err = errors.Wrapf(err, "(image name %q is a short name and no search registries are defined in %s)", options.FromImage, registriesConfPath)
}
- return nil, nil, err
+ return nil, "", nil, err
default:
// NOTE: a multi-line error string:
@@ -224,7 +224,7 @@ func resolveImage(ctx context.Context, systemContext *types.SystemContext, store
for _, f := range failures {
e = e + fmt.Sprintf("\n* %q: %s", f.resolvedImageName, f.err.Error())
}
- return nil, nil, errors.New(e)
+ return nil, "", nil, errors.New(e)
}
}
@@ -250,21 +250,19 @@ func findUnusedContainer(name string, containers []storage.Container) string {
}
func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) {
- var ref types.ImageReference
- var img *storage.Image
- var err error
-
+ var (
+ ref types.ImageReference
+ img *storage.Image
+ err error
+ )
if options.FromImage == BaseImageFakeName {
options.FromImage = ""
}
- if options.Transport == "" {
- options.Transport = util.DefaultTransport
- }
systemContext := getSystemContext(options.SystemContext, options.SignaturePolicyPath)
if options.FromImage != "" && options.FromImage != "scratch" {
- ref, img, err = resolveImage(ctx, systemContext, store, options)
+ ref, _, img, err = resolveImage(ctx, systemContext, store, options)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go
index bbbbf3476..09f951b35 100644
--- a/vendor/github.com/containers/buildah/pkg/cli/common.go
+++ b/vendor/github.com/containers/buildah/pkg/cli/common.go
@@ -55,20 +55,21 @@ type BudResults struct {
File []string
Format string
Iidfile string
- NoCache bool
Label []string
Logfile string
Loglevel int
+ NoCache bool
Platform string
Pull bool
PullAlways bool
Quiet bool
Rm bool
Runtime string
- RuntimeOpts []string
+ RuntimeFlags []string
SignaturePolicy string
Squash bool
Tag []string
+ Target string
TlsVerify bool
}
@@ -138,7 +139,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
fs.BoolVar(&flags.Compress, "compress", false, "This is legacy option, which has no effect on the image")
fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
- fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", false, "don't compress layers by default")
+ fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default")
fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "This is a Docker specific option and is a NOOP")
fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile")
fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.")
@@ -153,10 +154,11 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.BoolVarP(&flags.Quiet, "quiet", "q", false, "refrain from announcing build instructions and image read/write progress")
fs.BoolVar(&flags.Rm, "rm", true, "Remove intermediate containers after a successful build (default true)")
fs.StringVar(&flags.Runtime, "runtime", util.Runtime(), "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.")
- fs.StringSliceVar(&flags.RuntimeOpts, "runtime-flag", []string{}, "add global flags for the container runtime")
+ fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
fs.BoolVar(&flags.Squash, "squash", false, "Squash newly built layers into a single new layer. The build process does not currently support caching so this is a NOOP.")
fs.StringSliceVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
+ fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
fs.BoolVar(&flags.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
return fs
}
diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go
index ffc7c15bb..a26d15631 100644
--- a/vendor/github.com/containers/buildah/pkg/parse/parse.go
+++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go
@@ -251,9 +251,9 @@ func SystemContextFromOptions(c *cobra.Command) (*types.SystemContext, error) {
}
tlsVerify, err := c.Flags().GetBool("tls-verify")
if err == nil && c.Flag("tls-verify").Changed {
- ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(tlsVerify)
- ctx.OCIInsecureSkipTLSVerify = tlsVerify
- ctx.DockerDaemonInsecureSkipTLSVerify = tlsVerify
+ ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!tlsVerify)
+ ctx.OCIInsecureSkipTLSVerify = !tlsVerify
+ ctx.DockerDaemonInsecureSkipTLSVerify = !tlsVerify
}
creds, err := c.Flags().GetString("creds")
if err == nil && c.Flag("creds").Changed {
diff --git a/vendor/github.com/containers/buildah/pull.go b/vendor/github.com/containers/buildah/pull.go
index aede1784b..d1f33fb01 100644
--- a/vendor/github.com/containers/buildah/pull.go
+++ b/vendor/github.com/containers/buildah/pull.go
@@ -9,10 +9,13 @@ import (
"github.com/containers/buildah/pkg/blobcache"
"github.com/containers/buildah/util"
cp "github.com/containers/image/copy"
+ "github.com/containers/image/directory"
"github.com/containers/image/docker"
+ dockerarchive "github.com/containers/image/docker/archive"
"github.com/containers/image/docker/reference"
tarfile "github.com/containers/image/docker/tarfile"
ociarchive "github.com/containers/image/oci/archive"
+ oci "github.com/containers/image/oci/layout"
"github.com/containers/image/signature"
is "github.com/containers/image/storage"
"github.com/containers/image/transports"
@@ -40,10 +43,6 @@ type PullOptions struct {
// github.com/containers/image/types SystemContext to hold credentials
// and other authentication/authorization information.
SystemContext *types.SystemContext
- // Transport is a value which is prepended to the image's name, if the
- // image name alone can not be resolved to a reference to a source
- // image. No separator is implicitly added.
- Transport string
// BlobDirectory is the name of a directory in which we'll attempt to
// store copies of layer blobs that we pull down, if any. It should
// already exist.
@@ -51,10 +50,6 @@ type PullOptions struct {
// AllTags is a boolean value that determines if all tagged images
// will be downloaded from the repository. The default is false.
AllTags bool
- // Quiet is a boolean value that determines if minimal output to
- // the user will be displayed, this is best used for logging.
- // The default is false.
- Quiet bool
}
func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference, spec string) (string, error) {
@@ -65,7 +60,7 @@ func localImageNameForReference(ctx context.Context, store storage.Store, srcRef
file := split[len(split)-1]
var name string
switch srcRef.Transport().Name() {
- case util.DockerArchive:
+ case dockerarchive.Transport.Name():
tarSource, err := tarfile.NewSourceFromFile(file)
if err != nil {
return "", errors.Wrapf(err, "error opening tarfile %q as a source image", file)
@@ -92,7 +87,7 @@ func localImageNameForReference(ctx context.Context, store storage.Store, srcRef
}
}
}
- case util.OCIArchive:
+ case ociarchive.Transport.Name():
// retrieve the manifest from index.json to access the image name
manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
if err != nil {
@@ -107,7 +102,14 @@ func localImageNameForReference(ctx context.Context, store storage.Store, srcRef
} else {
name = manifest.Annotations["org.opencontainers.image.ref.name"]
}
- case util.DirTransport:
+ case directory.Transport.Name():
+ // supports pull from a directory
+ name = split[1]
+ // remove leading "/"
+ if name[:1] == "/" {
+ name = name[1:]
+ }
+ case oci.Transport.Name():
// supports pull from a directory
name = split[1]
// remove leading "/"
@@ -152,76 +154,71 @@ func localImageNameForReference(ctx context.Context, store storage.Store, srcRef
// Pull copies the contents of the image from somewhere else to local storage.
func Pull(ctx context.Context, imageName string, options PullOptions) error {
- spec := imageName
systemContext := getSystemContext(options.SystemContext, options.SignaturePolicyPath)
- srcRef, err := alltransports.ParseImageName(spec)
- if err != nil {
- if options.Transport == "" {
- options.Transport = util.DefaultTransport
- }
- logrus.Debugf("error parsing image name %q, trying with transport %q: %v", spec, options.Transport, err)
- transport := options.Transport
- if transport != util.DefaultTransport {
- transport = transport + ":"
- }
- spec = transport + spec
- srcRef2, err2 := alltransports.ParseImageName(spec)
- if err2 != nil {
- return errors.Wrapf(err2, "error parsing image name %q", imageName)
- }
- srcRef = srcRef2
+
+ boptions := BuilderOptions{
+ FromImage: imageName,
+ SignaturePolicyPath: options.SignaturePolicyPath,
+ SystemContext: systemContext,
+ PullBlobDirectory: options.BlobDirectory,
+ ReportWriter: options.ReportWriter,
}
- if options.Quiet {
- options.ReportWriter = nil // Turns off logging output
+
+ storageRef, transport, img, err := resolveImage(ctx, systemContext, options.Store, boptions)
+ if err != nil {
+ return err
}
- var names []string
+
+ var errs *multierror.Error
if options.AllTags {
- if srcRef.DockerReference() == nil {
- return errors.New("Non-docker transport is currently not supported")
+ if transport != util.DefaultTransport {
+ return errors.New("Non-docker transport is not supported, for --all-tags pulling")
+ }
+
+ spec := transport + storageRef.DockerReference().Name()
+ storageRef, err = alltransports.ParseImageName(spec)
+ if err != nil {
+ return errors.Wrapf(err, "error getting repository tags")
}
- tags, err := docker.GetRepositoryTags(ctx, systemContext, srcRef)
+ tags, err := docker.GetRepositoryTags(ctx, systemContext, storageRef)
if err != nil {
return errors.Wrapf(err, "error getting repository tags")
}
for _, tag := range tags {
name := spec + ":" + tag
- names = append(names, name)
+ if options.ReportWriter != nil {
+ options.ReportWriter.Write([]byte("Pulling " + name + "\n"))
+ }
+ ref, err := pullImage(ctx, options.Store, transport, name, options, systemContext)
+ if err != nil {
+ errs = multierror.Append(errs, err)
+ continue
+ }
+ img, err := is.Transport.GetStoreImage(options.Store, ref)
+ if err != nil {
+ errs = multierror.Append(errs, err)
+ continue
+ }
+ fmt.Printf("%s\n", img.ID)
}
} else {
- names = append(names, spec)
- }
- var errs *multierror.Error
- for _, name := range names {
- if options.ReportWriter != nil {
- options.ReportWriter.Write([]byte("Pulling " + name + "\n"))
- }
- ref, err := pullImage(ctx, options.Store, name, options, systemContext)
- if err != nil {
- errs = multierror.Append(errs, err)
- continue
- }
- img, err := is.Transport.GetStoreImage(options.Store, ref)
- if err != nil {
- errs = multierror.Append(errs, err)
- continue
- }
fmt.Printf("%s\n", img.ID)
}
return errs.ErrorOrNil()
}
-func pullImage(ctx context.Context, store storage.Store, imageName string, options PullOptions, sc *types.SystemContext) (types.ImageReference, error) {
+func pullImage(ctx context.Context, store storage.Store, transport string, imageName string, options PullOptions, sc *types.SystemContext) (types.ImageReference, error) {
spec := imageName
srcRef, err := alltransports.ParseImageName(spec)
if err != nil {
- if options.Transport == "" {
- options.Transport = util.DefaultTransport
- }
- logrus.Debugf("error parsing image name %q, trying with transport %q: %v", spec, options.Transport, err)
- transport := options.Transport
- if transport != util.DefaultTransport {
- transport = transport + ":"
+ logrus.Debugf("error parsing image name %q, trying with transport %q: %v", spec, transport, err)
+ if transport == "" {
+ transport = util.DefaultTransport
+ } else {
+ if transport != util.DefaultTransport {
+ transport = transport + ":"
+ }
}
spec = transport + spec
srcRef2, err2 := alltransports.ParseImageName(spec)
diff --git a/vendor/github.com/containers/buildah/unshare/unshare.c b/vendor/github.com/containers/buildah/unshare/unshare.c
index 3865e414f..8eefae41b 100644
--- a/vendor/github.com/containers/buildah/unshare/unshare.c
+++ b/vendor/github.com/containers/buildah/unshare/unshare.c
@@ -2,6 +2,8 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <linux/memfd.h>
#include <fcntl.h>
#include <grp.h>
#include <sched.h>
@@ -12,6 +14,28 @@
#include <errno.h>
#include <unistd.h>
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+#endif
+#ifndef F_SEAL_SEAL
+#define F_SEAL_SEAL 0x0001LU
+#endif
+#ifndef F_SEAL_SHRINK
+#define F_SEAL_SHRINK 0x0002LU
+#endif
+#ifndef F_SEAL_GROW
+#define F_SEAL_GROW 0x0004LU
+#endif
+#ifndef F_SEAL_WRITE
+#define F_SEAL_WRITE 0x0008LU
+#endif
+
+#define BUFSTEP 1024
+
static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces";
static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone";
@@ -59,6 +83,119 @@ static void _check_proc_sys_file(const char *path)
}
}
+static char **parse_proc_stringlist(const char *list) {
+ int fd, n, i, n_strings;
+ char *buf, *new_buf, **ret;
+ size_t size, new_size, used;
+
+ fd = open(list, O_RDONLY);
+ if (fd == -1) {
+ return NULL;
+ }
+ buf = NULL;
+ size = 0;
+ used = 0;
+ for (;;) {
+ new_size = used + BUFSTEP;
+ new_buf = realloc(buf, new_size);
+ if (new_buf == NULL) {
+ free(buf);
+ fprintf(stderr, "realloc(%ld): out of memory\n", (long)(size + BUFSTEP));
+ return NULL;
+ }
+ buf = new_buf;
+ size = new_size;
+ memset(buf + used, '\0', size - used);
+ n = read(fd, buf + used, size - used - 1);
+ if (n < 0) {
+ fprintf(stderr, "read(): %m\n");
+ return NULL;
+ }
+ if (n == 0) {
+ break;
+ }
+ used += n;
+ }
+ close(fd);
+ n_strings = 0;
+ for (n = 0; n < used; n++) {
+ if ((n == 0) || (buf[n-1] == '\0')) {
+ n_strings++;
+ }
+ }
+ ret = calloc(n_strings + 1, sizeof(char *));
+ if (ret == NULL) {
+ fprintf(stderr, "calloc(): out of memory\n");
+ return NULL;
+ }
+ i = 0;
+ for (n = 0; n < used; n++) {
+ if ((n == 0) || (buf[n-1] == '\0')) {
+ ret[i++] = &buf[n];
+ }
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static int buildah_reexec(void) {
+ char **argv, *exename;
+ int fd, mmfd, n_read, n_written;
+ struct stat st;
+ char buf[2048];
+
+ argv = parse_proc_stringlist("/proc/self/cmdline");
+ if (argv == NULL) {
+ return -1;
+ }
+ fd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ fprintf(stderr, "open(\"/proc/self/exe\"): %m\n");
+ return -1;
+ }
+ if (fstat(fd, &st) == -1) {
+ fprintf(stderr, "fstat(\"/proc/self/exe\"): %m\n");
+ return -1;
+ }
+ exename = basename(argv[0]);
+ mmfd = syscall(SYS_memfd_create, exename, (long) MFD_ALLOW_SEALING | MFD_CLOEXEC);
+ if (mmfd == -1) {
+ fprintf(stderr, "memfd_create(): %m\n");
+ return -1;
+ }
+ for (;;) {
+ n_read = read(fd, buf, sizeof(buf));
+ if (n_read < 0) {
+ fprintf(stderr, "read(\"/proc/self/exe\"): %m\n");
+ return -1;
+ }
+ if (n_read == 0) {
+ break;
+ }
+ n_written = write(mmfd, buf, n_read);
+ if (n_written < 0) {
+ fprintf(stderr, "write(anonfd): %m\n");
+ return -1;
+ }
+ if (n_written != n_read) {
+ fprintf(stderr, "write(anonfd): short write (%d != %d)\n", n_written, n_read);
+ return -1;
+ }
+ }
+ close(fd);
+ if (fcntl(mmfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == -1) {
+ close(mmfd);
+ fprintf(stderr, "Error sealing memfd copy: %m\n");
+ return -1;
+ }
+ if (fexecve(mmfd, argv, environ) == -1) {
+ close(mmfd);
+ fprintf(stderr, "Error during reexec(...): %m\n");
+ return -1;
+ }
+ return 0;
+}
+
void _buildah_unshare(void)
{
int flags, pidfd, continuefd, n, pgrp, sid, ctty;
@@ -132,5 +269,8 @@ void _buildah_unshare(void)
_exit(1);
}
}
+ if (buildah_reexec() != 0) {
+ _exit(1);
+ }
return;
}
diff --git a/vendor/github.com/containers/buildah/unshare/unshare.go b/vendor/github.com/containers/buildah/unshare/unshare.go
index 2a970b8d6..1072c2035 100644
--- a/vendor/github.com/containers/buildah/unshare/unshare.go
+++ b/vendor/github.com/containers/buildah/unshare/unshare.go
@@ -56,8 +56,10 @@ func (c *Cmd) Start() error {
c.Env = append(c.Env, fmt.Sprintf("_Buildah-unshare=%d", c.UnshareFlags))
// Please the libpod "rootless" package to find the expected env variables.
- c.Env = append(c.Env, "_LIBPOD_USERNS_CONFIGURED=done")
- c.Env = append(c.Env, fmt.Sprintf("_LIBPOD_ROOTLESS_UID=%d", os.Geteuid()))
+ if os.Geteuid() != 0 {
+ c.Env = append(c.Env, "_LIBPOD_USERNS_CONFIGURED=done")
+ c.Env = append(c.Env, fmt.Sprintf("_LIBPOD_ROOTLESS_UID=%d", os.Geteuid()))
+ }
// Create the pipe for reading the child's PID.
pidRead, pidWrite, err := os.Pipe()
diff --git a/vendor/github.com/containers/buildah/util/util.go b/vendor/github.com/containers/buildah/util/util.go
index e46f9b7cb..d98493634 100644
--- a/vendor/github.com/containers/buildah/util/util.go
+++ b/vendor/github.com/containers/buildah/util/util.go
@@ -11,14 +11,11 @@ import (
"strings"
"syscall"
- "github.com/containers/image/directory"
- dockerarchive "github.com/containers/image/docker/archive"
"github.com/containers/image/docker/reference"
- ociarchive "github.com/containers/image/oci/archive"
"github.com/containers/image/pkg/sysregistriesv2"
"github.com/containers/image/signature"
is "github.com/containers/image/storage"
- "github.com/containers/image/tarball"
+ "github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
@@ -43,36 +40,18 @@ var (
"index.docker.io": "library",
"docker.io": "library",
}
- // Transports contains the possible transports used for images
- Transports = map[string]string{
- dockerarchive.Transport.Name(): "",
- ociarchive.Transport.Name(): "",
- directory.Transport.Name(): "",
- tarball.Transport.Name(): "",
- }
- // DockerArchive is the transport we prepend to an image name
- // when saving to docker-archive
- DockerArchive = dockerarchive.Transport.Name()
- // OCIArchive is the transport we prepend to an image name
- // when saving to oci-archive
- OCIArchive = ociarchive.Transport.Name()
- // DirTransport is the transport for pushing and pulling
- // images to and from a directory
- DirTransport = directory.Transport.Name()
- // TarballTransport is the transport for importing a tar archive
- // and creating a filesystem image
- TarballTransport = tarball.Transport.Name()
)
// ResolveName checks if name is a valid image name, and if that name doesn't
// include a domain portion, returns a list of the names which it might
-// correspond to in the set of configured registries,
-// and a boolean which is true iff 1) the list of search registries was used, and 2) it was empty.
+// correspond to in the set of configured registries, the transport used to
+// pull the image, and a boolean which is true iff
+// 1) the list of search registries was used, and 2) it was empty.
// NOTE: The "list of search registries is empty" check does not count blocked registries,
// and neither the implied "localhost" nor a possible firstRegistry are counted
-func ResolveName(name string, firstRegistry string, sc *types.SystemContext, store storage.Store) ([]string, bool, error) {
+func ResolveName(name string, firstRegistry string, sc *types.SystemContext, store storage.Store) ([]string, string, bool, error) {
if name == "" {
- return nil, false, nil
+ return nil, "", false, nil
}
// Maybe it's a truncated image ID. Don't prepend a registry name, then.
@@ -80,27 +59,28 @@ func ResolveName(name string, firstRegistry string, sc *types.SystemContext, sto
if img, err := store.Image(name); err == nil && img != nil && strings.HasPrefix(img.ID, name) {
// It's a truncated version of the ID of an image that's present in local storage;
// we need only expand the ID.
- return []string{img.ID}, false, nil
+ return []string{img.ID}, "", false, nil
}
}
// If the image includes a transport's name as a prefix, use it as-is.
+ if strings.HasPrefix(name, DefaultTransport) {
+ return []string{strings.TrimPrefix(name, DefaultTransport)}, DefaultTransport, false, nil
+ }
split := strings.SplitN(name, ":", 2)
if len(split) == 2 {
- if _, ok := Transports[split[0]]; ok {
- return []string{split[1]}, false, nil
+ if trans := transports.Get(split[0]); trans != nil {
+ return []string{split[1]}, trans.Name(), false, nil
}
}
-
- name = strings.TrimPrefix(name, DefaultTransport)
// If the image name already included a domain component, we're done.
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
- return nil, false, errors.Wrapf(err, "error parsing image name %q", name)
+ return nil, "", false, errors.Wrapf(err, "error parsing image name %q", name)
}
if named.String() == name {
// Parsing produced the same result, so there was a domain name in there to begin with.
- return []string{name}, false, nil
+ return []string{name}, DefaultTransport, false, nil
}
if reference.Domain(named) != "" && RegistryDefaultPathPrefix[reference.Domain(named)] != "" {
// If this domain can cause us to insert something in the middle, check if that happened.
@@ -117,7 +97,7 @@ func ResolveName(name string, firstRegistry string, sc *types.SystemContext, sto
defaultPrefix := RegistryDefaultPathPrefix[reference.Domain(named)] + "/"
if strings.HasPrefix(repoPath, defaultPrefix) && path.Join(domain, repoPath[len(defaultPrefix):])+tag+digest == name {
// Yup, parsing just inserted a bit in the middle, so there was a domain name there to begin with.
- return []string{name}, false, nil
+ return []string{name}, DefaultTransport, false, nil
}
}
@@ -153,7 +133,7 @@ func ResolveName(name string, firstRegistry string, sc *types.SystemContext, sto
candidate := path.Join(registry, middle, name)
candidates = append(candidates, candidate)
}
- return candidates, searchRegistriesAreEmpty, nil
+ return candidates, DefaultTransport, searchRegistriesAreEmpty, nil
}
// ExpandNames takes unqualified names, parses them as image names, and returns
@@ -164,7 +144,7 @@ func ExpandNames(names []string, firstRegistry string, systemContext *types.Syst
expanded := make([]string, 0, len(names))
for _, n := range names {
var name reference.Named
- nameList, _, err := ResolveName(n, firstRegistry, systemContext, store)
+ nameList, _, _, err := ResolveName(n, firstRegistry, systemContext, store)
if err != nil {
return nil, errors.Wrapf(err, "error parsing name %q", n)
}
@@ -200,7 +180,7 @@ func FindImage(store storage.Store, firstRegistry string, systemContext *types.S
var ref types.ImageReference
var img *storage.Image
var err error
- names, _, err := ResolveName(image, firstRegistry, systemContext, store)
+ names, _, _, err := ResolveName(image, firstRegistry, systemContext, store)
if err != nil {
return nil, nil, errors.Wrapf(err, "error parsing name %q", image)
}
diff --git a/vendor/github.com/containers/buildah/vendor.conf b/vendor/github.com/containers/buildah/vendor.conf
index bda5f3965..7438fc909 100644
--- a/vendor/github.com/containers/buildah/vendor.conf
+++ b/vendor/github.com/containers/buildah/vendor.conf
@@ -3,10 +3,13 @@ github.com/blang/semver v3.5.0
github.com/BurntSushi/toml v0.2.0
github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
github.com/containernetworking/cni v0.7.0-alpha1
-github.com/containers/image v1.3
+github.com/containers/image v1.4
+github.com/vbauerster/mpb v3.3.4
+github.com/mattn/go-isatty v0.0.4
+github.com/VividCortex/ewma v1.1.1
github.com/boltdb/bolt v1.3.1
github.com/containers/libpod v1.0
-github.com/containers/storage v1.9
+github.com/containers/storage v1.10
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/docker-credential-helpers v0.6.1
@@ -21,7 +24,6 @@ github.com/gorilla/mux v1.6.2
github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/go-multierror v1.0.0
github.com/imdario/mergo v0.3.6
-github.com/mattn/go-runewidth v0.0.4
github.com/mattn/go-shellwords v1.0.3
github.com/Microsoft/go-winio v0.4.11
github.com/Microsoft/hcsshim v0.8.3
@@ -36,7 +38,7 @@ github.com/opencontainers/runc v1.0.0-rc6
github.com/opencontainers/runtime-spec v1.0.0
github.com/opencontainers/runtime-tools v0.8.0
github.com/opencontainers/selinux v1.1
-github.com/openshift/imagebuilder a4122153148e3b34161191f868565d8dffe65a69
+github.com/openshift/imagebuilder 36823496a6868f72bc36282cc475eb8a070c0934
github.com/ostreedev/ostree-go 9ab99253d365aac3a330d1f7281cf29f3d22820b
github.com/pkg/errors v0.8.1
github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac
@@ -55,7 +57,6 @@ golang.org/x/net 45ffb0cd1ba084b73e26dee67e667e1be5acce83 https://github.com/gol
golang.org/x/sync 37e7f081c4d4c64e13b10787722085407fe5d15f https://github.com/golang/sync
golang.org/x/sys 7fbe1cd0fcc20051e1fcb87fbabec4a1bacaaeba https://github.com/golang/sys
golang.org/x/text e6919f6577db79269a6443b9dc46d18f2238fb5d https://github.com/golang/text
-gopkg.in/cheggaaa/pb.v1 v1.0.27
gopkg.in/yaml.v2 v2.2.2
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
github.com/klauspost/pgzip v1.2.1
@@ -63,17 +64,4 @@ github.com/klauspost/compress v1.4.1
github.com/klauspost/cpuid v1.2.0
github.com/onsi/gomega v1.4.3
github.com/spf13/cobra v0.0.3
-github.com/cpuguy83/go-md2man v1.0.8
github.com/spf13/pflag v1.0.3
-github.com/inconshreveable/mousetrap v1.0.0
-github.com/russross/blackfriday v2.0.1
-github.com/mitchellh/go-homedir v1.0.0
-github.com/spf13/viper v1.3.1
-github.com/fsnotify/fsnotify v1.4.7
-github.com/hashicorp/hcl v1.0.0
-github.com/magiconair/properties v1.8.0
-github.com/mitchellh/mapstructure v1.1.2
-github.com/pelletier/go-toml v1.2.0
-github.com/spf13/afero v1.2.0
-github.com/spf13/cast v1.3.0
-github.com/spf13/jwalterweatherman v1.0.0
diff --git a/vendor/github.com/containers/image/copy/copy.go b/vendor/github.com/containers/image/copy/copy.go
index 2d3a2a1a8..ba99336aa 100644
--- a/vendor/github.com/containers/image/copy/copy.go
+++ b/vendor/github.com/containers/image/copy/copy.go
@@ -22,12 +22,13 @@ import (
"github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/klauspost/pgzip"
- "github.com/opencontainers/go-digest"
+ digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "github.com/vbauerster/mpb"
+ "github.com/vbauerster/mpb/decor"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/sync/semaphore"
- pb "gopkg.in/cheggaaa/pb.v1"
)
type digestingReader struct {
@@ -422,27 +423,6 @@ func (ic *imageCopier) updateEmbeddedDockerReference() error {
return nil
}
-// shortDigest returns the first 12 characters of the digest.
-func shortDigest(d digest.Digest) string {
- return d.Encoded()[:12]
-}
-
-// createProgressBar creates a pb.ProgressBar. Note that if the copier's
-// reportWriter is ioutil.Discard, the progress bar's output will be discarded
-// and a single line will be printed instead.
-func (c *copier) createProgressBar(srcInfo types.BlobInfo, kind string) *pb.ProgressBar {
- bar := pb.New(int(srcInfo.Size)).SetUnits(pb.U_BYTES)
- bar.SetMaxWidth(80)
- bar.ShowTimeLeft = false
- bar.ShowPercent = false
- bar.Prefix(fmt.Sprintf("Copying %s %s:", kind, shortDigest(srcInfo.Digest)))
- bar.Output = c.progressOutput
- if bar.Output == ioutil.Discard {
- c.Printf("Copying %s %s\n", kind, srcInfo.Digest)
- }
- return bar
-}
-
// isTTY returns true if the io.Writer is a file and a tty.
func isTTY(w io.Writer) bool {
if f, ok := w.(*os.File); ok {
@@ -477,6 +457,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
// copyGroup is used to determine if all layers are copied
copyGroup := sync.WaitGroup{}
copyGroup.Add(numLayers)
+
// copySemaphore is used to limit the number of parallel downloads to
// avoid malicious images causing troubles and to be nice to servers.
var copySemaphore *semaphore.Weighted
@@ -487,8 +468,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
}
data := make([]copyLayerData, numLayers)
- copyLayerHelper := func(index int, srcLayer types.BlobInfo, bar *pb.ProgressBar) {
- defer bar.Finish()
+ copyLayerHelper := func(index int, srcLayer types.BlobInfo, bar *mpb.Bar) {
defer copySemaphore.Release(1)
defer copyGroup.Done()
cld := copyLayerData{}
@@ -498,45 +478,37 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
// does not support them.
if ic.diffIDsAreNeeded {
cld.err = errors.New("getting DiffID for foreign layers is unimplemented")
- bar.Prefix(fmt.Sprintf("Skipping blob %s (DiffID foreign layer unimplemented):", shortDigest(srcLayer.Digest)))
- bar.Finish()
} else {
cld.destInfo = srcLayer
logrus.Debugf("Skipping foreign layer %q copy to %s", cld.destInfo.Digest, ic.c.dest.Reference().Transport().Name())
- bar.Prefix(fmt.Sprintf("Skipping blob %s (foreign layer):", shortDigest(srcLayer.Digest)))
- bar.Add64(bar.Total)
- bar.Finish()
}
} else {
cld.destInfo, cld.diffID, cld.err = ic.copyLayer(ctx, srcLayer, bar)
}
data[index] = cld
+ bar.SetTotal(srcLayer.Size, true)
}
- progressBars := make([]*pb.ProgressBar, numLayers)
- for i, srcInfo := range srcInfos {
- bar := ic.c.createProgressBar(srcInfo, "blob")
- progressBars[i] = bar
- }
+ func() { // A scope for defer
+ progressPool, progressCleanup := ic.c.newProgressPool(ctx)
+ defer progressCleanup()
- progressPool := pb.NewPool(progressBars...)
- progressPool.Output = ic.c.progressOutput
+ progressBars := make([]*mpb.Bar, numLayers)
+ for i, srcInfo := range srcInfos {
+ progressBars[i] = ic.c.createProgressBar(progressPool, srcInfo, "blob")
+ }
- if err := progressPool.Start(); err != nil {
- return errors.Wrapf(err, "error creating progress-bar pool")
- }
+ for i, srcLayer := range srcInfos {
+ copySemaphore.Acquire(ctx, 1)
+ go copyLayerHelper(i, srcLayer, progressBars[i])
+ }
- for i, srcLayer := range srcInfos {
- copySemaphore.Acquire(ctx, 1)
- go copyLayerHelper(i, srcLayer, progressBars[i])
- }
+ // Wait for all layers to be copied
+ copyGroup.Wait()
+ }()
destInfos := make([]types.BlobInfo, numLayers)
diffIDs := make([]digest.Digest, numLayers)
-
- copyGroup.Wait()
- progressPool.Stop()
-
for i, cld := range data {
if cld.err != nil {
return cld.err
@@ -607,6 +579,44 @@ func (ic *imageCopier) copyUpdatedConfigAndManifest(ctx context.Context) ([]byte
return manifest, nil
}
+// newProgressPool creates a *mpb.Progress and a cleanup function.
+// The caller must eventually call the returned cleanup function after the pool will no longer be updated.
+func (c *copier) newProgressPool(ctx context.Context) (*mpb.Progress, func()) {
+ ctx, cancel := context.WithCancel(ctx)
+ pool := mpb.New(mpb.WithWidth(40), mpb.WithOutput(c.progressOutput), mpb.WithContext(ctx))
+ return pool, func() {
+ cancel()
+ pool.Wait()
+ }
+}
+
+// createProgressBar creates a mpb.Bar in pool. Note that if the copier's reportWriter
+// is ioutil.Discard, the progress bar's output will be discarded
+func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind string) *mpb.Bar {
+ // shortDigestLen is the length of the digest used for blobs.
+ const shortDigestLen = 12
+
+ prefix := fmt.Sprintf("Copying %s %s", kind, info.Digest.Encoded())
+ // Truncate the prefix (chopping of some part of the digest) to make all progress bars aligned in a column.
+ maxPrefixLen := len("Copying blob ") + shortDigestLen
+ if len(prefix) > maxPrefixLen {
+ prefix = prefix[:maxPrefixLen]
+ }
+
+ bar := pool.AddBar(info.Size,
+ mpb.PrependDecorators(
+ decor.Name(prefix),
+ ),
+ mpb.AppendDecorators(
+ decor.CountersKibiByte("%.1f / %.1f"),
+ ),
+ )
+ if c.progressOutput == ioutil.Discard {
+ c.Printf("Copying %s %s\n", kind, info.Digest)
+ }
+ return bar
+}
+
// copyConfig copies config.json, if any, from src to dest.
func (c *copier) copyConfig(ctx context.Context, src types.Image) error {
srcInfo := src.ConfigInfo()
@@ -615,12 +625,20 @@ func (c *copier) copyConfig(ctx context.Context, src types.Image) error {
if err != nil {
return errors.Wrapf(err, "Error reading config blob %s", srcInfo.Digest)
}
- bar := c.createProgressBar(srcInfo, "config")
- defer bar.Finish()
- bar.Start()
- destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, bar)
+
+ destInfo, err := func() (types.BlobInfo, error) { // A scope for defer
+ progressPool, progressCleanup := c.newProgressPool(ctx)
+ defer progressCleanup()
+ bar := c.createProgressBar(progressPool, srcInfo, "config")
+ destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, bar)
+ if err != nil {
+ return types.BlobInfo{}, err
+ }
+ bar.SetTotal(int64(len(configBlob)), true)
+ return destInfo, nil
+ }()
if err != nil {
- return err
+ return nil
}
if destInfo.Digest != srcInfo.Digest {
return errors.Errorf("Internal error: copying uncompressed config blob %s changed digest to %s", srcInfo.Digest, destInfo.Digest)
@@ -638,7 +656,7 @@ type diffIDResult struct {
// copyLayer copies a layer with srcInfo (with known Digest and possibly known Size) in src to dest, perhaps compressing it if canCompress,
// and returns a complete blobInfo of the copied layer, and a value for LayerDiffIDs if diffIDIsNeeded
-func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, bar *pb.ProgressBar) (types.BlobInfo, digest.Digest, error) {
+func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, bar *mpb.Bar) (types.BlobInfo, digest.Digest, error) {
cachedDiffID := ic.c.blobInfoCache.UncompressedDigest(srcInfo.Digest) // May be ""
diffIDIsNeeded := ic.diffIDsAreNeeded && cachedDiffID == ""
@@ -649,9 +667,7 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, ba
return types.BlobInfo{}, "", errors.Wrapf(err, "Error trying to reuse blob %s at destination", srcInfo.Digest)
}
if reused {
- bar.Prefix(fmt.Sprintf("Skipping blob %s (already present):", shortDigest(srcInfo.Digest)))
- bar.Add64(bar.Total)
- bar.Finish()
+ logrus.Debugf("Skipping blob %s (already present):", srcInfo.Digest)
return blobInfo, cachedDiffID, nil
}
}
@@ -663,8 +679,7 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, ba
}
defer srcStream.Close()
- blobInfo, diffIDChan, err := ic.copyLayerFromStream(ctx, srcStream, types.BlobInfo{Digest: srcInfo.Digest, Size: srcBlobSize},
- diffIDIsNeeded, bar)
+ blobInfo, diffIDChan, err := ic.copyLayerFromStream(ctx, srcStream, types.BlobInfo{Digest: srcInfo.Digest, Size: srcBlobSize}, diffIDIsNeeded, bar)
if err != nil {
return types.BlobInfo{}, "", err
}
@@ -692,7 +707,7 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, ba
// perhaps compressing the stream if canCompress,
// and returns a complete blobInfo of the copied blob and perhaps a <-chan diffIDResult if diffIDIsNeeded, to be read by the caller.
func (ic *imageCopier) copyLayerFromStream(ctx context.Context, srcStream io.Reader, srcInfo types.BlobInfo,
- diffIDIsNeeded bool, bar *pb.ProgressBar) (types.BlobInfo, <-chan diffIDResult, error) {
+ diffIDIsNeeded bool, bar *mpb.Bar) (types.BlobInfo, <-chan diffIDResult, error) {
var getDiffIDRecorder func(compression.DecompressorFunc) io.Writer // = nil
var diffIDChan chan diffIDResult
@@ -753,7 +768,7 @@ func computeDiffID(stream io.Reader, decompressor compression.DecompressorFunc)
// and returns a complete blobInfo of the copied blob.
func (c *copier) copyBlobFromStream(ctx context.Context, srcStream io.Reader, srcInfo types.BlobInfo,
getOriginalLayerCopyWriter func(decompressor compression.DecompressorFunc) io.Writer,
- canModifyBlob bool, isConfig bool, bar *pb.ProgressBar) (types.BlobInfo, error) {
+ canModifyBlob bool, isConfig bool, bar *mpb.Bar) (types.BlobInfo, error) {
// The copying happens through a pipeline of connected io.Readers.
// === Input: srcStream
@@ -776,7 +791,7 @@ func (c *copier) copyBlobFromStream(ctx context.Context, srcStream io.Reader, sr
return types.BlobInfo{}, errors.Wrapf(err, "Error reading blob %s", srcInfo.Digest)
}
isCompressed := decompressor != nil
- destStream = bar.NewProxyReader(destStream)
+ destStream = bar.ProxyReader(destStream)
// === Send a copy of the original, uncompressed, stream, to a separate path if necessary.
var originalLayerReader io.Reader // DO NOT USE this other than to drain the input if no other consumer in the pipeline has done so.
diff --git a/vendor/github.com/containers/image/vendor.conf b/vendor/github.com/containers/image/vendor.conf
index bbbb1a458..1c5b6b378 100644
--- a/vendor/github.com/containers/image/vendor.conf
+++ b/vendor/github.com/containers/image/vendor.conf
@@ -28,7 +28,6 @@ golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8
golang.org/x/net 6b27048ae5e6ad1ef927e72e437531493de612fe
golang.org/x/sync 42b317875d0fa942474b76e1b46a6060d720ae6e
golang.org/x/sys 43e60d72a8e2bd92ee98319ba9a384a0e9837c08
-gopkg.in/cheggaaa/pb.v1 v1.0.27
gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a
k8s.io/client-go bcde30fb7eaed76fd98a36b4120321b94995ffb6
github.com/xeipuuv/gojsonschema master
@@ -48,3 +47,6 @@ github.com/boltdb/bolt master
github.com/klauspost/pgzip v1.2.1
github.com/klauspost/compress v1.4.1
github.com/klauspost/cpuid v1.2.0
+github.com/vbauerster/mpb v3.3.4
+github.com/mattn/go-isatty v0.0.4
+github.com/VividCortex/ewma v1.1.1
diff --git a/vendor/github.com/containers/storage/containers.go b/vendor/github.com/containers/storage/containers.go
index beaf41f39..10d628dbe 100644
--- a/vendor/github.com/containers/storage/containers.go
+++ b/vendor/github.com/containers/storage/containers.go
@@ -568,6 +568,9 @@ func (r *containerStore) Lock() {
r.lockfile.Lock()
}
+func (r *containerStore) RLock() {
+ r.lockfile.RLock()
+}
func (r *containerStore) Unlock() {
r.lockfile.Unlock()
}
diff --git a/vendor/github.com/containers/storage/containers_ffjson.go b/vendor/github.com/containers/storage/containers_ffjson.go
index 40b912bb3..aef6becfe 100644
--- a/vendor/github.com/containers/storage/containers_ffjson.go
+++ b/vendor/github.com/containers/storage/containers_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: ./containers.go
+// source: containers.go
package storage
diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
index 57d6dd63a..657d9b3ce 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
@@ -239,6 +239,8 @@ func parseOptions(options []string) (*overlayOptions, error) {
}
key = strings.ToLower(key)
switch key {
+ case ".override_kernel_check", "overlay.override_kernel_check", "overlay2.override_kernel_check":
+ logrus.Warnf("overlay: override_kernel_check option was specified, but is no longer necessary")
case ".mountopt", "overlay.mountopt", "overlay2.mountopt":
o.mountOptions = val
case ".size", "overlay.size", "overlay2.size":
diff --git a/vendor/github.com/containers/storage/errors.go b/vendor/github.com/containers/storage/errors.go
index 562415c09..63cb9ab74 100644
--- a/vendor/github.com/containers/storage/errors.go
+++ b/vendor/github.com/containers/storage/errors.go
@@ -43,8 +43,6 @@ var (
ErrSizeUnknown = errors.New("size is not known")
// ErrStoreIsReadOnly is returned when the caller makes a call to a read-only store that would require modifying its contents.
ErrStoreIsReadOnly = errors.New("called a write method on a read-only store")
- // ErrLockReadOnly indicates that the caller only took a read-only lock, and is not allowed to write.
- ErrLockReadOnly = errors.New("lock is not a read-write lock")
// ErrDuplicateImageNames indicates that the read-only store uses the same name for multiple images.
ErrDuplicateImageNames = errors.New("read-only image store assigns the same name to multiple images")
// ErrDuplicateLayerNames indicates that the read-only store uses the same name for multiple layers.
diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go
index fa4a7c43b..93505f5fb 100644
--- a/vendor/github.com/containers/storage/images.go
+++ b/vendor/github.com/containers/storage/images.go
@@ -756,6 +756,10 @@ func (r *imageStore) Lock() {
r.lockfile.Lock()
}
+func (r *imageStore) RLock() {
+ r.lockfile.RLock()
+}
+
func (r *imageStore) Unlock() {
r.lockfile.Unlock()
}
diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go
index cdc3cbba9..d612f0459 100644
--- a/vendor/github.com/containers/storage/layers.go
+++ b/vendor/github.com/containers/storage/layers.go
@@ -1222,6 +1222,10 @@ func (r *layerStore) Lock() {
r.lockfile.Lock()
}
+func (r *layerStore) RLock() {
+ r.lockfile.RLock()
+}
+
func (r *layerStore) Unlock() {
r.lockfile.Unlock()
}
diff --git a/vendor/github.com/containers/storage/layers_ffjson.go b/vendor/github.com/containers/storage/layers_ffjson.go
index 09b5d0f33..125b5d8c9 100644
--- a/vendor/github.com/containers/storage/layers_ffjson.go
+++ b/vendor/github.com/containers/storage/layers_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: ./layers.go
+// source: layers.go
package storage
diff --git a/vendor/github.com/containers/storage/lockfile.go b/vendor/github.com/containers/storage/lockfile.go
index 9f6a18144..7f07b9ac5 100644
--- a/vendor/github.com/containers/storage/lockfile.go
+++ b/vendor/github.com/containers/storage/lockfile.go
@@ -1,7 +1,6 @@
package storage
import (
- "fmt"
"path/filepath"
"sync"
"time"
@@ -13,7 +12,14 @@ import (
// identifier of the last party that made changes to whatever's being protected
// by the lock.
type Locker interface {
- sync.Locker
+ // Acquire a writer lock.
+ Lock()
+
+ // Unlock the lock.
+ Unlock()
+
+ // Acquire a reader lock.
+ RLock()
// Touch records, for others sharing the lock, that the caller was the
// last writer. It should only be called with the lock held.
@@ -39,31 +45,22 @@ var (
)
// GetLockfile opens a read-write lock file, creating it if necessary. The
-// Locker object it returns will be returned unlocked.
+// Locker object may already be locked if the path has already been requested
+// by the current process.
func GetLockfile(path string) (Locker, error) {
- lockfilesLock.Lock()
- defer lockfilesLock.Unlock()
- if lockfiles == nil {
- lockfiles = make(map[string]Locker)
- }
- cleanPath := filepath.Clean(path)
- if locker, ok := lockfiles[cleanPath]; ok {
- if !locker.IsReadWrite() {
- return nil, errors.Wrapf(ErrLockReadOnly, "lock %q is a read-only lock", cleanPath)
- }
- return locker, nil
- }
- locker, err := getLockFile(path, false) // platform dependent locker
- if err != nil {
- return nil, err
- }
- lockfiles[filepath.Clean(path)] = locker
- return locker, nil
+ return getLockfile(path, false)
}
-// GetROLockfile opens a read-only lock file. The Locker object it returns
-// will be returned unlocked.
+// GetROLockfile opens a read-only lock file, creating it if necessary. The
+// Locker object may already be locked if the path has already been requested
+// by the current process.
func GetROLockfile(path string) (Locker, error) {
+ return getLockfile(path, true)
+}
+
+// getLockfile is a helper for GetLockfile and GetROLockfile and returns Locker
+// based on the path and read-only property.
+func getLockfile(path string, ro bool) (Locker, error) {
lockfilesLock.Lock()
defer lockfilesLock.Unlock()
if lockfiles == nil {
@@ -71,12 +68,15 @@ func GetROLockfile(path string) (Locker, error) {
}
cleanPath := filepath.Clean(path)
if locker, ok := lockfiles[cleanPath]; ok {
- if locker.IsReadWrite() {
- return nil, fmt.Errorf("lock %q is a read-write lock", cleanPath)
+ if ro && locker.IsReadWrite() {
+ return nil, errors.Errorf("lock %q is not a read-only lock", cleanPath)
+ }
+ if !ro && !locker.IsReadWrite() {
+ return nil, errors.Errorf("lock %q is not a read-write lock", cleanPath)
}
return locker, nil
}
- locker, err := getLockFile(path, true) // platform dependent locker
+ locker, err := getLockFile(path, ro) // platform dependent locker
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/containers/storage/lockfile_unix.go b/vendor/github.com/containers/storage/lockfile_unix.go
index 31e39c02e..0adbc49a5 100644
--- a/vendor/github.com/containers/storage/lockfile_unix.go
+++ b/vendor/github.com/containers/storage/lockfile_unix.go
@@ -3,6 +3,7 @@
package storage
import (
+ "fmt"
"os"
"sync"
"time"
@@ -24,38 +25,84 @@ func getLockFile(path string, ro bool) (Locker, error) {
return nil, errors.Wrapf(err, "error opening %q", path)
}
unix.CloseOnExec(fd)
+
+ locktype := unix.F_WRLCK
if ro {
- return &lockfile{file: path, fd: uintptr(fd), lw: stringid.GenerateRandomID(), locktype: unix.F_RDLCK, locked: false}, nil
+ locktype = unix.F_RDLCK
}
- return &lockfile{file: path, fd: uintptr(fd), lw: stringid.GenerateRandomID(), locktype: unix.F_WRLCK, locked: false}, nil
+ return &lockfile{
+ stateMutex: &sync.Mutex{},
+ writeMutex: &sync.Mutex{},
+ file: path,
+ fd: uintptr(fd),
+ lw: stringid.GenerateRandomID(),
+ locktype: int16(locktype),
+ locked: false,
+ ro: ro}, nil
}
type lockfile struct {
- mu sync.Mutex
- file string
- fd uintptr
- lw string
- locktype int16
- locked bool
+ // stateMutex is used to synchronize concurrent accesses
+ stateMutex *sync.Mutex
+ // writeMutex is used to serialize and avoid recursive writer locks
+ writeMutex *sync.Mutex
+ counter int64
+ file string
+ fd uintptr
+ lw string
+ locktype int16
+ locked bool
+ ro bool
}
-// Lock locks the lock file
-func (l *lockfile) Lock() {
+// lock locks the lockfile via FCTNL(2) based on the specified type and
+// command.
+func (l *lockfile) lock(l_type int16) {
lk := unix.Flock_t{
- Type: l.locktype,
+ Type: l_type,
Whence: int16(os.SEEK_SET),
Start: 0,
Len: 0,
Pid: int32(os.Getpid()),
}
- l.mu.Lock()
+ if l_type == unix.F_WRLCK {
+ // If we try to lock as a writer, lock the writerMutex first to
+ // avoid multiple writer acquisitions of the same process.
+ // Note: it's important to lock it prior to the stateMutex to
+ // avoid a deadlock.
+ l.writeMutex.Lock()
+ }
+ l.stateMutex.Lock()
+ l.locktype = l_type
+ if l.counter == 0 {
+ // Optimization: only use the (expensive) fcntl syscall when
+ // the counter is 0. If it's greater than that, we're owning
+ // the lock already and can only be a reader.
+ for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
+ time.Sleep(10 * time.Millisecond)
+ }
+ }
l.locked = true
- for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
- time.Sleep(10 * time.Millisecond)
+ l.counter++
+ l.stateMutex.Unlock()
+}
+
+// Lock locks the lockfile as a writer. Note that RLock() will be called if
+// the lock is a read-only one.
+func (l *lockfile) Lock() {
+ if l.ro {
+ l.RLock()
+ } else {
+ l.lock(unix.F_WRLCK)
}
}
-// Unlock unlocks the lock file
+// LockRead locks the lockfile as a reader.
+func (l *lockfile) RLock() {
+ l.lock(unix.F_RDLCK)
+}
+
+// Unlock unlocks the lockfile.
func (l *lockfile) Unlock() {
lk := unix.Flock_t{
Type: unix.F_UNLCK,
@@ -64,19 +111,40 @@ func (l *lockfile) Unlock() {
Len: 0,
Pid: int32(os.Getpid()),
}
- for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
- time.Sleep(10 * time.Millisecond)
+ l.stateMutex.Lock()
+ if l.locked == false {
+ // Panic when unlocking an unlocked lock. That's a violation
+ // of the lock semantics and will reveal such.
+ panic("calling Unlock on unlocked lock")
+ }
+ l.counter--
+ if l.counter < 0 {
+ // Panic when the counter is negative. There is no way we can
+ // recover from a corrupted lock and we need to protect the
+ // storage from corruption.
+ panic(fmt.Sprintf("lock %q has been unlocked too often", l.file))
+ }
+ if l.counter == 0 {
+ // We should only release the lock when the counter is 0 to
+ // avoid releasing read-locks too early; a given process may
+ // acquire a read lock multiple times.
+ l.locked = false
+ for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
+ time.Sleep(10 * time.Millisecond)
+ }
+ }
+ if l.locktype == unix.F_WRLCK {
+ l.writeMutex.Unlock()
}
- l.locked = false
- l.mu.Unlock()
+ l.stateMutex.Unlock()
}
-// Check if lock is locked
+// Locked checks if lockfile is locked.
func (l *lockfile) Locked() bool {
return l.locked
}
-// Touch updates the lock file with the UID of the user
+// Touch updates the lock file with the UID of the user.
func (l *lockfile) Touch() error {
l.lw = stringid.GenerateRandomID()
id := []byte(l.lw)
@@ -98,7 +166,8 @@ func (l *lockfile) Touch() error {
return nil
}
-// Modified indicates if the lock file has been updated since the last time it was loaded
+// Modified indicates if the lockfile has been updated since the last time it
+// was loaded.
func (l *lockfile) Modified() (bool, error) {
id := []byte(l.lw)
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
@@ -117,7 +186,7 @@ func (l *lockfile) Modified() (bool, error) {
return l.lw != lw, nil
}
-// IsRWLock indicates if the lock file is a read-write lock
+// IsReadWriteLock indicates if the lock file is a read-write lock.
func (l *lockfile) IsReadWrite() bool {
- return (l.locktype == unix.F_WRLCK)
+ return !l.ro
}
diff --git a/vendor/github.com/containers/storage/lockfile_windows.go b/vendor/github.com/containers/storage/lockfile_windows.go
index 77490b851..a3821bfeb 100644
--- a/vendor/github.com/containers/storage/lockfile_windows.go
+++ b/vendor/github.com/containers/storage/lockfile_windows.go
@@ -23,6 +23,11 @@ func (l *lockfile) Lock() {
l.locked = true
}
+func (l *lockfile) RLock() {
+ l.mu.Lock()
+ l.locked = true
+}
+
func (l *lockfile) Unlock() {
l.locked = false
l.mu.Unlock()
diff --git a/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go b/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go
index 2ba44b85d..9b8103e4d 100644
--- a/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go
+++ b/vendor/github.com/containers/storage/pkg/archive/archive_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: ./pkg/archive/archive.go
+// source: pkg/archive/archive.go
package archive
diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go
index 856c73e51..d53703d6b 100644
--- a/vendor/github.com/containers/storage/store.go
+++ b/vendor/github.com/containers/storage/store.go
@@ -566,10 +566,10 @@ func GetStore(options StoreOptions) (Store, error) {
}
if options.GraphRoot == "" {
- return nil, ErrIncompleteOptions
+ return nil, errors.Wrap(ErrIncompleteOptions, "no storage root specified")
}
if options.RunRoot == "" {
- return nil, ErrIncompleteOptions
+ return nil, errors.Wrap(ErrIncompleteOptions, "no storage runroot specified")
}
if err := os.MkdirAll(options.RunRoot, 0700); err != nil && !os.IsExist(err) {
@@ -870,7 +870,8 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
gidMap := options.GIDMap
if parent != "" {
var ilayer *Layer
- for _, lstore := range append([]ROLayerStore{rlstore}, rlstores...) {
+ for _, l := range append([]ROLayerStore{rlstore}, rlstores...) {
+ lstore := l
if lstore != rlstore {
lstore.Lock()
defer lstore.Unlock()
@@ -949,7 +950,8 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
return nil, err
}
var ilayer *Layer
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1010,7 +1012,8 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, crea
}
var layer, parentLayer *Layer
// Locate the image's top layer and its parent, if it has one.
- for _, store := range append([]ROLayerStore{rlstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{rlstore}, lstores...) {
+ store := s
if store != rlstore {
store.Lock()
defer store.Unlock()
@@ -1143,7 +1146,8 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
}
}
var cimage *Image
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1315,7 +1319,8 @@ func (s *store) Metadata(id string) (string, error) {
if err != nil {
return "", err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1336,7 +1341,8 @@ func (s *store) Metadata(id string) (string, error) {
if err != nil {
return "", err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1375,7 +1381,8 @@ func (s *store) ListImageBigData(id string) ([]string, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1400,7 +1407,8 @@ func (s *store) ImageBigDataSize(id, key string) (int64, error) {
if err != nil {
return -1, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1426,7 +1434,8 @@ func (s *store) ImageBigDataDigest(id, key string) (digest.Digest, error) {
return "", err
}
stores = append([]ROImageStore{ristore}, stores...)
- for _, ristore := range stores {
+ for _, r := range stores {
+ ristore := r
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
@@ -1451,8 +1460,9 @@ func (s *store) ImageBigData(id, key string) ([]byte, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
- store.Lock()
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
+ store.RLock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
if err = store.Load(); err != nil {
@@ -1495,7 +1505,8 @@ func (s *store) ImageSize(id string) (int64, error) {
if err != nil {
return -1, errors.Wrapf(err, "error loading additional layer stores")
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1516,7 +1527,8 @@ func (s *store) ImageSize(id string) (int64, error) {
}
// Look for the image's record.
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1603,7 +1615,8 @@ func (s *store) ContainerSize(id string) (int64, error) {
if err != nil {
return -1, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1772,7 +1785,8 @@ func (s *store) Exists(id string) bool {
if err != nil {
return false
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1793,7 +1807,8 @@ func (s *store) Exists(id string) bool {
if err != nil {
return false
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1895,7 +1910,8 @@ func (s *store) Names(id string) ([]string, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1916,7 +1932,8 @@ func (s *store) Names(id string) ([]string, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1955,7 +1972,8 @@ func (s *store) Lookup(name string) (string, error) {
if err != nil {
return "", err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -1976,7 +1994,8 @@ func (s *store) Lookup(name string) (string, error) {
if err != nil {
return "", err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2486,7 +2505,8 @@ func (s *store) Changes(from, to string) ([]archive.Change, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2510,7 +2530,8 @@ func (s *store) DiffSize(from, to string) (int64, error) {
if err != nil {
return -1, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2534,8 +2555,9 @@ func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, erro
if err != nil {
return nil, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
- store.Lock()
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
+ store.RLock()
if modified, err := store.Modified(); modified || err != nil {
if err = store.Load(); err != nil {
return nil, err
@@ -2588,7 +2610,8 @@ func (s *store) layersByMappedDigest(m func(ROLayerStore, digest.Digest) ([]Laye
if err != nil {
return nil, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2634,7 +2657,8 @@ func (s *store) LayerSize(id string) (int64, error) {
if err != nil {
return -1, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2712,7 +2736,8 @@ func (s *store) Layers() ([]Layer, error) {
return nil, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2740,7 +2765,8 @@ func (s *store) Images() ([]Image, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2783,7 +2809,8 @@ func (s *store) Layer(id string) (*Layer, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROLayerStore{lstore}, lstores...) {
+ for _, s := range append([]ROLayerStore{lstore}, lstores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2808,7 +2835,8 @@ func (s *store) Image(id string) (*Image, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
@@ -2840,7 +2868,8 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) {
if err != nil {
return nil, err
}
- for _, store := range append([]ROImageStore{istore}, istores...) {
+ for _, s := range append([]ROImageStore{istore}, istores...) {
+ store := s
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE
new file mode 100644
index 000000000..65dc692b6
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
+
+MIT License (Expat)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md
new file mode 100644
index 000000000..1e69004bb
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/README.md
@@ -0,0 +1,50 @@
+# go-isatty
+
+[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty)
+[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty)
+[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty)
+
+isatty for golang
+
+## Usage
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/mattn/go-isatty"
+ "os"
+)
+
+func main() {
+ if isatty.IsTerminal(os.Stdout.Fd()) {
+ fmt.Println("Is Terminal")
+ } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
+ fmt.Println("Is Cygwin/MSYS2 Terminal")
+ } else {
+ fmt.Println("Is Not Terminal")
+ }
+}
+```
+
+## Installation
+
+```
+$ go get github.com/mattn/go-isatty
+```
+
+## License
+
+MIT
+
+## Author
+
+Yasuhiro Matsumoto (a.k.a mattn)
+
+## Thanks
+
+* k-takata: base idea for IsCygwinTerminal
+
+ https://github.com/k-takata/go-iscygpty
diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go
new file mode 100644
index 000000000..17d4f90eb
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/doc.go
@@ -0,0 +1,2 @@
+// Package isatty implements interface to isatty
+package isatty
diff --git a/vendor/github.com/mattn/go-isatty/isatty_appengine.go b/vendor/github.com/mattn/go-isatty/isatty_appengine.go
new file mode 100644
index 000000000..9584a9884
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_appengine.go
@@ -0,0 +1,15 @@
+// +build appengine
+
+package isatty
+
+// IsTerminal returns true if the file descriptor is terminal which
+// is always false on on appengine classic which is a sandboxed PaaS.
+func IsTerminal(fd uintptr) bool {
+ return false
+}
+
+// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go
new file mode 100644
index 000000000..42f2514d1
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go
@@ -0,0 +1,18 @@
+// +build darwin freebsd openbsd netbsd dragonfly
+// +build !appengine
+
+package isatty
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const ioctlReadTermios = syscall.TIOCGETA
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_linux.go b/vendor/github.com/mattn/go-isatty/isatty_linux.go
new file mode 100644
index 000000000..7384cf991
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_linux.go
@@ -0,0 +1,18 @@
+// +build linux
+// +build !appengine,!ppc64,!ppc64le
+
+package isatty
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+const ioctlReadTermios = syscall.TCGETS
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go b/vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
new file mode 100644
index 000000000..44e5d2130
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
@@ -0,0 +1,19 @@
+// +build linux
+// +build ppc64 ppc64le
+
+package isatty
+
+import (
+ "unsafe"
+
+ syscall "golang.org/x/sys/unix"
+)
+
+const ioctlReadTermios = syscall.TCGETS
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var termios syscall.Termios
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+ return err == 0
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go
new file mode 100644
index 000000000..9d8b4a599
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_others.go
@@ -0,0 +1,10 @@
+// +build !windows
+// +build !appengine
+
+package isatty
+
+// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
+// terminal. This is also always false on this environment.
+func IsCygwinTerminal(fd uintptr) bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go
new file mode 100644
index 000000000..1f0c6bf53
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go
@@ -0,0 +1,16 @@
+// +build solaris
+// +build !appengine
+
+package isatty
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
+func IsTerminal(fd uintptr) bool {
+ var termio unix.Termio
+ err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
+ return err == nil
+}
diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go
new file mode 100644
index 000000000..af51cbcaa
--- /dev/null
+++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go
@@ -0,0 +1,94 @@
+// +build windows
+// +build !appengine
+
+package isatty
+
+import (
+ "strings"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+const (
+ fileNameInfo uintptr = 2
+ fileTypePipe = 3
+)
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32.dll")
+ procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
+ procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
+ procGetFileType = kernel32.NewProc("GetFileType")
+)
+
+func init() {
+ // Check if GetFileInformationByHandleEx is available.
+ if procGetFileInformationByHandleEx.Find() != nil {
+ procGetFileInformationByHandleEx = nil
+ }
+}
+
+// IsTerminal return true if the file descriptor is terminal.
+func IsTerminal(fd uintptr) bool {
+ var st uint32
+ r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
+ return r != 0 && e == 0
+}
+
+// Check pipe name is used for cygwin/msys2 pty.
+// Cygwin/MSYS2 PTY has a name like:
+// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
+func isCygwinPipeName(name string) bool {
+ token := strings.Split(name, "-")
+ if len(token) < 5 {
+ return false
+ }
+
+ if token[0] != `\msys` && token[0] != `\cygwin` {
+ return false
+ }
+
+ if token[1] == "" {
+ return false
+ }
+
+ if !strings.HasPrefix(token[2], "pty") {
+ return false
+ }
+
+ if token[3] != `from` && token[3] != `to` {
+ return false
+ }
+
+ if token[4] != "master" {
+ return false
+ }
+
+ return true
+}
+
+// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
+// terminal.
+func IsCygwinTerminal(fd uintptr) bool {
+ if procGetFileInformationByHandleEx == nil {
+ return false
+ }
+
+ // Cygwin/msys's pty is a pipe.
+ ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
+ if ft != fileTypePipe || e != 0 {
+ return false
+ }
+
+ var buf [2 + syscall.MAX_PATH]uint16
+ r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
+ 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
+ uintptr(len(buf)*2), 0, 0)
+ if r == 0 || e != 0 {
+ return false
+ }
+
+ l := *(*uint32)(unsafe.Pointer(&buf))
+ return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
+}
diff --git a/vendor/github.com/mattn/go-runewidth/README.mkd b/vendor/github.com/mattn/go-runewidth/README.mkd
deleted file mode 100644
index 66663a94b..000000000
--- a/vendor/github.com/mattn/go-runewidth/README.mkd
+++ /dev/null
@@ -1,27 +0,0 @@
-go-runewidth
-============
-
-[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth)
-[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
-[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth)
-[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth)
-
-Provides functions to get fixed width of the character or string.
-
-Usage
------
-
-```go
-runewidth.StringWidth("つのだ☆HIRO") == 12
-```
-
-
-Author
-------
-
-Yasuhiro Matsumoto
-
-License
--------
-
-under the MIT License: http://mattn.mit-license.org/2013
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go
deleted file mode 100644
index 3cb94106f..000000000
--- a/vendor/github.com/mattn/go-runewidth/runewidth.go
+++ /dev/null
@@ -1,977 +0,0 @@
-package runewidth
-
-import (
- "os"
-)
-
-var (
- // EastAsianWidth will be set true if the current locale is CJK
- EastAsianWidth bool
-
- // ZeroWidthJoiner is flag to set to use UTR#51 ZWJ
- ZeroWidthJoiner bool
-
- // DefaultCondition is a condition in current locale
- DefaultCondition = &Condition{}
-)
-
-func init() {
- handleEnv()
-}
-
-func handleEnv() {
- env := os.Getenv("RUNEWIDTH_EASTASIAN")
- if env == "" {
- EastAsianWidth = IsEastAsian()
- } else {
- EastAsianWidth = env == "1"
- }
- // update DefaultCondition
- DefaultCondition.EastAsianWidth = EastAsianWidth
- DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner
-}
-
-type interval struct {
- first rune
- last rune
-}
-
-type table []interval
-
-func inTables(r rune, ts ...table) bool {
- for _, t := range ts {
- if inTable(r, t) {
- return true
- }
- }
- return false
-}
-
-func inTable(r rune, t table) bool {
- // func (t table) IncludesRune(r rune) bool {
- if r < t[0].first {
- return false
- }
-
- bot := 0
- top := len(t) - 1
- for top >= bot {
- mid := (bot + top) >> 1
-
- switch {
- case t[mid].last < r:
- bot = mid + 1
- case t[mid].first > r:
- top = mid - 1
- default:
- return true
- }
- }
-
- return false
-}
-
-var private = table{
- {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
-}
-
-var nonprint = table{
- {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
- {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
- {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
- {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
-}
-
-var combining = table{
- {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD},
- {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5},
- {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F},
- {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4},
- {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711},
- {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3},
- {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827},
- {0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D4, 0x08E1},
- {0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F},
- {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0983},
- {0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8},
- {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3},
- {0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42},
- {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51},
- {0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83},
- {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9},
- {0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B03},
- {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48},
- {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B62, 0x0B63},
- {0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8},
- {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0C00, 0x0C03},
- {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D},
- {0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C83},
- {0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8},
- {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3},
- {0x0D01, 0x0D03}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48},
- {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63},
- {0x0D82, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4},
- {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DF2, 0x0DF3},
- {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
- {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
- {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
- {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F},
- {0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97},
- {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E},
- {0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064},
- {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D},
- {0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F},
- {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
- {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD},
- {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9},
- {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B},
- {0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F},
- {0x1AB0, 0x1ABE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44},
- {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD},
- {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2},
- {0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF2, 0x1CF4},
- {0x1CF8, 0x1CF9}, {0x1DC0, 0x1DF5}, {0x1DFB, 0x1DFF},
- {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F},
- {0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A},
- {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F},
- {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806},
- {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA880, 0xA881},
- {0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA926, 0xA92D},
- {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0},
- {0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43},
- {0xAA4C, 0xAA4D}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0},
- {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF},
- {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6},
- {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E},
- {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD},
- {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03},
- {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A},
- {0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x11000, 0x11002},
- {0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA},
- {0x11100, 0x11102}, {0x11127, 0x11134}, {0x11173, 0x11173},
- {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111CA, 0x111CC},
- {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA},
- {0x11300, 0x11303}, {0x1133C, 0x1133C}, {0x1133E, 0x11344},
- {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357},
- {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374},
- {0x11435, 0x11446}, {0x114B0, 0x114C3}, {0x115AF, 0x115B5},
- {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640},
- {0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x11C2F, 0x11C36},
- {0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6},
- {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F51, 0x16F7E},
- {0x16F8F, 0x16F92}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169},
- {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B},
- {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1DA00, 0x1DA36},
- {0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84},
- {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006},
- {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
- {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A},
- {0xE0100, 0xE01EF},
-}
-
-var doublewidth = table{
- {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A},
- {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3},
- {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653},
- {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1},
- {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
- {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA},
- {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA},
- {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B},
- {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E},
- {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
- {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C},
- {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99},
- {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB},
- {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF},
- {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA},
- {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247},
- {0x3250, 0x32FE}, {0x3300, 0x4DBF}, {0x4E00, 0xA48C},
- {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3},
- {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
- {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60},
- {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE0}, {0x17000, 0x187EC},
- {0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004},
- {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
- {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248},
- {0x1F250, 0x1F251}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335},
- {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA},
- {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4},
- {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC},
- {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567},
- {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4},
- {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC},
- {0x1F6D0, 0x1F6D2}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6F6},
- {0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930},
- {0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E},
- {0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {0x20000, 0x2FFFD},
- {0x30000, 0x3FFFD},
-}
-
-var ambiguous = table{
- {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
- {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4},
- {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
- {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
- {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
- {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
- {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
- {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
- {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
- {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
- {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
- {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
- {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
- {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
- {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
- {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
- {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
- {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F},
- {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1},
- {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F},
- {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016},
- {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022},
- {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033},
- {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E},
- {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084},
- {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105},
- {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116},
- {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B},
- {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B},
- {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199},
- {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4},
- {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203},
- {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F},
- {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A},
- {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225},
- {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237},
- {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C},
- {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267},
- {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283},
- {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299},
- {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312},
- {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573},
- {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1},
- {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7},
- {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8},
- {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5},
- {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609},
- {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E},
- {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661},
- {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D},
- {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF},
- {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1},
- {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1},
- {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC},
- {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F},
- {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF},
- {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A},
- {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D},
- {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF},
- {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD},
-}
-
-var emoji = table{
- {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122},
- {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA},
- {0x231A, 0x231B}, {0x2328, 0x2328}, {0x23CF, 0x23CF},
- {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2},
- {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0},
- {0x25FB, 0x25FE}, {0x2600, 0x2604}, {0x260E, 0x260E},
- {0x2611, 0x2611}, {0x2614, 0x2615}, {0x2618, 0x2618},
- {0x261D, 0x261D}, {0x2620, 0x2620}, {0x2622, 0x2623},
- {0x2626, 0x2626}, {0x262A, 0x262A}, {0x262E, 0x262F},
- {0x2638, 0x263A}, {0x2640, 0x2640}, {0x2642, 0x2642},
- {0x2648, 0x2653}, {0x265F, 0x2660}, {0x2663, 0x2663},
- {0x2665, 0x2666}, {0x2668, 0x2668}, {0x267B, 0x267B},
- {0x267E, 0x267F}, {0x2692, 0x2697}, {0x2699, 0x2699},
- {0x269B, 0x269C}, {0x26A0, 0x26A1}, {0x26AA, 0x26AB},
- {0x26B0, 0x26B1}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
- {0x26C8, 0x26C8}, {0x26CE, 0x26CF}, {0x26D1, 0x26D1},
- {0x26D3, 0x26D4}, {0x26E9, 0x26EA}, {0x26F0, 0x26F5},
- {0x26F7, 0x26FA}, {0x26FD, 0x26FD}, {0x2702, 0x2702},
- {0x2705, 0x2705}, {0x2708, 0x270D}, {0x270F, 0x270F},
- {0x2712, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716},
- {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728},
- {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747},
- {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
- {0x2757, 0x2757}, {0x2763, 0x2764}, {0x2795, 0x2797},
- {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
- {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C},
- {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030},
- {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299},
- {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F170, 0x1F171},
- {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
- {0x1F1E6, 0x1F1FF}, {0x1F201, 0x1F202}, {0x1F21A, 0x1F21A},
- {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F250, 0x1F251},
- {0x1F300, 0x1F321}, {0x1F324, 0x1F393}, {0x1F396, 0x1F397},
- {0x1F399, 0x1F39B}, {0x1F39E, 0x1F3F0}, {0x1F3F3, 0x1F3F5},
- {0x1F3F7, 0x1F4FD}, {0x1F4FF, 0x1F53D}, {0x1F549, 0x1F54E},
- {0x1F550, 0x1F567}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F57A},
- {0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590},
- {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A5}, {0x1F5A8, 0x1F5A8},
- {0x1F5B1, 0x1F5B2}, {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4},
- {0x1F5D1, 0x1F5D3}, {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1},
- {0x1F5E3, 0x1F5E3}, {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF},
- {0x1F5F3, 0x1F5F3}, {0x1F5FA, 0x1F64F}, {0x1F680, 0x1F6C5},
- {0x1F6CB, 0x1F6D2}, {0x1F6E0, 0x1F6E5}, {0x1F6E9, 0x1F6E9},
- {0x1F6EB, 0x1F6EC}, {0x1F6F0, 0x1F6F0}, {0x1F6F3, 0x1F6F9},
- {0x1F910, 0x1F93A}, {0x1F93C, 0x1F93E}, {0x1F940, 0x1F945},
- {0x1F947, 0x1F970}, {0x1F973, 0x1F976}, {0x1F97A, 0x1F97A},
- {0x1F97C, 0x1F9A2}, {0x1F9B0, 0x1F9B9}, {0x1F9C0, 0x1F9C2},
- {0x1F9D0, 0x1F9FF},
-}
-
-var notassigned = table{
- {0x0378, 0x0379}, {0x0380, 0x0383}, {0x038B, 0x038B},
- {0x038D, 0x038D}, {0x03A2, 0x03A2}, {0x0530, 0x0530},
- {0x0557, 0x0558}, {0x0560, 0x0560}, {0x0588, 0x0588},
- {0x058B, 0x058C}, {0x0590, 0x0590}, {0x05C8, 0x05CF},
- {0x05EB, 0x05EF}, {0x05F5, 0x05FF}, {0x061D, 0x061D},
- {0x070E, 0x070E}, {0x074B, 0x074C}, {0x07B2, 0x07BF},
- {0x07FB, 0x07FF}, {0x082E, 0x082F}, {0x083F, 0x083F},
- {0x085C, 0x085D}, {0x085F, 0x089F}, {0x08B5, 0x08B5},
- {0x08BE, 0x08D3}, {0x0984, 0x0984}, {0x098D, 0x098E},
- {0x0991, 0x0992}, {0x09A9, 0x09A9}, {0x09B1, 0x09B1},
- {0x09B3, 0x09B5}, {0x09BA, 0x09BB}, {0x09C5, 0x09C6},
- {0x09C9, 0x09CA}, {0x09CF, 0x09D6}, {0x09D8, 0x09DB},
- {0x09DE, 0x09DE}, {0x09E4, 0x09E5}, {0x09FC, 0x0A00},
- {0x0A04, 0x0A04}, {0x0A0B, 0x0A0E}, {0x0A11, 0x0A12},
- {0x0A29, 0x0A29}, {0x0A31, 0x0A31}, {0x0A34, 0x0A34},
- {0x0A37, 0x0A37}, {0x0A3A, 0x0A3B}, {0x0A3D, 0x0A3D},
- {0x0A43, 0x0A46}, {0x0A49, 0x0A4A}, {0x0A4E, 0x0A50},
- {0x0A52, 0x0A58}, {0x0A5D, 0x0A5D}, {0x0A5F, 0x0A65},
- {0x0A76, 0x0A80}, {0x0A84, 0x0A84}, {0x0A8E, 0x0A8E},
- {0x0A92, 0x0A92}, {0x0AA9, 0x0AA9}, {0x0AB1, 0x0AB1},
- {0x0AB4, 0x0AB4}, {0x0ABA, 0x0ABB}, {0x0AC6, 0x0AC6},
- {0x0ACA, 0x0ACA}, {0x0ACE, 0x0ACF}, {0x0AD1, 0x0ADF},
- {0x0AE4, 0x0AE5}, {0x0AF2, 0x0AF8}, {0x0AFA, 0x0B00},
- {0x0B04, 0x0B04}, {0x0B0D, 0x0B0E}, {0x0B11, 0x0B12},
- {0x0B29, 0x0B29}, {0x0B31, 0x0B31}, {0x0B34, 0x0B34},
- {0x0B3A, 0x0B3B}, {0x0B45, 0x0B46}, {0x0B49, 0x0B4A},
- {0x0B4E, 0x0B55}, {0x0B58, 0x0B5B}, {0x0B5E, 0x0B5E},
- {0x0B64, 0x0B65}, {0x0B78, 0x0B81}, {0x0B84, 0x0B84},
- {0x0B8B, 0x0B8D}, {0x0B91, 0x0B91}, {0x0B96, 0x0B98},
- {0x0B9B, 0x0B9B}, {0x0B9D, 0x0B9D}, {0x0BA0, 0x0BA2},
- {0x0BA5, 0x0BA7}, {0x0BAB, 0x0BAD}, {0x0BBA, 0x0BBD},
- {0x0BC3, 0x0BC5}, {0x0BC9, 0x0BC9}, {0x0BCE, 0x0BCF},
- {0x0BD1, 0x0BD6}, {0x0BD8, 0x0BE5}, {0x0BFB, 0x0BFF},
- {0x0C04, 0x0C04}, {0x0C0D, 0x0C0D}, {0x0C11, 0x0C11},
- {0x0C29, 0x0C29}, {0x0C3A, 0x0C3C}, {0x0C45, 0x0C45},
- {0x0C49, 0x0C49}, {0x0C4E, 0x0C54}, {0x0C57, 0x0C57},
- {0x0C5B, 0x0C5F}, {0x0C64, 0x0C65}, {0x0C70, 0x0C77},
- {0x0C84, 0x0C84}, {0x0C8D, 0x0C8D}, {0x0C91, 0x0C91},
- {0x0CA9, 0x0CA9}, {0x0CB4, 0x0CB4}, {0x0CBA, 0x0CBB},
- {0x0CC5, 0x0CC5}, {0x0CC9, 0x0CC9}, {0x0CCE, 0x0CD4},
- {0x0CD7, 0x0CDD}, {0x0CDF, 0x0CDF}, {0x0CE4, 0x0CE5},
- {0x0CF0, 0x0CF0}, {0x0CF3, 0x0D00}, {0x0D04, 0x0D04},
- {0x0D0D, 0x0D0D}, {0x0D11, 0x0D11}, {0x0D3B, 0x0D3C},
- {0x0D45, 0x0D45}, {0x0D49, 0x0D49}, {0x0D50, 0x0D53},
- {0x0D64, 0x0D65}, {0x0D80, 0x0D81}, {0x0D84, 0x0D84},
- {0x0D97, 0x0D99}, {0x0DB2, 0x0DB2}, {0x0DBC, 0x0DBC},
- {0x0DBE, 0x0DBF}, {0x0DC7, 0x0DC9}, {0x0DCB, 0x0DCE},
- {0x0DD5, 0x0DD5}, {0x0DD7, 0x0DD7}, {0x0DE0, 0x0DE5},
- {0x0DF0, 0x0DF1}, {0x0DF5, 0x0E00}, {0x0E3B, 0x0E3E},
- {0x0E5C, 0x0E80}, {0x0E83, 0x0E83}, {0x0E85, 0x0E86},
- {0x0E89, 0x0E89}, {0x0E8B, 0x0E8C}, {0x0E8E, 0x0E93},
- {0x0E98, 0x0E98}, {0x0EA0, 0x0EA0}, {0x0EA4, 0x0EA4},
- {0x0EA6, 0x0EA6}, {0x0EA8, 0x0EA9}, {0x0EAC, 0x0EAC},
- {0x0EBA, 0x0EBA}, {0x0EBE, 0x0EBF}, {0x0EC5, 0x0EC5},
- {0x0EC7, 0x0EC7}, {0x0ECE, 0x0ECF}, {0x0EDA, 0x0EDB},
- {0x0EE0, 0x0EFF}, {0x0F48, 0x0F48}, {0x0F6D, 0x0F70},
- {0x0F98, 0x0F98}, {0x0FBD, 0x0FBD}, {0x0FCD, 0x0FCD},
- {0x0FDB, 0x0FFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC},
- {0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F},
- {0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F},
- {0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1},
- {0x12B6, 0x12B7}, {0x12BF, 0x12BF}, {0x12C1, 0x12C1},
- {0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311},
- {0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F},
- {0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF},
- {0x169D, 0x169F}, {0x16F9, 0x16FF}, {0x170D, 0x170D},
- {0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F},
- {0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F},
- {0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF},
- {0x180F, 0x180F}, {0x181A, 0x181F}, {0x1878, 0x187F},
- {0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F},
- {0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943},
- {0x196E, 0x196F}, {0x1975, 0x197F}, {0x19AC, 0x19AF},
- {0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D},
- {0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F},
- {0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1ABF, 0x1AFF},
- {0x1B4C, 0x1B4F}, {0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB},
- {0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1CBF},
- {0x1CC8, 0x1CCF}, {0x1CF7, 0x1CF7}, {0x1CFA, 0x1CFF},
- {0x1DF6, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F},
- {0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58},
- {0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E},
- {0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5},
- {0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC}, {0x1FF0, 0x1FF1},
- {0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x2065, 0x2065},
- {0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F},
- {0x20BF, 0x20CF}, {0x20F1, 0x20FF}, {0x218C, 0x218F},
- {0x23FF, 0x23FF}, {0x2427, 0x243F}, {0x244B, 0x245F},
- {0x2B74, 0x2B75}, {0x2B96, 0x2B97}, {0x2BBA, 0x2BBC},
- {0x2BC9, 0x2BC9}, {0x2BD2, 0x2BEB}, {0x2BF0, 0x2BFF},
- {0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8},
- {0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F},
- {0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F},
- {0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7},
- {0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF},
- {0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF}, {0x2E45, 0x2E7F},
- {0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF},
- {0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098},
- {0x3100, 0x3104}, {0x312E, 0x3130}, {0x318F, 0x318F},
- {0x31BB, 0x31BF}, {0x31E4, 0x31EF}, {0x321F, 0x321F},
- {0x32FF, 0x32FF}, {0x4DB6, 0x4DBF}, {0x9FD6, 0x9FFF},
- {0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F},
- {0xA6F8, 0xA6FF}, {0xA7AF, 0xA7AF}, {0xA7B8, 0xA7F6},
- {0xA82C, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F},
- {0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA8FE, 0xA8FF},
- {0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE},
- {0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F},
- {0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA},
- {0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10},
- {0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F},
- {0xAB66, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF},
- {0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA}, {0xD7FC, 0xD7FF},
- {0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12},
- {0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D},
- {0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45},
- {0xFBC2, 0xFBD2}, {0xFD40, 0xFD4F}, {0xFD90, 0xFD91},
- {0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F},
- {0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F},
- {0xFE75, 0xFE75}, {0xFEFD, 0xFEFE}, {0xFF00, 0xFF00},
- {0xFFBF, 0xFFC1}, {0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1},
- {0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7},
- {0xFFEF, 0xFFF8}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C},
- {0x10027, 0x10027}, {0x1003B, 0x1003B}, {0x1003E, 0x1003E},
- {0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF},
- {0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F},
- {0x1019C, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F},
- {0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF},
- {0x10324, 0x1032F}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F},
- {0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF},
- {0x1049E, 0x1049F}, {0x104AA, 0x104AF}, {0x104D4, 0x104D7},
- {0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E},
- {0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F},
- {0x10768, 0x107FF}, {0x10806, 0x10807}, {0x10809, 0x10809},
- {0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E},
- {0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF},
- {0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E},
- {0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB},
- {0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B},
- {0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A34, 0x10A37},
- {0x10A3B, 0x10A3E}, {0x10A48, 0x10A4F}, {0x10A59, 0x10A5F},
- {0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF},
- {0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77},
- {0x10B92, 0x10B98}, {0x10B9D, 0x10BA8}, {0x10BB0, 0x10BFF},
- {0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9},
- {0x10D00, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1104E, 0x11051},
- {0x11070, 0x1107E}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF},
- {0x110FA, 0x110FF}, {0x11135, 0x11135}, {0x11144, 0x1114F},
- {0x11177, 0x1117F}, {0x111CE, 0x111CF}, {0x111E0, 0x111E0},
- {0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F},
- {0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E},
- {0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF},
- {0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E},
- {0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331},
- {0x11334, 0x11334}, {0x1133A, 0x1133B}, {0x11345, 0x11346},
- {0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356},
- {0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F},
- {0x11375, 0x113FF}, {0x1145A, 0x1145A}, {0x1145C, 0x1145C},
- {0x1145E, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F},
- {0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F},
- {0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B8, 0x116BF},
- {0x116CA, 0x116FF}, {0x1171A, 0x1171C}, {0x1172C, 0x1172F},
- {0x11740, 0x1189F}, {0x118F3, 0x118FE}, {0x11900, 0x11ABF},
- {0x11AF9, 0x11BFF}, {0x11C09, 0x11C09}, {0x11C37, 0x11C37},
- {0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91},
- {0x11CA8, 0x11CA8}, {0x11CB7, 0x11FFF}, {0x1239A, 0x123FF},
- {0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF},
- {0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F},
- {0x16A5F, 0x16A5F}, {0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF},
- {0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F},
- {0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C},
- {0x16B90, 0x16EFF}, {0x16F45, 0x16F4F}, {0x16F7F, 0x16F8E},
- {0x16FA0, 0x16FDF}, {0x16FE1, 0x16FFF}, {0x187ED, 0x187FF},
- {0x18AF3, 0x1AFFF}, {0x1B002, 0x1BBFF}, {0x1BC6B, 0x1BC6F},
- {0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B},
- {0x1BCA4, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128},
- {0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2FF}, {0x1D357, 0x1D35F},
- {0x1D372, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D},
- {0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8},
- {0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC},
- {0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C},
- {0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A},
- {0x1D53F, 0x1D53F}, {0x1D545, 0x1D545}, {0x1D547, 0x1D549},
- {0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD},
- {0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF},
- {0x1E007, 0x1E007}, {0x1E019, 0x1E01A}, {0x1E022, 0x1E022},
- {0x1E025, 0x1E025}, {0x1E02B, 0x1E7FF}, {0x1E8C5, 0x1E8C6},
- {0x1E8D7, 0x1E8FF}, {0x1E94B, 0x1E94F}, {0x1E95A, 0x1E95D},
- {0x1E960, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20},
- {0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26}, {0x1EE28, 0x1EE28},
- {0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A},
- {0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48},
- {0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50},
- {0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58},
- {0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E},
- {0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66},
- {0x1EE6B, 0x1EE6B}, {0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78},
- {0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A},
- {0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA},
- {0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF}, {0x1F02C, 0x1F02F},
- {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0},
- {0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F10D, 0x1F10F},
- {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F16F}, {0x1F1AD, 0x1F1E5},
- {0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F},
- {0x1F252, 0x1F2FF}, {0x1F6D3, 0x1F6DF}, {0x1F6ED, 0x1F6EF},
- {0x1F6F7, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF},
- {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F},
- {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F90F}, {0x1F91F, 0x1F91F},
- {0x1F928, 0x1F92F}, {0x1F931, 0x1F932}, {0x1F93F, 0x1F93F},
- {0x1F94C, 0x1F94F}, {0x1F95F, 0x1F97F}, {0x1F992, 0x1F9BF},
- {0x1F9C1, 0x1FFFF}, {0x2A6D7, 0x2A6FF}, {0x2B735, 0x2B73F},
- {0x2B81E, 0x2B81F}, {0x2CEA2, 0x2F7FF}, {0x2FA1E, 0xE0000},
- {0xE0002, 0xE001F}, {0xE0080, 0xE00FF}, {0xE01F0, 0xEFFFF},
- {0xFFFFE, 0xFFFFF},
-}
-
-var neutral = table{
- {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9},
- {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB},
- {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6},
- {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7},
- {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1},
- {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD},
- {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112},
- {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A},
- {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E},
- {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C},
- {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A},
- {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1},
- {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7},
- {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250},
- {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6},
- {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF},
- {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE},
- {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F},
- {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390},
- {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400},
- {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F},
- {0x0531, 0x0556}, {0x0559, 0x055F}, {0x0561, 0x0587},
- {0x0589, 0x058A}, {0x058D, 0x058F}, {0x0591, 0x05C7},
- {0x05D0, 0x05EA}, {0x05F0, 0x05F4}, {0x0600, 0x061C},
- {0x061E, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1},
- {0x07C0, 0x07FA}, {0x0800, 0x082D}, {0x0830, 0x083E},
- {0x0840, 0x085B}, {0x085E, 0x085E}, {0x08A0, 0x08B4},
- {0x08B6, 0x08BD}, {0x08D4, 0x0983}, {0x0985, 0x098C},
- {0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0},
- {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4},
- {0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7},
- {0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FB},
- {0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10},
- {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33},
- {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C},
- {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
- {0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E},
- {0x0A66, 0x0A75}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D},
- {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0},
- {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5},
- {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0},
- {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AF9},
- {0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10},
- {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33},
- {0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48},
- {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B5C, 0x0B5D},
- {0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83},
- {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95},
- {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F},
- {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9},
- {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD},
- {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA},
- {0x0C00, 0x0C03}, {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10},
- {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3D, 0x0C44},
- {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56},
- {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F},
- {0x0C78, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90},
- {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9},
- {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD},
- {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3},
- {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D01, 0x0D03},
- {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A},
- {0x0D3D, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F},
- {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D82, 0x0D83},
- {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB},
- {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA},
- {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF},
- {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A},
- {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84},
- {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, {0x0E8D, 0x0E8D},
- {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, {0x0EA1, 0x0EA3},
- {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EA7}, {0x0EAA, 0x0EAB},
- {0x0EAD, 0x0EB9}, {0x0EBB, 0x0EBD}, {0x0EC0, 0x0EC4},
- {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9},
- {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C},
- {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC},
- {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7},
- {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248},
- {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258},
- {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
- {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE},
- {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
- {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
- {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5},
- {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8},
- {0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736},
- {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770},
- {0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9},
- {0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819},
- {0x1820, 0x1877}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
- {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B},
- {0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974},
- {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
- {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C},
- {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD},
- {0x1AB0, 0x1ABE}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C},
- {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
- {0x1C4D, 0x1C88}, {0x1CC0, 0x1CC7}, {0x1CD0, 0x1CF6},
- {0x1CF8, 0x1CF9}, {0x1D00, 0x1DF5}, {0x1DFB, 0x1F15},
- {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
- {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B},
- {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4},
- {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB},
- {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
- {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017},
- {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023},
- {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034},
- {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064},
- {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080},
- {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8},
- {0x20AA, 0x20AB}, {0x20AD, 0x20BE}, {0x20D0, 0x20F0},
- {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108},
- {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120},
- {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152},
- {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F},
- {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7},
- {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6},
- {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206},
- {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210},
- {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C},
- {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226},
- {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B},
- {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251},
- {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269},
- {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285},
- {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4},
- {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319},
- {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF},
- {0x23F1, 0x23F2}, {0x23F4, 0x23FE}, {0x2400, 0x2426},
- {0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F},
- {0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F},
- {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5},
- {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5},
- {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1},
- {0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604},
- {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613},
- {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F},
- {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F},
- {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B},
- {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692},
- {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9},
- {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2},
- {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709},
- {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B},
- {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756},
- {0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF},
- {0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984},
- {0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54},
- {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2BB9},
- {0x2BBD, 0x2BC8}, {0x2BCA, 0x2BD1}, {0x2BEC, 0x2BEF},
- {0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2CF3},
- {0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D},
- {0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96},
- {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6},
- {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE},
- {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E44},
- {0x303F, 0x303F}, {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B},
- {0xA640, 0xA6F7}, {0xA700, 0xA7AE}, {0xA7B0, 0xA7B7},
- {0xA7F7, 0xA82B}, {0xA830, 0xA839}, {0xA840, 0xA877},
- {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA8FD},
- {0xA900, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD},
- {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
- {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
- {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E},
- {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E},
- {0xAB30, 0xAB65}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
- {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF},
- {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
- {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41},
- {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F},
- {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD},
- {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC},
- {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B},
- {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D},
- {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
- {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E},
- {0x10190, 0x1019B}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD},
- {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB},
- {0x10300, 0x10323}, {0x10330, 0x1034A}, {0x10350, 0x1037A},
- {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
- {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3},
- {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563},
- {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755},
- {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808},
- {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C},
- {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF},
- {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B},
- {0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7},
- {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06},
- {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A33},
- {0x10A38, 0x10A3A}, {0x10A3F, 0x10A47}, {0x10A50, 0x10A58},
- {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6},
- {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72},
- {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF},
- {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2},
- {0x10CFA, 0x10CFF}, {0x10E60, 0x10E7E}, {0x11000, 0x1104D},
- {0x11052, 0x1106F}, {0x1107F, 0x110C1}, {0x110D0, 0x110E8},
- {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11143},
- {0x11150, 0x11176}, {0x11180, 0x111CD}, {0x111D0, 0x111DF},
- {0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x1123E},
- {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D},
- {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA},
- {0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C},
- {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330},
- {0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133C, 0x11344},
- {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350},
- {0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C},
- {0x11370, 0x11374}, {0x11400, 0x11459}, {0x1145B, 0x1145B},
- {0x1145D, 0x1145D}, {0x11480, 0x114C7}, {0x114D0, 0x114D9},
- {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644},
- {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B7},
- {0x116C0, 0x116C9}, {0x11700, 0x11719}, {0x1171D, 0x1172B},
- {0x11730, 0x1173F}, {0x118A0, 0x118F2}, {0x118FF, 0x118FF},
- {0x11AC0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36},
- {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F},
- {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x12000, 0x12399},
- {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543},
- {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38},
- {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F},
- {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45},
- {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, {0x16B63, 0x16B77},
- {0x16B7D, 0x16B8F}, {0x16F00, 0x16F44}, {0x16F50, 0x16F7E},
- {0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C},
- {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3},
- {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8},
- {0x1D200, 0x1D245}, {0x1D300, 0x1D356}, {0x1D360, 0x1D371},
- {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F},
- {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC},
- {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3},
- {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514},
- {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E},
- {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550},
- {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B},
- {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006},
- {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
- {0x1E026, 0x1E02A}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
- {0x1E900, 0x1E94A}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F},
- {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22},
- {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32},
- {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B},
- {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49},
- {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52},
- {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59},
- {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F},
- {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A},
- {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C},
- {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B},
- {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB},
- {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B},
- {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF},
- {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C},
- {0x1F12E, 0x1F12E}, {0x1F16A, 0x1F16B}, {0x1F1E6, 0x1F1FF},
- {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D},
- {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF},
- {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F},
- {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A},
- {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594},
- {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F},
- {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6E0, 0x1F6EA},
- {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D4},
- {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859},
- {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0xE0001, 0xE0001},
- {0xE0020, 0xE007F},
-}
-
-// Condition have flag EastAsianWidth whether the current locale is CJK or not.
-type Condition struct {
- EastAsianWidth bool
- ZeroWidthJoiner bool
-}
-
-// NewCondition return new instance of Condition which is current locale.
-func NewCondition() *Condition {
- return &Condition{
- EastAsianWidth: EastAsianWidth,
- ZeroWidthJoiner: ZeroWidthJoiner,
- }
-}
-
-// RuneWidth returns the number of cells in r.
-// See http://www.unicode.org/reports/tr11/
-func (c *Condition) RuneWidth(r rune) int {
- switch {
- case r < 0 || r > 0x10FFFF ||
- inTables(r, nonprint, combining, notassigned):
- return 0
- case (c.EastAsianWidth && IsAmbiguousWidth(r)) ||
- inTables(r, doublewidth, emoji):
- return 2
- default:
- return 1
- }
-}
-
-func (c *Condition) stringWidth(s string) (width int) {
- for _, r := range []rune(s) {
- width += c.RuneWidth(r)
- }
- return width
-}
-
-func (c *Condition) stringWidthZeroJoiner(s string) (width int) {
- r1, r2 := rune(0), rune(0)
- for _, r := range []rune(s) {
- if r == 0xFE0E || r == 0xFE0F {
- continue
- }
- w := c.RuneWidth(r)
- if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) {
- w = 0
- }
- width += w
- r1, r2 = r2, r
- }
- return width
-}
-
-// StringWidth return width as you can see
-func (c *Condition) StringWidth(s string) (width int) {
- if c.ZeroWidthJoiner {
- return c.stringWidthZeroJoiner(s)
- }
- return c.stringWidth(s)
-}
-
-// Truncate return string truncated with w cells
-func (c *Condition) Truncate(s string, w int, tail string) string {
- if c.StringWidth(s) <= w {
- return s
- }
- r := []rune(s)
- tw := c.StringWidth(tail)
- w -= tw
- width := 0
- i := 0
- for ; i < len(r); i++ {
- cw := c.RuneWidth(r[i])
- if width+cw > w {
- break
- }
- width += cw
- }
- return string(r[0:i]) + tail
-}
-
-// Wrap return string wrapped with w cells
-func (c *Condition) Wrap(s string, w int) string {
- width := 0
- out := ""
- for _, r := range []rune(s) {
- cw := RuneWidth(r)
- if r == '\n' {
- out += string(r)
- width = 0
- continue
- } else if width+cw > w {
- out += "\n"
- width = 0
- out += string(r)
- width += cw
- continue
- }
- out += string(r)
- width += cw
- }
- return out
-}
-
-// FillLeft return string filled in left by spaces in w cells
-func (c *Condition) FillLeft(s string, w int) string {
- width := c.StringWidth(s)
- count := w - width
- if count > 0 {
- b := make([]byte, count)
- for i := range b {
- b[i] = ' '
- }
- return string(b) + s
- }
- return s
-}
-
-// FillRight return string filled in left by spaces in w cells
-func (c *Condition) FillRight(s string, w int) string {
- width := c.StringWidth(s)
- count := w - width
- if count > 0 {
- b := make([]byte, count)
- for i := range b {
- b[i] = ' '
- }
- return s + string(b)
- }
- return s
-}
-
-// RuneWidth returns the number of cells in r.
-// See http://www.unicode.org/reports/tr11/
-func RuneWidth(r rune) int {
- return DefaultCondition.RuneWidth(r)
-}
-
-// IsAmbiguousWidth returns whether is ambiguous width or not.
-func IsAmbiguousWidth(r rune) bool {
- return inTables(r, private, ambiguous)
-}
-
-// IsNeutralWidth returns whether is neutral width or not.
-func IsNeutralWidth(r rune) bool {
- return inTable(r, neutral)
-}
-
-// StringWidth return width as you can see
-func StringWidth(s string) (width int) {
- return DefaultCondition.StringWidth(s)
-}
-
-// Truncate return string truncated with w cells
-func Truncate(s string, w int, tail string) string {
- return DefaultCondition.Truncate(s, w, tail)
-}
-
-// Wrap return string wrapped with w cells
-func Wrap(s string, w int) string {
- return DefaultCondition.Wrap(s, w)
-}
-
-// FillLeft return string filled in left by spaces in w cells
-func FillLeft(s string, w int) string {
- return DefaultCondition.FillLeft(s, w)
-}
-
-// FillRight return string filled in left by spaces in w cells
-func FillRight(s string, w int) string {
- return DefaultCondition.FillRight(s, w)
-}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
deleted file mode 100644
index 7d99f6e52..000000000
--- a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// +build appengine
-
-package runewidth
-
-// IsEastAsian return true if the current locale is CJK
-func IsEastAsian() bool {
- return false
-}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go
deleted file mode 100644
index c5fdf40ba..000000000
--- a/vendor/github.com/mattn/go-runewidth/runewidth_js.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build js
-// +build !appengine
-
-package runewidth
-
-func IsEastAsian() bool {
- // TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
- return false
-}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go
deleted file mode 100644
index 66a58b5d8..000000000
--- a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// +build !windows
-// +build !js
-// +build !appengine
-
-package runewidth
-
-import (
- "os"
- "regexp"
- "strings"
-)
-
-var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
-
-var mblenTable = map[string]int{
- "utf-8": 6,
- "utf8": 6,
- "jis": 8,
- "eucjp": 3,
- "euckr": 2,
- "euccn": 2,
- "sjis": 2,
- "cp932": 2,
- "cp51932": 2,
- "cp936": 2,
- "cp949": 2,
- "cp950": 2,
- "big5": 2,
- "gbk": 2,
- "gb2312": 2,
-}
-
-func isEastAsian(locale string) bool {
- charset := strings.ToLower(locale)
- r := reLoc.FindStringSubmatch(locale)
- if len(r) == 2 {
- charset = strings.ToLower(r[1])
- }
-
- if strings.HasSuffix(charset, "@cjk_narrow") {
- return false
- }
-
- for pos, b := range []byte(charset) {
- if b == '@' {
- charset = charset[:pos]
- break
- }
- }
- max := 1
- if m, ok := mblenTable[charset]; ok {
- max = m
- }
- if max > 1 && (charset[0] != 'u' ||
- strings.HasPrefix(locale, "ja") ||
- strings.HasPrefix(locale, "ko") ||
- strings.HasPrefix(locale, "zh")) {
- return true
- }
- return false
-}
-
-// IsEastAsian return true if the current locale is CJK
-func IsEastAsian() bool {
- locale := os.Getenv("LC_CTYPE")
- if locale == "" {
- locale = os.Getenv("LANG")
- }
-
- // ignore C locale
- if locale == "POSIX" || locale == "C" {
- return false
- }
- if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
- return false
- }
-
- return isEastAsian(locale)
-}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go
deleted file mode 100644
index d6a61777d..000000000
--- a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// +build windows
-// +build !appengine
-
-package runewidth
-
-import (
- "syscall"
-)
-
-var (
- kernel32 = syscall.NewLazyDLL("kernel32")
- procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
-)
-
-// IsEastAsian return true if the current locale is CJK
-func IsEastAsian() bool {
- r1, _, _ := procGetConsoleOutputCP.Call()
- if r1 == 0 {
- return false
- }
-
- switch int(r1) {
- case 932, 51932, 936, 949, 950:
- return true
- }
-
- return false
-}
diff --git a/vendor/github.com/openshift/imagebuilder/README.md b/vendor/github.com/openshift/imagebuilder/README.md
index 2f9c110dd..f26b4a7e0 100644
--- a/vendor/github.com/openshift/imagebuilder/README.md
+++ b/vendor/github.com/openshift/imagebuilder/README.md
@@ -70,7 +70,7 @@ is ignored.
## Code Example
-```
+```go
f, err := os.Open("path/to/Dockerfile")
if err != nil {
return err
diff --git a/vendor/github.com/openshift/imagebuilder/builder.go b/vendor/github.com/openshift/imagebuilder/builder.go
index d37965df6..16682af7d 100644
--- a/vendor/github.com/openshift/imagebuilder/builder.go
+++ b/vendor/github.com/openshift/imagebuilder/builder.go
@@ -40,6 +40,7 @@ type Run struct {
type Executor interface {
Preserve(path string) error
+ EnsureContainerPath(path string) error
Copy(excludes []string, copies ...Copy) error
Run(run Run, config docker.Config) error
UnrecognizedInstruction(step *Step) error
@@ -52,6 +53,11 @@ func (logExecutor) Preserve(path string) error {
return nil
}
+func (logExecutor) EnsureContainerPath(path string) error {
+ log.Printf("ENSURE %s", path)
+ return nil
+}
+
func (logExecutor) Copy(excludes []string, copies ...Copy) error {
for _, c := range copies {
log.Printf("COPY %v -> %s (from:%s download:%t), chown: %s", c.Src, c.Dest, c.From, c.Download, c.Chown)
@@ -75,6 +81,10 @@ func (noopExecutor) Preserve(path string) error {
return nil
}
+func (noopExecutor) EnsureContainerPath(path string) error {
+ return nil
+}
+
func (noopExecutor) Copy(excludes []string, copies ...Copy) error {
return nil
}
@@ -153,6 +163,7 @@ func (stages Stages) ByName(name string) (Stage, bool) {
return Stage{}, false
}
+// Get just the target stage.
func (stages Stages) ByTarget(target string) (Stages, bool) {
if len(target) == 0 {
return stages, true
@@ -165,6 +176,19 @@ func (stages Stages) ByTarget(target string) (Stages, bool) {
return nil, false
}
+// Get all the stages up to and including the target.
+func (stages Stages) ThroughTarget(target string) (Stages, bool) {
+ if len(target) == 0 {
+ return stages, true
+ }
+ for i, stage := range stages {
+ if stage.Name == target {
+ return stages[0 : i+1], true
+ }
+ }
+ return nil, false
+}
+
type Stage struct {
Position int
Name string
@@ -319,6 +343,13 @@ func (b *Builder) Run(step *Step, exec Executor, noRunsRemaining bool) error {
if err := exec.Copy(b.Excludes, copies...); err != nil {
return err
}
+
+ if len(b.RunConfig.WorkingDir) > 0 {
+ if err := exec.EnsureContainerPath(b.RunConfig.WorkingDir); err != nil {
+ return err
+ }
+ }
+
for _, run := range runs {
config := b.Config()
config.Env = step.Env
diff --git a/vendor/github.com/openshift/imagebuilder/dispatchers.go b/vendor/github.com/openshift/imagebuilder/dispatchers.go
index f6510c2fd..ff365848a 100644
--- a/vendor/github.com/openshift/imagebuilder/dispatchers.go
+++ b/vendor/github.com/openshift/imagebuilder/dispatchers.go
@@ -128,9 +128,20 @@ func add(b *Builder, args []string, attributes map[string]bool, flagArgs []strin
if len(args) < 2 {
return errAtLeastOneArgument("ADD")
}
+ var chown string
last := len(args) - 1
dest := makeAbsolute(args[last], b.RunConfig.WorkingDir)
- b.PendingCopies = append(b.PendingCopies, Copy{Src: args[0:last], Dest: dest, Download: true})
+ if len(flagArgs) > 0 {
+ for _, arg := range flagArgs {
+ switch {
+ case strings.HasPrefix(arg, "--chown="):
+ chown = strings.TrimPrefix(arg, "--chown=")
+ default:
+ return fmt.Errorf("ADD only supports the --chown=<uid:gid> flag")
+ }
+ }
+ }
+ b.PendingCopies = append(b.PendingCopies, Copy{Src: args[0:last], Dest: dest, Download: true, Chown: chown})
return nil
}
diff --git a/vendor/github.com/vbauerster/mpb/LICENSE b/vendor/github.com/vbauerster/mpb/LICENSE
new file mode 100644
index 000000000..5e68ed21e
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (C) 2016-2018 Vladimir Bauer
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/vbauerster/mpb/README.md b/vendor/github.com/vbauerster/mpb/README.md
new file mode 100644
index 000000000..9b760647e
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/README.md
@@ -0,0 +1,117 @@
+# Multi Progress Bar
+
+[![GoDoc](https://godoc.org/github.com/vbauerster/mpb?status.svg)](https://godoc.org/github.com/vbauerster/mpb)
+[![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb)
+[![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb)
+[![codecov](https://codecov.io/gh/vbauerster/mpb/branch/master/graph/badge.svg)](https://codecov.io/gh/vbauerster/mpb)
+
+**mpb** is a Go lib for rendering progress bars in terminal applications.
+
+## Features
+
+* __Multiple Bars__: Multiple progress bars are supported
+* __Dynamic Total__: [Set total](https://github.com/vbauerster/mpb/issues/9#issuecomment-344448984) while bar is running
+* __Dynamic Add/Remove__: Dynamically add or remove bars
+* __Cancellation__: Cancel whole rendering process
+* __Predefined Decorators__: Elapsed time, [ewma](https://github.com/VividCortex/ewma) based ETA, Percentage, Bytes counter
+* __Decorator's width sync__: Synchronized decorator's width among multiple bars
+
+## Installation
+
+```sh
+go get github.com/vbauerster/mpb
+```
+
+_Note:_ it is preferable to go get from github.com, rather than gopkg.in. See issue [#11](https://github.com/vbauerster/mpb/issues/11).
+
+## Usage
+
+#### [Rendering single bar](examples/singleBar/main.go)
+```go
+ p := mpb.New(
+ // override default (80) width
+ mpb.WithWidth(64),
+ // override default "[=>-]" format
+ mpb.WithFormat("╢▌▌░╟"),
+ // override default 120ms refresh rate
+ mpb.WithRefreshRate(180*time.Millisecond),
+ )
+
+ total := 100
+ name := "Single Bar:"
+ // adding a single bar
+ bar := p.AddBar(int64(total),
+ mpb.PrependDecorators(
+ // display our name with one space on the right
+ decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
+ // replace ETA decorator with "done" message, OnComplete event
+ decor.OnComplete(
+ // ETA decorator with ewma age of 60, and width reservation of 4
+ decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WC{W: 4}), "done",
+ ),
+ ),
+ mpb.AppendDecorators(decor.Percentage()),
+ )
+ // simulating some work
+ max := 100 * time.Millisecond
+ for i := 0; i < total; i++ {
+ start := time.Now()
+ time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
+ // ewma based decorators require work duration measurement
+ bar.IncrBy(1, time.Since(start))
+ }
+ // wait for our bar to complete and flush
+ p.Wait()
+```
+
+#### [Rendering multiple bars](examples/simple/main.go)
+```go
+ var wg sync.WaitGroup
+ p := mpb.New(mpb.WithWaitGroup(&wg))
+ total, numBars := 100, 3
+ wg.Add(numBars)
+
+ for i := 0; i < numBars; i++ {
+ name := fmt.Sprintf("Bar#%d:", i)
+ bar := p.AddBar(int64(total),
+ mpb.PrependDecorators(
+ // simple name decorator
+ decor.Name(name),
+ // decor.DSyncWidth bit enables column width synchronization
+ decor.Percentage(decor.WCSyncSpace),
+ ),
+ mpb.AppendDecorators(
+ // replace ETA decorator with "done" message, OnComplete event
+ decor.OnComplete(
+ // ETA decorator with ewma age of 60
+ decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
+ ),
+ ),
+ )
+ // simulating some work
+ go func() {
+ defer wg.Done()
+ max := 100 * time.Millisecond
+ for i := 0; i < total; i++ {
+ start := time.Now()
+ time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
+ // ewma based decorators require work duration measurement
+ bar.IncrBy(1, time.Since(start))
+ }
+ }()
+ }
+ // wait for all bars to complete and flush
+ p.Wait()
+```
+
+#### [Dynamic total](examples/dynTotal/main.go)
+
+![dynamic total](examples/gifs/godEMrCZmJkHYH1X9dN4Nm0U7.svg)
+
+#### [Complex example](examples/complex/main.go)
+
+![complex](examples/gifs/wHzf1M7sd7B3zVa2scBMnjqRf.svg)
+
+#### [Bytes counters](examples/io/single/main.go)
+
+![byte counters](examples/gifs/hIpTa3A5rQz65ssiVuRJu87X6.svg)
diff --git a/vendor/github.com/vbauerster/mpb/bar.go b/vendor/github.com/vbauerster/mpb/bar.go
new file mode 100644
index 000000000..5a506fc84
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/bar.go
@@ -0,0 +1,455 @@
+package mpb
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strings"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "github.com/vbauerster/mpb/decor"
+ "github.com/vbauerster/mpb/internal"
+)
+
+const (
+ rLeft = iota
+ rFill
+ rTip
+ rEmpty
+ rRight
+)
+
+const formatLen = 5
+
+type barRunes [formatLen]rune
+
+// Bar represents a progress Bar
+type Bar struct {
+ priority int
+ index int
+
+ runningBar *Bar
+ cacheState *bState
+ operateState chan func(*bState)
+ int64Ch chan int64
+ boolCh chan bool
+ frameReaderCh chan *frameReader
+ syncTableCh chan [][]chan int
+
+ // done is closed by Bar's goroutine, after cacheState is written
+ done chan struct{}
+ // shutdown is closed from master Progress goroutine only
+ shutdown chan struct{}
+}
+
+type (
+ bState struct {
+ id int
+ width int
+ total int64
+ current int64
+ runes barRunes
+ trimLeftSpace bool
+ trimRightSpace bool
+ toComplete bool
+ removeOnComplete bool
+ barClearOnComplete bool
+ completeFlushed bool
+ aDecorators []decor.Decorator
+ pDecorators []decor.Decorator
+ amountReceivers []decor.AmountReceiver
+ shutdownListeners []decor.ShutdownListener
+ refill *refill
+ bufP, bufB, bufA *bytes.Buffer
+ bufNL *bytes.Buffer
+ panicMsg string
+ newLineExtendFn func(io.Writer, *decor.Statistics)
+
+ // following options are assigned to the *Bar
+ priority int
+ runningBar *Bar
+ }
+ refill struct {
+ char rune
+ till int64
+ }
+ frameReader struct {
+ io.Reader
+ extendedLines int
+ toShutdown bool
+ removeOnComplete bool
+ }
+)
+
+func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
+ if total <= 0 {
+ total = time.Now().Unix()
+ }
+
+ s := &bState{
+ id: id,
+ priority: id,
+ total: total,
+ }
+
+ for _, opt := range options {
+ if opt != nil {
+ opt(s)
+ }
+ }
+
+ s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
+ s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
+ s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))
+
+ b := &Bar{
+ priority: s.priority,
+ runningBar: s.runningBar,
+ operateState: make(chan func(*bState)),
+ int64Ch: make(chan int64),
+ boolCh: make(chan bool),
+ frameReaderCh: make(chan *frameReader, 1),
+ syncTableCh: make(chan [][]chan int),
+ done: make(chan struct{}),
+ shutdown: make(chan struct{}),
+ }
+
+ if b.runningBar != nil {
+ b.priority = b.runningBar.priority
+ }
+
+ if s.newLineExtendFn != nil {
+ s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
+ }
+
+ go b.serve(wg, s, cancel)
+ return b
+}
+
+// RemoveAllPrependers removes all prepend functions.
+func (b *Bar) RemoveAllPrependers() {
+ select {
+ case b.operateState <- func(s *bState) { s.pDecorators = nil }:
+ case <-b.done:
+ }
+}
+
+// RemoveAllAppenders removes all append functions.
+func (b *Bar) RemoveAllAppenders() {
+ select {
+ case b.operateState <- func(s *bState) { s.aDecorators = nil }:
+ case <-b.done:
+ }
+}
+
+// ProxyReader wraps r with metrics required for progress tracking.
+func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
+ if r == nil {
+ panic("expect io.Reader, got nil")
+ }
+ rc, ok := r.(io.ReadCloser)
+ if !ok {
+ rc = ioutil.NopCloser(r)
+ }
+ return &proxyReader{rc, b, time.Now()}
+}
+
+// ID returs id of the bar.
+func (b *Bar) ID() int {
+ select {
+ case b.operateState <- func(s *bState) { b.int64Ch <- int64(s.id) }:
+ return int(<-b.int64Ch)
+ case <-b.done:
+ return b.cacheState.id
+ }
+}
+
+// Current returns bar's current number, in other words sum of all increments.
+func (b *Bar) Current() int64 {
+ select {
+ case b.operateState <- func(s *bState) { b.int64Ch <- s.current }:
+ return <-b.int64Ch
+ case <-b.done:
+ return b.cacheState.current
+ }
+}
+
+// SetTotal sets total dynamically.
+// Set final to true, when total is known, it will trigger bar complete event.
+func (b *Bar) SetTotal(total int64, final bool) bool {
+ select {
+ case b.operateState <- func(s *bState) {
+ if total > 0 {
+ s.total = total
+ }
+ if final {
+ s.current = s.total
+ s.toComplete = true
+ }
+ }:
+ return true
+ case <-b.done:
+ return false
+ }
+}
+
+// SetRefill sets fill rune to r, up until n.
+func (b *Bar) SetRefill(n int, r rune) {
+ if n <= 0 {
+ return
+ }
+ b.operateState <- func(s *bState) {
+ s.refill = &refill{r, int64(n)}
+ }
+}
+
+// RefillBy is deprecated, use SetRefill
+func (b *Bar) RefillBy(n int, r rune) {
+ b.SetRefill(n, r)
+}
+
+// Increment is a shorthand for b.IncrBy(1).
+func (b *Bar) Increment() {
+ b.IncrBy(1)
+}
+
+// IncrBy increments progress bar by amount of n.
+// wdd is optional work duration i.e. time.Since(start),
+// which expected to be provided, if any ewma based decorator is used.
+func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
+ select {
+ case b.operateState <- func(s *bState) {
+ s.current += int64(n)
+ if s.current >= s.total {
+ s.current = s.total
+ s.toComplete = true
+ }
+ for _, ar := range s.amountReceivers {
+ ar.NextAmount(n, wdd...)
+ }
+ }:
+ case <-b.done:
+ }
+}
+
+// Completed reports whether the bar is in completed state.
+func (b *Bar) Completed() bool {
+ // omit select here, because primary usage of the method is for loop
+ // condition, like for !bar.Completed() {...}
+ // so when toComplete=true it is called once (at which time, the bar is still alive),
+ // then quits the loop and never suppose to be called afterwards.
+ return <-b.boolCh
+}
+
+func (b *Bar) wSyncTable() [][]chan int {
+ select {
+ case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
+ return <-b.syncTableCh
+ case <-b.done:
+ return b.cacheState.wSyncTable()
+ }
+}
+
+func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
+ defer wg.Done()
+ for {
+ select {
+ case op := <-b.operateState:
+ op(s)
+ case b.boolCh <- s.toComplete:
+ case <-cancel:
+ s.toComplete = true
+ cancel = nil
+ case <-b.shutdown:
+ b.cacheState = s
+ close(b.done)
+ for _, sl := range s.shutdownListeners {
+ sl.Shutdown()
+ }
+ return
+ }
+ }
+}
+
+func (b *Bar) render(debugOut io.Writer, tw int) {
+ select {
+ case b.operateState <- func(s *bState) {
+ defer func() {
+ // recovering if user defined decorator panics for example
+ if p := recover(); p != nil {
+ s.panicMsg = fmt.Sprintf("panic: %v", p)
+ fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg)
+ b.frameReaderCh <- &frameReader{
+ Reader: strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
+ toShutdown: true,
+ }
+ }
+ }()
+ r := s.draw(tw)
+ var extendedLines int
+ if s.newLineExtendFn != nil {
+ s.bufNL.Reset()
+ s.newLineExtendFn(s.bufNL, newStatistics(s))
+ extendedLines = countLines(s.bufNL.Bytes())
+ r = io.MultiReader(r, s.bufNL)
+ }
+ b.frameReaderCh <- &frameReader{
+ Reader: r,
+ extendedLines: extendedLines,
+ toShutdown: s.toComplete && !s.completeFlushed,
+ removeOnComplete: s.removeOnComplete,
+ }
+ s.completeFlushed = s.toComplete
+ }:
+ case <-b.done:
+ s := b.cacheState
+ r := s.draw(tw)
+ var extendedLines int
+ if s.newLineExtendFn != nil {
+ s.bufNL.Reset()
+ s.newLineExtendFn(s.bufNL, newStatistics(s))
+ extendedLines = countLines(s.bufNL.Bytes())
+ r = io.MultiReader(r, s.bufNL)
+ }
+ b.frameReaderCh <- &frameReader{
+ Reader: r,
+ extendedLines: extendedLines,
+ }
+ }
+}
+
+func (s *bState) draw(termWidth int) io.Reader {
+ defer s.bufA.WriteByte('\n')
+
+ if s.panicMsg != "" {
+ return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
+ }
+
+ stat := newStatistics(s)
+
+ for _, d := range s.pDecorators {
+ s.bufP.WriteString(d.Decor(stat))
+ }
+
+ for _, d := range s.aDecorators {
+ s.bufA.WriteString(d.Decor(stat))
+ }
+
+ prependCount := utf8.RuneCount(s.bufP.Bytes())
+ appendCount := utf8.RuneCount(s.bufA.Bytes())
+
+ if s.barClearOnComplete && s.completeFlushed {
+ return io.MultiReader(s.bufP, s.bufA)
+ }
+
+ s.fillBar(s.width)
+ barCount := utf8.RuneCount(s.bufB.Bytes())
+ totalCount := prependCount + barCount + appendCount
+ if spaceCount := 0; totalCount > termWidth {
+ if !s.trimLeftSpace {
+ spaceCount++
+ }
+ if !s.trimRightSpace {
+ spaceCount++
+ }
+ s.fillBar(termWidth - prependCount - appendCount - spaceCount)
+ }
+
+ return io.MultiReader(s.bufP, s.bufB, s.bufA)
+}
+
+func (s *bState) fillBar(width int) {
+ defer func() {
+ s.bufB.WriteRune(s.runes[rRight])
+ if !s.trimRightSpace {
+ s.bufB.WriteByte(' ')
+ }
+ }()
+
+ s.bufB.Reset()
+ if !s.trimLeftSpace {
+ s.bufB.WriteByte(' ')
+ }
+ s.bufB.WriteRune(s.runes[rLeft])
+ if width <= 2 {
+ return
+ }
+
+ // bar s.width without leftEnd and rightEnd runes
+ barWidth := width - 2
+
+ completedWidth := internal.Percentage(s.total, s.current, int64(barWidth))
+
+ if s.refill != nil {
+ till := internal.Percentage(s.total, s.refill.till, int64(barWidth))
+ // append refill rune
+ var i int64
+ for i = 0; i < till; i++ {
+ s.bufB.WriteRune(s.refill.char)
+ }
+ for i = till; i < completedWidth; i++ {
+ s.bufB.WriteRune(s.runes[rFill])
+ }
+ } else {
+ var i int64
+ for i = 0; i < completedWidth; i++ {
+ s.bufB.WriteRune(s.runes[rFill])
+ }
+ }
+
+ if completedWidth < int64(barWidth) && completedWidth > 0 {
+ _, size := utf8.DecodeLastRune(s.bufB.Bytes())
+ s.bufB.Truncate(s.bufB.Len() - size)
+ s.bufB.WriteRune(s.runes[rTip])
+ }
+
+ for i := completedWidth; i < int64(barWidth); i++ {
+ s.bufB.WriteRune(s.runes[rEmpty])
+ }
+}
+
+func (s *bState) wSyncTable() [][]chan int {
+ columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
+ var pCount int
+ for _, d := range s.pDecorators {
+ if ok, ch := d.Syncable(); ok {
+ columns = append(columns, ch)
+ pCount++
+ }
+ }
+ var aCount int
+ for _, d := range s.aDecorators {
+ if ok, ch := d.Syncable(); ok {
+ columns = append(columns, ch)
+ aCount++
+ }
+ }
+ table := make([][]chan int, 2)
+ table[0] = columns[0:pCount]
+ table[1] = columns[pCount : pCount+aCount : pCount+aCount]
+ return table
+}
+
+func newStatistics(s *bState) *decor.Statistics {
+ return &decor.Statistics{
+ ID: s.id,
+ Completed: s.completeFlushed,
+ Total: s.total,
+ Current: s.current,
+ }
+}
+
+func strToBarRunes(format string) (array barRunes) {
+ for i, n := 0, 0; len(format) > 0; i++ {
+ array[i], n = utf8.DecodeRuneInString(format)
+ format = format[n:]
+ }
+ return
+}
+
+func countLines(b []byte) int {
+ return bytes.Count(b, []byte("\n"))
+}
diff --git a/vendor/github.com/vbauerster/mpb/bar_option.go b/vendor/github.com/vbauerster/mpb/bar_option.go
new file mode 100644
index 000000000..e33bce4da
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/bar_option.go
@@ -0,0 +1,124 @@
+package mpb
+
+import (
+ "io"
+
+ "github.com/vbauerster/mpb/decor"
+)
+
+// BarOption is a function option which changes the default behavior of a bar,
+// if passed to p.AddBar(int64, ...BarOption)
+type BarOption func(*bState)
+
+// AppendDecorators let you inject decorators to the bar's right side
+func AppendDecorators(appenders ...decor.Decorator) BarOption {
+ return func(s *bState) {
+ for _, decorator := range appenders {
+ if ar, ok := decorator.(decor.AmountReceiver); ok {
+ s.amountReceivers = append(s.amountReceivers, ar)
+ }
+ if sl, ok := decorator.(decor.ShutdownListener); ok {
+ s.shutdownListeners = append(s.shutdownListeners, sl)
+ }
+ s.aDecorators = append(s.aDecorators, decorator)
+ }
+ }
+}
+
+// PrependDecorators let you inject decorators to the bar's left side
+func PrependDecorators(prependers ...decor.Decorator) BarOption {
+ return func(s *bState) {
+ for _, decorator := range prependers {
+ if ar, ok := decorator.(decor.AmountReceiver); ok {
+ s.amountReceivers = append(s.amountReceivers, ar)
+ }
+ if sl, ok := decorator.(decor.ShutdownListener); ok {
+ s.shutdownListeners = append(s.shutdownListeners, sl)
+ }
+ s.pDecorators = append(s.pDecorators, decorator)
+ }
+ }
+}
+
+// BarTrimLeft trims left side space of the bar
+func BarTrimLeft() BarOption {
+ return func(s *bState) {
+ s.trimLeftSpace = true
+ }
+}
+
+// BarTrimRight trims right space of the bar
+func BarTrimRight() BarOption {
+ return func(s *bState) {
+ s.trimRightSpace = true
+ }
+}
+
+// BarTrim trims both left and right spaces of the bar
+func BarTrim() BarOption {
+ return func(s *bState) {
+ s.trimLeftSpace = true
+ s.trimRightSpace = true
+ }
+}
+
+// BarID overwrites internal bar id
+func BarID(id int) BarOption {
+ return func(s *bState) {
+ s.id = id
+ }
+}
+
+// BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event.
+// If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared
+// and then whole bar line gets removed completely.
+func BarRemoveOnComplete() BarOption {
+ return func(s *bState) {
+ s.removeOnComplete = true
+ }
+}
+
+// BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete.
+// To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set.
+func BarReplaceOnComplete(runningBar *Bar) BarOption {
+ return func(s *bState) {
+ s.runningBar = runningBar
+ }
+}
+
+// BarClearOnComplete is a flag, if set will clear bar section on complete event.
+// If you need to remove a whole bar line, refer to BarRemoveOnComplete.
+func BarClearOnComplete() BarOption {
+ return func(s *bState) {
+ s.barClearOnComplete = true
+ }
+}
+
+// BarPriority sets bar's priority.
+// Zero is highest priority, i.e. bar will be on top.
+// If `BarReplaceOnComplete` option is supplied, this option is ignored.
+func BarPriority(priority int) BarOption {
+ return func(s *bState) {
+ s.priority = priority
+ }
+}
+
+// BarNewLineExtend takes user defined efn, which gets called each render cycle.
+// Any write to provided writer of efn, will appear on new line of respective bar.
+func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption {
+ return func(s *bState) {
+ s.newLineExtendFn = efn
+ }
+}
+
+func barWidth(w int) BarOption {
+ return func(s *bState) {
+ s.width = w
+ }
+}
+
+func barFormat(format string) BarOption {
+ return func(s *bState) {
+ s.runes = strToBarRunes(format)
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/cwriter/writer.go b/vendor/github.com/vbauerster/mpb/cwriter/writer.go
new file mode 100644
index 000000000..0b1470d4c
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/cwriter/writer.go
@@ -0,0 +1,78 @@
+package cwriter
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+
+ isatty "github.com/mattn/go-isatty"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+// ESC is the ASCII code for escape character
+const ESC = 27
+
+var NotATTY = errors.New("not a terminal")
+
+var (
+ cursorUp = fmt.Sprintf("%c[%dA", ESC, 1)
+ clearLine = fmt.Sprintf("%c[2K\r", ESC)
+ clearCursorAndLine = cursorUp + clearLine
+)
+
+// Writer is a buffered the writer that updates the terminal.
+// The contents of writer will be flushed when Flush is called.
+type Writer struct {
+ out io.Writer
+ buf bytes.Buffer
+ isTerminal bool
+ fd int
+ lineCount int
+}
+
+// New returns a new Writer with defaults
+func New(out io.Writer) *Writer {
+ w := &Writer{out: out}
+ if f, ok := out.(*os.File); ok {
+ fd := f.Fd()
+ w.isTerminal = isatty.IsTerminal(fd)
+ w.fd = int(fd)
+ }
+ return w
+}
+
+// Flush flushes the underlying buffer
+func (w *Writer) Flush(lineCount int) error {
+ err := w.clearLines()
+ w.lineCount = lineCount
+ // WriteTo takes care of w.buf.Reset
+ if _, e := w.buf.WriteTo(w.out); err == nil {
+ err = e
+ }
+ return err
+}
+
+// Write appends the contents of p to the underlying buffer
+func (w *Writer) Write(p []byte) (n int, err error) {
+ return w.buf.Write(p)
+}
+
+// WriteString writes string to the underlying buffer
+func (w *Writer) WriteString(s string) (n int, err error) {
+ return w.buf.WriteString(s)
+}
+
+// ReadFrom reads from the provided io.Reader and writes to the underlying buffer.
+func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
+ return w.buf.ReadFrom(r)
+}
+
+func (w *Writer) GetWidth() (int, error) {
+ if w.isTerminal {
+ tw, _, err := terminal.GetSize(w.fd)
+ return tw, err
+ }
+ return -1, NotATTY
+}
diff --git a/vendor/github.com/vbauerster/mpb/cwriter/writer_posix.go b/vendor/github.com/vbauerster/mpb/cwriter/writer_posix.go
new file mode 100644
index 000000000..05e31c480
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/cwriter/writer_posix.go
@@ -0,0 +1,13 @@
+// +build !windows
+
+package cwriter
+
+import (
+ "io"
+ "strings"
+)
+
+func (w *Writer) clearLines() error {
+ _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount))
+ return err
+}
diff --git a/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go b/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go
new file mode 100644
index 000000000..dad7f50b2
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/cwriter/writer_windows.go
@@ -0,0 +1,77 @@
+// +build windows
+
+package cwriter
+
+import (
+ "io"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "github.com/mattn/go-isatty"
+)
+
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
+
+var (
+ procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
+ procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
+ procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
+ procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
+)
+
+type (
+ short int16
+ word uint16
+ dword uint32
+
+ coord struct {
+ x short
+ y short
+ }
+ smallRect struct {
+ left short
+ top short
+ right short
+ bottom short
+ }
+ consoleScreenBufferInfo struct {
+ size coord
+ cursorPosition coord
+ attributes word
+ window smallRect
+ maximumWindowSize coord
+ }
+)
+
+// FdWriter is a writer with a file descriptor.
+type FdWriter interface {
+ io.Writer
+ Fd() uintptr
+}
+
+func (w *Writer) clearLines() error {
+ f, ok := w.out.(FdWriter)
+ if ok && !isatty.IsTerminal(f.Fd()) {
+ _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount))
+ return err
+ }
+ fd := f.Fd()
+ var info consoleScreenBufferInfo
+ procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&info)))
+
+ for i := 0; i < w.lineCount; i++ {
+ // move the cursor up
+ info.cursorPosition.y--
+ procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&info.cursorPosition))))
+ // clear the line
+ cursor := coord{
+ x: info.window.left,
+ y: info.window.top + info.cursorPosition.y,
+ }
+ var count, w dword
+ count = dword(info.size.x)
+ procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w)))
+ }
+ return nil
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/counters.go b/vendor/github.com/vbauerster/mpb/decor/counters.go
new file mode 100644
index 000000000..e4161dc4b
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/counters.go
@@ -0,0 +1,206 @@
+package decor
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+const (
+ _ = iota
+ KiB = 1 << (iota * 10)
+ MiB
+ GiB
+ TiB
+)
+
+const (
+ KB = 1000
+ MB = KB * 1000
+ GB = MB * 1000
+ TB = GB * 1000
+)
+
+const (
+ _ = iota
+ UnitKiB
+ UnitKB
+)
+
+type CounterKiB int64
+
+func (c CounterKiB) Format(st fmt.State, verb rune) {
+ prec, ok := st.Precision()
+
+ if verb == 'd' || !ok {
+ prec = 0
+ }
+ if verb == 'f' && !ok {
+ prec = 6
+ }
+ // retain old beahavior if s verb used
+ if verb == 's' {
+ prec = 1
+ }
+
+ var res, unit string
+ switch {
+ case c >= TiB:
+ unit = "TiB"
+ res = strconv.FormatFloat(float64(c)/TiB, 'f', prec, 64)
+ case c >= GiB:
+ unit = "GiB"
+ res = strconv.FormatFloat(float64(c)/GiB, 'f', prec, 64)
+ case c >= MiB:
+ unit = "MiB"
+ res = strconv.FormatFloat(float64(c)/MiB, 'f', prec, 64)
+ case c >= KiB:
+ unit = "KiB"
+ res = strconv.FormatFloat(float64(c)/KiB, 'f', prec, 64)
+ default:
+ unit = "b"
+ res = strconv.FormatInt(int64(c), 10)
+ }
+
+ if st.Flag(' ') {
+ res += " "
+ }
+ res += unit
+
+ if w, ok := st.Width(); ok {
+ if len(res) < w {
+ pad := strings.Repeat(" ", w-len(res))
+ if st.Flag(int('-')) {
+ res += pad
+ } else {
+ res = pad + res
+ }
+ }
+ }
+
+ io.WriteString(st, res)
+}
+
+type CounterKB int64
+
+func (c CounterKB) Format(st fmt.State, verb rune) {
+ prec, ok := st.Precision()
+
+ if verb == 'd' || !ok {
+ prec = 0
+ }
+ if verb == 'f' && !ok {
+ prec = 6
+ }
+ // retain old beahavior if s verb used
+ if verb == 's' {
+ prec = 1
+ }
+
+ var res, unit string
+ switch {
+ case c >= TB:
+ unit = "TB"
+ res = strconv.FormatFloat(float64(c)/TB, 'f', prec, 64)
+ case c >= GB:
+ unit = "GB"
+ res = strconv.FormatFloat(float64(c)/GB, 'f', prec, 64)
+ case c >= MB:
+ unit = "MB"
+ res = strconv.FormatFloat(float64(c)/MB, 'f', prec, 64)
+ case c >= KB:
+ unit = "kB"
+ res = strconv.FormatFloat(float64(c)/KB, 'f', prec, 64)
+ default:
+ unit = "b"
+ res = strconv.FormatInt(int64(c), 10)
+ }
+
+ if st.Flag(' ') {
+ res += " "
+ }
+ res += unit
+
+ if w, ok := st.Width(); ok {
+ if len(res) < w {
+ pad := strings.Repeat(" ", w-len(res))
+ if st.Flag(int('-')) {
+ res += pad
+ } else {
+ res = pad + res
+ }
+ }
+ }
+
+ io.WriteString(st, res)
+}
+
+// CountersNoUnit is a wrapper around Counters with no unit param.
+func CountersNoUnit(pairFormat string, wcc ...WC) Decorator {
+ return Counters(0, pairFormat, wcc...)
+}
+
+// CountersKibiByte is a wrapper around Counters with predefined unit UnitKiB (bytes/1024).
+func CountersKibiByte(pairFormat string, wcc ...WC) Decorator {
+ return Counters(UnitKiB, pairFormat, wcc...)
+}
+
+// CountersKiloByte is a wrapper around Counters with predefined unit UnitKB (bytes/1000).
+func CountersKiloByte(pairFormat string, wcc ...WC) Decorator {
+ return Counters(UnitKB, pairFormat, wcc...)
+}
+
+// Counters decorator with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `pairFormat` printf compatible verbs for current and total, like "%f" or "%d"
+//
+// `wcc` optional WC config
+//
+// pairFormat example if UnitKB is chosen:
+//
+// "%.1f / %.1f" = "1.0MB / 12.0MB" or "% .1f / % .1f" = "1.0 MB / 12.0 MB"
+func Counters(unit int, pairFormat string, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &countersDecorator{
+ WC: wc,
+ unit: unit,
+ pairFormat: pairFormat,
+ }
+ return d
+}
+
+type countersDecorator struct {
+ WC
+ unit int
+ pairFormat string
+ completeMsg *string
+}
+
+func (d *countersDecorator) Decor(st *Statistics) string {
+ if st.Completed && d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+
+ var str string
+ switch d.unit {
+ case UnitKiB:
+ str = fmt.Sprintf(d.pairFormat, CounterKiB(st.Current), CounterKiB(st.Total))
+ case UnitKB:
+ str = fmt.Sprintf(d.pairFormat, CounterKB(st.Current), CounterKB(st.Total))
+ default:
+ str = fmt.Sprintf(d.pairFormat, st.Current, st.Total)
+ }
+
+ return d.FormatMsg(str)
+}
+
+func (d *countersDecorator) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/decorator.go b/vendor/github.com/vbauerster/mpb/decor/decorator.go
new file mode 100644
index 000000000..6aaf6c830
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/decorator.go
@@ -0,0 +1,144 @@
+package decor
+
+import (
+ "fmt"
+ "time"
+ "unicode/utf8"
+)
+
+const (
+ // DidentRight bit specifies identation direction.
+ // |foo |b | With DidentRight
+ // | foo| b| Without DidentRight
+ DidentRight = 1 << iota
+
+ // DextraSpace bit adds extra space, makes sense with DSyncWidth only.
+ // When DidentRight bit set, the space will be added to the right,
+ // otherwise to the left.
+ DextraSpace
+
+ // DSyncWidth bit enables same column width synchronization.
+ // Effective with multiple bars only.
+ DSyncWidth
+
+ // DSyncWidthR is shortcut for DSyncWidth|DidentRight
+ DSyncWidthR = DSyncWidth | DidentRight
+
+ // DSyncSpace is shortcut for DSyncWidth|DextraSpace
+ DSyncSpace = DSyncWidth | DextraSpace
+
+ // DSyncSpaceR is shortcut for DSyncWidth|DextraSpace|DidentRight
+ DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
+)
+
+const (
+ ET_STYLE_GO = iota
+ ET_STYLE_HHMMSS
+ ET_STYLE_HHMM
+ ET_STYLE_MMSS
+)
+
+// Statistics is a struct, which gets passed to a Decorator.
+type Statistics struct {
+ ID int
+ Completed bool
+ Total int64
+ Current int64
+}
+
+// Decorator interface.
+// A decorator must implement this interface, in order to be used with mpb library.
+type Decorator interface {
+ Decor(*Statistics) string
+ Syncable
+}
+
+// Syncable interface.
+// All decorators implement this interface implicitly.
+// Its Syncable method exposes width sync channel, if sync is enabled.
+type Syncable interface {
+ Syncable() (bool, chan int)
+}
+
+// OnCompleteMessenger interface.
+// Decorators implementing this interface suppose to return provided string on complete event.
+type OnCompleteMessenger interface {
+ OnCompleteMessage(string)
+}
+
+// AmountReceiver interface.
+// If decorator needs to receive increment amount,
+// so this is the right interface to implement.
+type AmountReceiver interface {
+ NextAmount(int, ...time.Duration)
+}
+
+// ShutdownListener interface.
+// If decorator needs to be notified once upon bar shutdown event,
+// so this is the right interface to implement.
+type ShutdownListener interface {
+ Shutdown()
+}
+
+// Global convenience shortcuts
+var (
+ WCSyncWidth = WC{C: DSyncWidth}
+ WCSyncWidthR = WC{C: DSyncWidthR}
+ WCSyncSpace = WC{C: DSyncSpace}
+ WCSyncSpaceR = WC{C: DSyncSpaceR}
+)
+
+// WC is a struct with two public fields W and C, both of int type.
+// W represents width and C represents bit set of width related config.
+type WC struct {
+ W int
+ C int
+ format string
+ wsync chan int
+}
+
+// FormatMsg formats final message according to WC.W and WC.C.
+// Should be called by any Decorator implementation.
+func (wc WC) FormatMsg(msg string) string {
+ if (wc.C & DSyncWidth) != 0 {
+ wc.wsync <- utf8.RuneCountInString(msg)
+ max := <-wc.wsync
+ if max == 0 {
+ max = wc.W
+ }
+ if (wc.C & DextraSpace) != 0 {
+ max++
+ }
+ return fmt.Sprintf(fmt.Sprintf(wc.format, max), msg)
+ }
+ return fmt.Sprintf(fmt.Sprintf(wc.format, wc.W), msg)
+}
+
+// Init initializes width related config.
+func (wc *WC) Init() {
+ wc.format = "%%"
+ if (wc.C & DidentRight) != 0 {
+ wc.format += "-"
+ }
+ wc.format += "%ds"
+ if (wc.C & DSyncWidth) != 0 {
+ wc.wsync = make(chan int)
+ }
+}
+
+func (wc *WC) Syncable() (bool, chan int) {
+ return (wc.C & DSyncWidth) != 0, wc.wsync
+}
+
+// OnComplete returns decorator, which wraps provided decorator, with sole
+// purpose to display provided message on complete event.
+//
+// `decorator` Decorator to wrap
+//
+// `message` message to display on complete event
+func OnComplete(decorator Decorator, message string) Decorator {
+ if d, ok := decorator.(OnCompleteMessenger); ok {
+ d.OnCompleteMessage(message)
+ }
+ return decorator
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/doc.go b/vendor/github.com/vbauerster/mpb/decor/doc.go
new file mode 100644
index 000000000..561a8677c
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/doc.go
@@ -0,0 +1,25 @@
+// Copyright (C) 2016-2018 Vladimir Bauer
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ Package decor contains common decorators used by "github.com/vbauerster/mpb" package.
+
+ Some decorators returned by this package might have a closure state. It is ok to use
+ decorators concurrently, unless you share the same decorator among multiple
+ *mpb.Bar instances. To avoid data races, create new decorator per *mpb.Bar instance.
+
+ Don't:
+
+ p := mpb.New()
+ name := decor.Name("bar")
+ p.AddBar(100, mpb.AppendDecorators(name))
+ p.AddBar(100, mpb.AppendDecorators(name))
+
+ Do:
+
+ p := mpb.New()
+ p.AddBar(100, mpb.AppendDecorators(decor.Name("bar1")))
+ p.AddBar(100, mpb.AppendDecorators(decor.Name("bar2")))
+*/
+package decor
diff --git a/vendor/github.com/vbauerster/mpb/decor/elapsed.go b/vendor/github.com/vbauerster/mpb/decor/elapsed.go
new file mode 100644
index 000000000..649d40a30
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/elapsed.go
@@ -0,0 +1,68 @@
+package decor
+
+import (
+ "fmt"
+ "time"
+)
+
+// Elapsed returns elapsed time decorator.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `wcc` optional WC config
+func Elapsed(style int, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &elapsedDecorator{
+ WC: wc,
+ style: style,
+ startTime: time.Now(),
+ }
+ return d
+}
+
+type elapsedDecorator struct {
+ WC
+ style int
+ startTime time.Time
+ msg string
+ completeMsg *string
+}
+
+func (d *elapsedDecorator) Decor(st *Statistics) string {
+ if st.Completed {
+ if d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+ return d.FormatMsg(d.msg)
+ }
+
+ timeElapsed := time.Since(d.startTime)
+ hours := int64((timeElapsed / time.Hour) % 60)
+ minutes := int64((timeElapsed / time.Minute) % 60)
+ seconds := int64((timeElapsed / time.Second) % 60)
+
+ switch d.style {
+ case ET_STYLE_GO:
+ d.msg = fmt.Sprint(time.Duration(timeElapsed.Seconds()) * time.Second)
+ case ET_STYLE_HHMMSS:
+ d.msg = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ case ET_STYLE_HHMM:
+ d.msg = fmt.Sprintf("%02d:%02d", hours, minutes)
+ case ET_STYLE_MMSS:
+ if hours > 0 {
+ d.msg = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ } else {
+ d.msg = fmt.Sprintf("%02d:%02d", minutes, seconds)
+ }
+ }
+
+ return d.FormatMsg(d.msg)
+}
+
+func (d *elapsedDecorator) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/eta.go b/vendor/github.com/vbauerster/mpb/decor/eta.go
new file mode 100644
index 000000000..44a1f03ea
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/eta.go
@@ -0,0 +1,207 @@
+package decor
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "github.com/VividCortex/ewma"
+ "github.com/vbauerster/mpb/internal"
+)
+
+type TimeNormalizer func(time.Duration) time.Duration
+
+// EwmaETA exponential-weighted-moving-average based ETA decorator.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `age` is the previous N samples to average over.
+//
+// `wcc` optional WC config
+func EwmaETA(style int, age float64, wcc ...WC) Decorator {
+ return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...)
+}
+
+// MovingAverageETA decorator relies on MovingAverage implementation to calculate its average.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `average` available implementations of MovingAverage [ewma.MovingAverage|NewMedian|NewMedianEwma]
+//
+// `normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
+//
+// `wcc` optional WC config
+func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &movingAverageETA{
+ WC: wc,
+ style: style,
+ average: average,
+ normalizer: normalizer,
+ }
+ return d
+}
+
+type movingAverageETA struct {
+ WC
+ style int
+ average ewma.MovingAverage
+ completeMsg *string
+ normalizer TimeNormalizer
+}
+
+func (d *movingAverageETA) Decor(st *Statistics) string {
+ if st.Completed && d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+
+ v := internal.Round(d.average.Value())
+ remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v)))
+ hours := int64((remaining / time.Hour) % 60)
+ minutes := int64((remaining / time.Minute) % 60)
+ seconds := int64((remaining / time.Second) % 60)
+
+ var str string
+ switch d.style {
+ case ET_STYLE_GO:
+ str = fmt.Sprint(time.Duration(remaining.Seconds()) * time.Second)
+ case ET_STYLE_HHMMSS:
+ str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ case ET_STYLE_HHMM:
+ str = fmt.Sprintf("%02d:%02d", hours, minutes)
+ case ET_STYLE_MMSS:
+ if hours > 0 {
+ str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ } else {
+ str = fmt.Sprintf("%02d:%02d", minutes, seconds)
+ }
+ }
+
+ return d.FormatMsg(str)
+}
+
+func (d *movingAverageETA) NextAmount(n int, wdd ...time.Duration) {
+ var workDuration time.Duration
+ for _, wd := range wdd {
+ workDuration = wd
+ }
+ lastItemEstimate := float64(workDuration) / float64(n)
+ if math.IsInf(lastItemEstimate, 0) || math.IsNaN(lastItemEstimate) {
+ return
+ }
+ d.average.Add(lastItemEstimate)
+}
+
+func (d *movingAverageETA) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
+
+// AverageETA decorator.
+//
+// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
+//
+// `wcc` optional WC config
+func AverageETA(style int, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &averageETA{
+ WC: wc,
+ style: style,
+ startTime: time.Now(),
+ }
+ return d
+}
+
+type averageETA struct {
+ WC
+ style int
+ startTime time.Time
+ completeMsg *string
+}
+
+func (d *averageETA) Decor(st *Statistics) string {
+ if st.Completed && d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+
+ var str string
+ timeElapsed := time.Since(d.startTime)
+ v := internal.Round(float64(timeElapsed) / float64(st.Current))
+ if math.IsInf(v, 0) || math.IsNaN(v) {
+ v = 0
+ }
+ remaining := time.Duration((st.Total - st.Current) * int64(v))
+ hours := int64((remaining / time.Hour) % 60)
+ minutes := int64((remaining / time.Minute) % 60)
+ seconds := int64((remaining / time.Second) % 60)
+
+ switch d.style {
+ case ET_STYLE_GO:
+ str = fmt.Sprint(time.Duration(remaining.Seconds()) * time.Second)
+ case ET_STYLE_HHMMSS:
+ str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ case ET_STYLE_HHMM:
+ str = fmt.Sprintf("%02d:%02d", hours, minutes)
+ case ET_STYLE_MMSS:
+ if hours > 0 {
+ str = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+ } else {
+ str = fmt.Sprintf("%02d:%02d", minutes, seconds)
+ }
+ }
+
+ return d.FormatMsg(str)
+}
+
+func (d *averageETA) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
+
+func MaxTolerateTimeNormalizer(maxTolerate time.Duration) TimeNormalizer {
+ var normalized time.Duration
+ var lastCall time.Time
+ return func(remaining time.Duration) time.Duration {
+ if diff := normalized - remaining; diff <= 0 || diff > maxTolerate || remaining < maxTolerate/2 {
+ normalized = remaining
+ lastCall = time.Now()
+ return remaining
+ }
+ normalized -= time.Since(lastCall)
+ lastCall = time.Now()
+ return normalized
+ }
+}
+
+func FixedIntervalTimeNormalizer(updInterval int) TimeNormalizer {
+ var normalized time.Duration
+ var lastCall time.Time
+ var count int
+ return func(remaining time.Duration) time.Duration {
+ if count == 0 || remaining <= time.Duration(15*time.Second) {
+ count = updInterval
+ normalized = remaining
+ lastCall = time.Now()
+ return remaining
+ }
+ count--
+ normalized -= time.Since(lastCall)
+ lastCall = time.Now()
+ if normalized > 0 {
+ return normalized
+ }
+ return remaining
+ }
+}
+
+func NopNormalizer() TimeNormalizer {
+ return func(remaining time.Duration) time.Duration {
+ return remaining
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/moving-average.go b/vendor/github.com/vbauerster/mpb/decor/moving-average.go
new file mode 100644
index 000000000..f9596a27f
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/moving-average.go
@@ -0,0 +1,66 @@
+package decor
+
+import (
+ "sort"
+
+ "github.com/VividCortex/ewma"
+)
+
+// MovingAverage is the interface that computes a moving average over a time-
+// series stream of numbers. The average may be over a window or exponentially
+// decaying.
+type MovingAverage interface {
+ Add(float64)
+ Value() float64
+ Set(float64)
+}
+
+type medianWindow [3]float64
+
+func (s *medianWindow) Len() int { return len(s) }
+func (s *medianWindow) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s *medianWindow) Less(i, j int) bool { return s[i] < s[j] }
+
+func (s *medianWindow) Add(value float64) {
+ s[0], s[1] = s[1], s[2]
+ s[2] = value
+}
+
+func (s *medianWindow) Value() float64 {
+ tmp := *s
+ sort.Sort(&tmp)
+ return tmp[1]
+}
+
+func (s *medianWindow) Set(value float64) {
+ for i := 0; i < len(s); i++ {
+ s[i] = value
+ }
+}
+
+// NewMedian is fixed last 3 samples median MovingAverage.
+func NewMedian() MovingAverage {
+ return new(medianWindow)
+}
+
+type medianEwma struct {
+ count uint
+ median MovingAverage
+ MovingAverage
+}
+
+func (s *medianEwma) Add(v float64) {
+ s.median.Add(v)
+ if s.count >= 2 {
+ s.MovingAverage.Add(s.median.Value())
+ }
+ s.count++
+}
+
+// NewMedianEwma is ewma based MovingAverage, which gets its values from median MovingAverage.
+func NewMedianEwma(age ...float64) MovingAverage {
+ return &medianEwma{
+ MovingAverage: ewma.NewMovingAverage(age...),
+ median: NewMedian(),
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/name.go b/vendor/github.com/vbauerster/mpb/decor/name.go
new file mode 100644
index 000000000..a5a5d1469
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/name.go
@@ -0,0 +1,45 @@
+package decor
+
+// StaticName returns name decorator.
+//
+// `name` string to display
+//
+// `wcc` optional WC config
+func StaticName(name string, wcc ...WC) Decorator {
+ return Name(name, wcc...)
+}
+
+// Name returns name decorator.
+//
+// `name` string to display
+//
+// `wcc` optional WC config
+func Name(name string, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &nameDecorator{
+ WC: wc,
+ msg: name,
+ }
+ return d
+}
+
+type nameDecorator struct {
+ WC
+ msg string
+ complete *string
+}
+
+func (d *nameDecorator) Decor(st *Statistics) string {
+ if st.Completed && d.complete != nil {
+ return d.FormatMsg(*d.complete)
+ }
+ return d.FormatMsg(d.msg)
+}
+
+func (d *nameDecorator) OnCompleteMessage(msg string) {
+ d.complete = &msg
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/percentage.go b/vendor/github.com/vbauerster/mpb/decor/percentage.go
new file mode 100644
index 000000000..078fbcf89
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/percentage.go
@@ -0,0 +1,39 @@
+package decor
+
+import (
+ "fmt"
+
+ "github.com/vbauerster/mpb/internal"
+)
+
+// Percentage returns percentage decorator.
+//
+// `wcc` optional WC config
+func Percentage(wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &percentageDecorator{
+ WC: wc,
+ }
+ return d
+}
+
+type percentageDecorator struct {
+ WC
+ completeMsg *string
+}
+
+func (d *percentageDecorator) Decor(st *Statistics) string {
+ if st.Completed && d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+ str := fmt.Sprintf("%d %%", internal.Percentage(st.Total, st.Current, 100))
+ return d.FormatMsg(str)
+}
+
+func (d *percentageDecorator) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
diff --git a/vendor/github.com/vbauerster/mpb/decor/speed.go b/vendor/github.com/vbauerster/mpb/decor/speed.go
new file mode 100644
index 000000000..395e5d04d
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/decor/speed.go
@@ -0,0 +1,270 @@
+package decor
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/VividCortex/ewma"
+)
+
+type SpeedKiB float64
+
+func (s SpeedKiB) Format(st fmt.State, verb rune) {
+ prec, ok := st.Precision()
+
+ if verb == 'd' || !ok {
+ prec = 0
+ }
+ if verb == 'f' && !ok {
+ prec = 6
+ }
+ // retain old beahavior if s verb used
+ if verb == 's' {
+ prec = 1
+ }
+
+ var res, unit string
+ switch {
+ case s >= TiB:
+ unit = "TiB/s"
+ res = strconv.FormatFloat(float64(s)/TiB, 'f', prec, 64)
+ case s >= GiB:
+ unit = "GiB/s"
+ res = strconv.FormatFloat(float64(s)/GiB, 'f', prec, 64)
+ case s >= MiB:
+ unit = "MiB/s"
+ res = strconv.FormatFloat(float64(s)/MiB, 'f', prec, 64)
+ case s >= KiB:
+ unit = "KiB/s"
+ res = strconv.FormatFloat(float64(s)/KiB, 'f', prec, 64)
+ default:
+ unit = "b/s"
+ res = strconv.FormatInt(int64(s), 10)
+ }
+
+ if st.Flag(' ') {
+ res += " "
+ }
+ res += unit
+
+ if w, ok := st.Width(); ok {
+ if len(res) < w {
+ pad := strings.Repeat(" ", w-len(res))
+ if st.Flag(int('-')) {
+ res += pad
+ } else {
+ res = pad + res
+ }
+ }
+ }
+
+ io.WriteString(st, res)
+}
+
+type SpeedKB float64
+
+func (s SpeedKB) Format(st fmt.State, verb rune) {
+ prec, ok := st.Precision()
+
+ if verb == 'd' || !ok {
+ prec = 0
+ }
+ if verb == 'f' && !ok {
+ prec = 6
+ }
+ // retain old beahavior if s verb used
+ if verb == 's' {
+ prec = 1
+ }
+
+ var res, unit string
+ switch {
+ case s >= TB:
+ unit = "TB/s"
+ res = strconv.FormatFloat(float64(s)/TB, 'f', prec, 64)
+ case s >= GB:
+ unit = "GB/s"
+ res = strconv.FormatFloat(float64(s)/GB, 'f', prec, 64)
+ case s >= MB:
+ unit = "MB/s"
+ res = strconv.FormatFloat(float64(s)/MB, 'f', prec, 64)
+ case s >= KB:
+ unit = "kB/s"
+ res = strconv.FormatFloat(float64(s)/KB, 'f', prec, 64)
+ default:
+ unit = "b/s"
+ res = strconv.FormatInt(int64(s), 10)
+ }
+
+ if st.Flag(' ') {
+ res += " "
+ }
+ res += unit
+
+ if w, ok := st.Width(); ok {
+ if len(res) < w {
+ pad := strings.Repeat(" ", w-len(res))
+ if st.Flag(int('-')) {
+ res += pad
+ } else {
+ res = pad + res
+ }
+ }
+ }
+
+ io.WriteString(st, res)
+}
+
+// EwmaSpeed exponential-weighted-moving-average based speed decorator,
+// with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `unitFormat` printf compatible verb for value, like "%f" or "%d"
+//
+// `average` MovingAverage implementation
+//
+// `wcc` optional WC config
+//
+// unitFormat example if UnitKiB is chosen:
+//
+// "%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
+func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator {
+ return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...)
+}
+
+// MovingAverageSpeed decorator relies on MovingAverage implementation to calculate its average.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `unitFormat` printf compatible verb for value, like "%f" or "%d"
+//
+// `average` MovingAverage implementation
+//
+// `wcc` optional WC config
+func MovingAverageSpeed(unit int, unitFormat string, average MovingAverage, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &movingAverageSpeed{
+ WC: wc,
+ unit: unit,
+ unitFormat: unitFormat,
+ average: average,
+ }
+ return d
+}
+
+type movingAverageSpeed struct {
+ WC
+ unit int
+ unitFormat string
+ average ewma.MovingAverage
+ msg string
+ completeMsg *string
+}
+
+func (d *movingAverageSpeed) Decor(st *Statistics) string {
+ if st.Completed {
+ if d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+ return d.FormatMsg(d.msg)
+ }
+
+ speed := d.average.Value()
+ switch d.unit {
+ case UnitKiB:
+ d.msg = fmt.Sprintf(d.unitFormat, SpeedKiB(speed))
+ case UnitKB:
+ d.msg = fmt.Sprintf(d.unitFormat, SpeedKB(speed))
+ default:
+ d.msg = fmt.Sprintf(d.unitFormat, speed)
+ }
+
+ return d.FormatMsg(d.msg)
+}
+
+func (s *movingAverageSpeed) NextAmount(n int, wdd ...time.Duration) {
+ var workDuration time.Duration
+ for _, wd := range wdd {
+ workDuration = wd
+ }
+ speed := float64(n) / workDuration.Seconds() / 1000
+ if math.IsInf(speed, 0) || math.IsNaN(speed) {
+ return
+ }
+ s.average.Add(speed)
+}
+
+func (d *movingAverageSpeed) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
+
+// AverageSpeed decorator with dynamic unit measure adjustment.
+//
+// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
+//
+// `unitFormat` printf compatible verb for value, like "%f" or "%d"
+//
+// `wcc` optional WC config
+//
+// unitFormat example if UnitKiB is chosen:
+//
+// "%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
+func AverageSpeed(unit int, unitFormat string, wcc ...WC) Decorator {
+ var wc WC
+ for _, widthConf := range wcc {
+ wc = widthConf
+ }
+ wc.Init()
+ d := &averageSpeed{
+ WC: wc,
+ unit: unit,
+ unitFormat: unitFormat,
+ startTime: time.Now(),
+ }
+ return d
+}
+
+type averageSpeed struct {
+ WC
+ unit int
+ unitFormat string
+ startTime time.Time
+ msg string
+ completeMsg *string
+}
+
+func (d *averageSpeed) Decor(st *Statistics) string {
+ if st.Completed {
+ if d.completeMsg != nil {
+ return d.FormatMsg(*d.completeMsg)
+ }
+ return d.FormatMsg(d.msg)
+ }
+
+ timeElapsed := time.Since(d.startTime)
+ speed := float64(st.Current) / timeElapsed.Seconds()
+
+ switch d.unit {
+ case UnitKiB:
+ d.msg = fmt.Sprintf(d.unitFormat, SpeedKiB(speed))
+ case UnitKB:
+ d.msg = fmt.Sprintf(d.unitFormat, SpeedKB(speed))
+ default:
+ d.msg = fmt.Sprintf(d.unitFormat, speed)
+ }
+
+ return d.FormatMsg(d.msg)
+}
+
+func (d *averageSpeed) OnCompleteMessage(msg string) {
+ d.completeMsg = &msg
+}
diff --git a/vendor/github.com/vbauerster/mpb/doc.go b/vendor/github.com/vbauerster/mpb/doc.go
new file mode 100644
index 000000000..16245956a
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/doc.go
@@ -0,0 +1,6 @@
+// Copyright (C) 2016-2018 Vladimir Bauer
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package mpb is a library for rendering progress bars in terminal applications.
+package mpb
diff --git a/vendor/github.com/vbauerster/mpb/internal/percentage.go b/vendor/github.com/vbauerster/mpb/internal/percentage.go
new file mode 100644
index 000000000..3c8defb7d
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/internal/percentage.go
@@ -0,0 +1,10 @@
+package internal
+
+// Percentage is a helper function, to calculate percentage.
+func Percentage(total, current, width int64) int64 {
+ if total <= 0 {
+ return 0
+ }
+ p := float64(width*current) / float64(total)
+ return int64(Round(p))
+}
diff --git a/vendor/github.com/vbauerster/mpb/internal/round.go b/vendor/github.com/vbauerster/mpb/internal/round.go
new file mode 100644
index 000000000..c54a789d2
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/internal/round.go
@@ -0,0 +1,49 @@
+package internal
+
+import "math"
+
+const (
+ uvone = 0x3FF0000000000000
+ mask = 0x7FF
+ shift = 64 - 11 - 1
+ bias = 1023
+ signMask = 1 << 63
+ fracMask = 1<<shift - 1
+)
+
+// Round returns the nearest integer, rounding half away from zero.
+//
+// Special cases are:
+// Round(±0) = ±0
+// Round(±Inf) = ±Inf
+// Round(NaN) = NaN
+func Round(x float64) float64 {
+ // Round is a faster implementation of:
+ //
+ // func Round(x float64) float64 {
+ // t := Trunc(x)
+ // if Abs(x-t) >= 0.5 {
+ // return t + Copysign(1, x)
+ // }
+ // return t
+ // }
+ bits := math.Float64bits(x)
+ e := uint(bits>>shift) & mask
+ if e < bias {
+ // Round abs(x) < 1 including denormals.
+ bits &= signMask // +-0
+ if e == bias-1 {
+ bits |= uvone // +-1
+ }
+ } else if e < bias+shift {
+ // Round any abs(x) >= 1 containing a fractional component [0,1).
+ //
+ // Numbers with larger exponents are returned unchanged since they
+ // must be either an integer, infinity, or NaN.
+ const half = 1 << (shift - 1)
+ e -= bias
+ bits += half >> e
+ bits &^= fracMask >> e
+ }
+ return math.Float64frombits(bits)
+}
diff --git a/vendor/github.com/vbauerster/mpb/options.go b/vendor/github.com/vbauerster/mpb/options.go
new file mode 100644
index 000000000..05d2ecf1f
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/options.go
@@ -0,0 +1,95 @@
+package mpb
+
+import (
+ "io"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "github.com/vbauerster/mpb/cwriter"
+)
+
+// ProgressOption is a function option which changes the default behavior of
+// progress pool, if passed to mpb.New(...ProgressOption)
+type ProgressOption func(*pState)
+
+// WithWaitGroup provides means to have a single joint point.
+// If *sync.WaitGroup is provided, you can safely call just p.Wait()
+// without calling Wait() on provided *sync.WaitGroup.
+// Makes sense when there are more than one bar to render.
+func WithWaitGroup(wg *sync.WaitGroup) ProgressOption {
+ return func(s *pState) {
+ s.uwg = wg
+ }
+}
+
+// WithWidth overrides default width 80
+func WithWidth(w int) ProgressOption {
+ return func(s *pState) {
+ if w >= 0 {
+ s.width = w
+ }
+ }
+}
+
+// WithFormat overrides default bar format "[=>-]"
+func WithFormat(format string) ProgressOption {
+ return func(s *pState) {
+ if utf8.RuneCountInString(format) == formatLen {
+ s.format = format
+ }
+ }
+}
+
+// WithRefreshRate overrides default 120ms refresh rate
+func WithRefreshRate(d time.Duration) ProgressOption {
+ return func(s *pState) {
+ if d < 10*time.Millisecond {
+ return
+ }
+ s.rr = d
+ }
+}
+
+// WithManualRefresh disables internal auto refresh time.Ticker.
+// Refresh will occur upon receive value from provided ch.
+func WithManualRefresh(ch <-chan time.Time) ProgressOption {
+ return func(s *pState) {
+ s.manualRefreshCh = ch
+ }
+}
+
+// WithCancel provide your cancel channel,
+// which you plan to close at some point.
+func WithCancel(ch <-chan struct{}) ProgressOption {
+ return func(s *pState) {
+ s.cancel = ch
+ }
+}
+
+// WithShutdownNotifier provided chanel will be closed, after all bars have been rendered.
+func WithShutdownNotifier(ch chan struct{}) ProgressOption {
+ return func(s *pState) {
+ s.shutdownNotifier = ch
+ }
+}
+
+// WithOutput overrides default output os.Stdout
+func WithOutput(w io.Writer) ProgressOption {
+ return func(s *pState) {
+ if w == nil {
+ return
+ }
+ s.cw = cwriter.New(w)
+ }
+}
+
+// WithDebugOutput sets debug output.
+func WithDebugOutput(w io.Writer) ProgressOption {
+ return func(s *pState) {
+ if w == nil {
+ return
+ }
+ s.debugOut = w
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/options_go1.7.go b/vendor/github.com/vbauerster/mpb/options_go1.7.go
new file mode 100644
index 000000000..ca9a5bad8
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/options_go1.7.go
@@ -0,0 +1,15 @@
+//+build go1.7
+
+package mpb
+
+import "context"
+
+// WithContext provided context will be used for cancellation purposes
+func WithContext(ctx context.Context) ProgressOption {
+ return func(s *pState) {
+ if ctx == nil {
+ panic("ctx must not be nil")
+ }
+ s.cancel = ctx.Done()
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/priority_queue.go b/vendor/github.com/vbauerster/mpb/priority_queue.go
new file mode 100644
index 000000000..7bc588c29
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/priority_queue.go
@@ -0,0 +1,40 @@
+package mpb
+
+import "container/heap"
+
+// A priorityQueue implements heap.Interface
+type priorityQueue []*Bar
+
+func (pq priorityQueue) Len() int { return len(pq) }
+
+func (pq priorityQueue) Less(i, j int) bool {
+ return pq[i].priority < pq[j].priority
+}
+
+func (pq priorityQueue) Swap(i, j int) {
+ pq[i], pq[j] = pq[j], pq[i]
+ pq[i].index = i
+ pq[j].index = j
+}
+
+func (pq *priorityQueue) Push(x interface{}) {
+ n := len(*pq)
+ bar := x.(*Bar)
+ bar.index = n
+ *pq = append(*pq, bar)
+}
+
+func (pq *priorityQueue) Pop() interface{} {
+ old := *pq
+ n := len(old)
+ bar := old[n-1]
+ bar.index = -1 // for safety
+ *pq = old[0 : n-1]
+ return bar
+}
+
+// update modifies the priority of a Bar in the queue.
+func (pq *priorityQueue) update(bar *Bar, priority int) {
+ bar.priority = priority
+ heap.Fix(pq, bar.index)
+}
diff --git a/vendor/github.com/vbauerster/mpb/progress.go b/vendor/github.com/vbauerster/mpb/progress.go
new file mode 100644
index 000000000..d95fe45b7
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/progress.go
@@ -0,0 +1,251 @@
+package mpb
+
+import (
+ "container/heap"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/vbauerster/mpb/cwriter"
+)
+
+const (
+ // default RefreshRate
+ prr = 120 * time.Millisecond
+ // default width
+ pwidth = 80
+ // default format
+ pformat = "[=>-]"
+)
+
+// Progress represents the container that renders Progress bars
+type Progress struct {
+ wg *sync.WaitGroup
+ uwg *sync.WaitGroup
+ operateState chan func(*pState)
+ done chan struct{}
+}
+
+type pState struct {
+ bHeap *priorityQueue
+ shutdownPending []*Bar
+ heapUpdated bool
+ zeroWait bool
+ idCounter int
+ width int
+ format string
+ rr time.Duration
+ cw *cwriter.Writer
+ pMatrix map[int][]chan int
+ aMatrix map[int][]chan int
+
+ // following are provided by user
+ uwg *sync.WaitGroup
+ manualRefreshCh <-chan time.Time
+ cancel <-chan struct{}
+ shutdownNotifier chan struct{}
+ waitBars map[*Bar]*Bar
+ debugOut io.Writer
+}
+
+// New creates new Progress instance, which orchestrates bars rendering process.
+// Accepts mpb.ProgressOption funcs for customization.
+func New(options ...ProgressOption) *Progress {
+ pq := make(priorityQueue, 0)
+ heap.Init(&pq)
+ s := &pState{
+ bHeap: &pq,
+ width: pwidth,
+ format: pformat,
+ cw: cwriter.New(os.Stdout),
+ rr: prr,
+ waitBars: make(map[*Bar]*Bar),
+ debugOut: ioutil.Discard,
+ }
+
+ for _, opt := range options {
+ if opt != nil {
+ opt(s)
+ }
+ }
+
+ p := &Progress{
+ uwg: s.uwg,
+ wg: new(sync.WaitGroup),
+ operateState: make(chan func(*pState)),
+ done: make(chan struct{}),
+ }
+ go p.serve(s)
+ return p
+}
+
+// AddBar creates a new progress bar and adds to the container.
+func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
+ p.wg.Add(1)
+ result := make(chan *Bar)
+ select {
+ case p.operateState <- func(s *pState) {
+ options = append(options, barWidth(s.width), barFormat(s.format))
+ b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
+ if b.runningBar != nil {
+ s.waitBars[b.runningBar] = b
+ } else {
+ heap.Push(s.bHeap, b)
+ s.heapUpdated = true
+ }
+ s.idCounter++
+ result <- b
+ }:
+ return <-result
+ case <-p.done:
+ p.wg.Done()
+ return nil
+ }
+}
+
+// Abort is only effective while bar progress is running,
+// it means remove bar now without waiting for its completion.
+// If bar is already completed, there is nothing to abort.
+// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
+func (p *Progress) Abort(b *Bar, remove bool) {
+ select {
+ case p.operateState <- func(s *pState) {
+ if b.index < 0 {
+ return
+ }
+ if remove {
+ s.heapUpdated = heap.Remove(s.bHeap, b.index) != nil
+ }
+ s.shutdownPending = append(s.shutdownPending, b)
+ }:
+ case <-p.done:
+ }
+}
+
+// UpdateBarPriority provides a way to change bar's order position.
+// Zero is highest priority, i.e. bar will be on top.
+func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
+ select {
+ case p.operateState <- func(s *pState) { s.bHeap.update(b, priority) }:
+ case <-p.done:
+ }
+}
+
+// BarCount returns bars count
+func (p *Progress) BarCount() int {
+ result := make(chan int, 1)
+ select {
+ case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
+ return <-result
+ case <-p.done:
+ return 0
+ }
+}
+
+// Wait first waits for user provided *sync.WaitGroup, if any,
+// then waits far all bars to complete and finally shutdowns master goroutine.
+// After this method has been called, there is no way to reuse *Progress instance.
+func (p *Progress) Wait() {
+ if p.uwg != nil {
+ p.uwg.Wait()
+ }
+
+ p.wg.Wait()
+
+ select {
+ case p.operateState <- func(s *pState) { s.zeroWait = true }:
+ <-p.done
+ case <-p.done:
+ }
+}
+
+func (s *pState) updateSyncMatrix() {
+ s.pMatrix = make(map[int][]chan int)
+ s.aMatrix = make(map[int][]chan int)
+ for i := 0; i < s.bHeap.Len(); i++ {
+ bar := (*s.bHeap)[i]
+ table := bar.wSyncTable()
+ pRow, aRow := table[0], table[1]
+
+ for i, ch := range pRow {
+ s.pMatrix[i] = append(s.pMatrix[i], ch)
+ }
+
+ for i, ch := range aRow {
+ s.aMatrix[i] = append(s.aMatrix[i], ch)
+ }
+ }
+}
+
+func (s *pState) render(tw int) {
+ if s.heapUpdated {
+ s.updateSyncMatrix()
+ s.heapUpdated = false
+ }
+ syncWidth(s.pMatrix)
+ syncWidth(s.aMatrix)
+
+ for i := 0; i < s.bHeap.Len(); i++ {
+ bar := (*s.bHeap)[i]
+ go bar.render(s.debugOut, tw)
+ }
+
+ if err := s.flush(s.bHeap.Len()); err != nil {
+ fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err)
+ }
+}
+
+func (s *pState) flush(lineCount int) error {
+ for s.bHeap.Len() > 0 {
+ bar := heap.Pop(s.bHeap).(*Bar)
+ frameReader := <-bar.frameReaderCh
+ defer func() {
+ if frameReader.toShutdown {
+ // shutdown at next flush, in other words decrement underlying WaitGroup
+ // only after the bar with completed state has been flushed.
+ // this ensures no bar ends up with less than 100% rendered.
+ s.shutdownPending = append(s.shutdownPending, bar)
+ if replacementBar, ok := s.waitBars[bar]; ok {
+ heap.Push(s.bHeap, replacementBar)
+ s.heapUpdated = true
+ delete(s.waitBars, bar)
+ }
+ if frameReader.removeOnComplete {
+ s.heapUpdated = true
+ return
+ }
+ }
+ heap.Push(s.bHeap, bar)
+ }()
+ s.cw.ReadFrom(frameReader)
+ lineCount += frameReader.extendedLines
+ }
+
+ for i := len(s.shutdownPending) - 1; i >= 0; i-- {
+ close(s.shutdownPending[i].shutdown)
+ s.shutdownPending = s.shutdownPending[:i]
+ }
+
+ return s.cw.Flush(lineCount)
+}
+
+func syncWidth(matrix map[int][]chan int) {
+ for _, column := range matrix {
+ column := column
+ go func() {
+ var maxWidth int
+ for _, ch := range column {
+ w := <-ch
+ if w > maxWidth {
+ maxWidth = w
+ }
+ }
+ for _, ch := range column {
+ ch <- maxWidth
+ }
+ }()
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/progress_posix.go b/vendor/github.com/vbauerster/mpb/progress_posix.go
new file mode 100644
index 000000000..545245a42
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/progress_posix.go
@@ -0,0 +1,70 @@
+// +build !windows
+
+package mpb
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+)
+
+func (p *Progress) serve(s *pState) {
+
+ var ticker *time.Ticker
+ var refreshCh <-chan time.Time
+ var winch chan os.Signal
+ var resumeTimer *time.Timer
+ var resumeEvent <-chan time.Time
+ winchIdleDur := s.rr * 2
+
+ if s.manualRefreshCh == nil {
+ ticker = time.NewTicker(s.rr)
+ refreshCh = ticker.C
+ winch = make(chan os.Signal, 2)
+ signal.Notify(winch, syscall.SIGWINCH)
+ } else {
+ refreshCh = s.manualRefreshCh
+ }
+
+ for {
+ select {
+ case op := <-p.operateState:
+ op(s)
+ case <-refreshCh:
+ if s.zeroWait {
+ if s.manualRefreshCh == nil {
+ signal.Stop(winch)
+ ticker.Stop()
+ }
+ if s.shutdownNotifier != nil {
+ close(s.shutdownNotifier)
+ }
+ close(p.done)
+ return
+ }
+ tw, err := s.cw.GetWidth()
+ if err != nil {
+ tw = s.width
+ }
+ s.render(tw)
+ case <-winch:
+ tw, err := s.cw.GetWidth()
+ if err != nil {
+ tw = s.width
+ }
+ s.render(tw - tw/8)
+ if resumeTimer != nil && resumeTimer.Reset(winchIdleDur) {
+ break
+ }
+ ticker.Stop()
+ resumeTimer = time.NewTimer(winchIdleDur)
+ resumeEvent = resumeTimer.C
+ case <-resumeEvent:
+ ticker = time.NewTicker(s.rr)
+ refreshCh = ticker.C
+ resumeEvent = nil
+ resumeTimer = nil
+ }
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/progress_windows.go b/vendor/github.com/vbauerster/mpb/progress_windows.go
new file mode 100644
index 000000000..cab03d36c
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/progress_windows.go
@@ -0,0 +1,43 @@
+// +build windows
+
+package mpb
+
+import (
+ "time"
+)
+
+func (p *Progress) serve(s *pState) {
+
+ var ticker *time.Ticker
+ var refreshCh <-chan time.Time
+
+ if s.manualRefreshCh == nil {
+ ticker = time.NewTicker(s.rr)
+ refreshCh = ticker.C
+ } else {
+ refreshCh = s.manualRefreshCh
+ }
+
+ for {
+ select {
+ case op := <-p.operateState:
+ op(s)
+ case <-refreshCh:
+ if s.zeroWait {
+ if s.manualRefreshCh == nil {
+ ticker.Stop()
+ }
+ if s.shutdownNotifier != nil {
+ close(s.shutdownNotifier)
+ }
+ close(p.done)
+ return
+ }
+ tw, err := s.cw.GetWidth()
+ if err != nil {
+ tw = s.width
+ }
+ s.render(tw)
+ }
+ }
+}
diff --git a/vendor/github.com/vbauerster/mpb/proxyreader.go b/vendor/github.com/vbauerster/mpb/proxyreader.go
new file mode 100644
index 000000000..d2692ccf4
--- /dev/null
+++ b/vendor/github.com/vbauerster/mpb/proxyreader.go
@@ -0,0 +1,22 @@
+package mpb
+
+import (
+ "io"
+ "time"
+)
+
+// proxyReader is io.Reader wrapper, for proxy read bytes
+type proxyReader struct {
+ io.ReadCloser
+ bar *Bar
+ iT time.Time
+}
+
+func (pr *proxyReader) Read(p []byte) (n int, err error) {
+ n, err = pr.ReadCloser.Read(p)
+ if n > 0 {
+ pr.bar.IncrBy(n, time.Since(pr.iT))
+ pr.iT = time.Now()
+ }
+ return
+}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE b/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE
deleted file mode 100644
index 511970333..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/LICENSE
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright (c) 2012-2015, Sergey Cherepanov
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/README.md b/vendor/gopkg.in/cheggaaa/pb.v1/README.md
deleted file mode 100644
index 1295df70e..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/README.md
+++ /dev/null
@@ -1,177 +0,0 @@
-# Terminal progress bar for Go
-
-Simple progress bar for console programs.
-
-Please check the new version https://github.com/cheggaaa/pb/tree/v2 (currently, it's beta)
-
-## Installation
-
-```
-go get gopkg.in/cheggaaa/pb.v1
-```
-
-## Usage
-
-```Go
-package main
-
-import (
- "gopkg.in/cheggaaa/pb.v1"
- "time"
-)
-
-func main() {
- count := 100000
- bar := pb.StartNew(count)
- for i := 0; i < count; i++ {
- bar.Increment()
- time.Sleep(time.Millisecond)
- }
- bar.FinishPrint("The End!")
-}
-
-```
-
-Result will be like this:
-
-```
-> go run test.go
-37158 / 100000 [================>_______________________________] 37.16% 1m11s
-```
-
-## Customization
-
-```Go
-// create bar
-bar := pb.New(count)
-
-// refresh info every second (default 200ms)
-bar.SetRefreshRate(time.Second)
-
-// show percents (by default already true)
-bar.ShowPercent = true
-
-// show bar (by default already true)
-bar.ShowBar = true
-
-// no counters
-bar.ShowCounters = false
-
-// show "time left"
-bar.ShowTimeLeft = true
-
-// show average speed
-bar.ShowSpeed = true
-
-// sets the width of the progress bar
-bar.SetWidth(80)
-
-// sets the width of the progress bar, but if terminal size smaller will be ignored
-bar.SetMaxWidth(80)
-
-// convert output to readable format (like KB, MB)
-bar.SetUnits(pb.U_BYTES)
-
-// and start
-bar.Start()
-```
-
-## Progress bar for IO Operations
-
-```go
-// create and start bar
-bar := pb.New(myDataLen).SetUnits(pb.U_BYTES)
-bar.Start()
-
-// my io.Reader
-r := myReader
-
-// my io.Writer
-w := myWriter
-
-// create proxy reader
-reader := bar.NewProxyReader(r)
-
-// and copy from pb reader
-io.Copy(w, reader)
-
-```
-
-```go
-// create and start bar
-bar := pb.New(myDataLen).SetUnits(pb.U_BYTES)
-bar.Start()
-
-// my io.Reader
-r := myReader
-
-// my io.Writer
-w := myWriter
-
-// create multi writer
-writer := io.MultiWriter(w, bar)
-
-// and copy
-io.Copy(writer, r)
-
-bar.Finish()
-```
-
-## Custom Progress Bar Look-and-feel
-
-```go
-bar.Format("<.- >")
-```
-
-## Multiple Progress Bars (experimental and unstable)
-
-Do not print to terminal while pool is active.
-
-```go
-package main
-
-import (
- "math/rand"
- "sync"
- "time"
-
- "gopkg.in/cheggaaa/pb.v1"
-)
-
-func main() {
- // create bars
- first := pb.New(200).Prefix("First ")
- second := pb.New(200).Prefix("Second ")
- third := pb.New(200).Prefix("Third ")
- // start pool
- pool, err := pb.StartPool(first, second, third)
- if err != nil {
- panic(err)
- }
- // update bars
- wg := new(sync.WaitGroup)
- for _, bar := range []*pb.ProgressBar{first, second, third} {
- wg.Add(1)
- go func(cb *pb.ProgressBar) {
- for n := 0; n < 200; n++ {
- cb.Increment()
- time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
- }
- cb.Finish()
- wg.Done()
- }(bar)
- }
- wg.Wait()
- // close pool
- pool.Stop()
-}
-```
-
-The result will be as follows:
-
-```
-$ go run example/multiple.go
-First 34 / 200 [=========>---------------------------------------------] 17.00% 00m08s
-Second 42 / 200 [===========>------------------------------------------] 21.00% 00m06s
-Third 36 / 200 [=========>---------------------------------------------] 18.00% 00m08s
-```
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/format.go b/vendor/gopkg.in/cheggaaa/pb.v1/format.go
deleted file mode 100644
index 8bb8a7a1d..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/format.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package pb
-
-import (
- "fmt"
- "time"
-)
-
-type Units int
-
-const (
- // U_NO are default units, they represent a simple value and are not formatted at all.
- U_NO Units = iota
- // U_BYTES units are formatted in a human readable way (B, KiB, MiB, ...)
- U_BYTES
- // U_BYTES_DEC units are like U_BYTES, but base 10 (B, KB, MB, ...)
- U_BYTES_DEC
- // U_DURATION units are formatted in a human readable way (3h14m15s)
- U_DURATION
-)
-
-const (
- KiB = 1024
- MiB = 1048576
- GiB = 1073741824
- TiB = 1099511627776
-
- KB = 1e3
- MB = 1e6
- GB = 1e9
- TB = 1e12
-)
-
-func Format(i int64) *formatter {
- return &formatter{n: i}
-}
-
-type formatter struct {
- n int64
- unit Units
- width int
- perSec bool
-}
-
-func (f *formatter) To(unit Units) *formatter {
- f.unit = unit
- return f
-}
-
-func (f *formatter) Width(width int) *formatter {
- f.width = width
- return f
-}
-
-func (f *formatter) PerSec() *formatter {
- f.perSec = true
- return f
-}
-
-func (f *formatter) String() (out string) {
- switch f.unit {
- case U_BYTES:
- out = formatBytes(f.n)
- case U_BYTES_DEC:
- out = formatBytesDec(f.n)
- case U_DURATION:
- out = formatDuration(f.n)
- default:
- out = fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n)
- }
- if f.perSec {
- out += "/s"
- }
- return
-}
-
-// Convert bytes to human readable string. Like 2 MiB, 64.2 KiB, 52 B
-func formatBytes(i int64) (result string) {
- switch {
- case i >= TiB:
- result = fmt.Sprintf("%.02f TiB", float64(i)/TiB)
- case i >= GiB:
- result = fmt.Sprintf("%.02f GiB", float64(i)/GiB)
- case i >= MiB:
- result = fmt.Sprintf("%.02f MiB", float64(i)/MiB)
- case i >= KiB:
- result = fmt.Sprintf("%.02f KiB", float64(i)/KiB)
- default:
- result = fmt.Sprintf("%d B", i)
- }
- return
-}
-
-// Convert bytes to base-10 human readable string. Like 2 MB, 64.2 KB, 52 B
-func formatBytesDec(i int64) (result string) {
- switch {
- case i >= TB:
- result = fmt.Sprintf("%.02f TB", float64(i)/TB)
- case i >= GB:
- result = fmt.Sprintf("%.02f GB", float64(i)/GB)
- case i >= MB:
- result = fmt.Sprintf("%.02f MB", float64(i)/MB)
- case i >= KB:
- result = fmt.Sprintf("%.02f KB", float64(i)/KB)
- default:
- result = fmt.Sprintf("%d B", i)
- }
- return
-}
-
-func formatDuration(n int64) (result string) {
- d := time.Duration(n)
- if d > time.Hour*24 {
- result = fmt.Sprintf("%dd", d/24/time.Hour)
- d -= (d / time.Hour / 24) * (time.Hour * 24)
- }
- if d > time.Hour {
- result = fmt.Sprintf("%s%dh", result, d/time.Hour)
- d -= d / time.Hour * time.Hour
- }
- m := d / time.Minute
- d -= m * time.Minute
- s := d / time.Second
- result = fmt.Sprintf("%s%02dm%02ds", result, m, s)
- return
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pb.go b/vendor/gopkg.in/cheggaaa/pb.v1/pb.go
deleted file mode 100644
index e03291b89..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pb.go
+++ /dev/null
@@ -1,503 +0,0 @@
-// Simple console progress bars
-package pb
-
-import (
- "fmt"
- "io"
- "math"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- "unicode/utf8"
-)
-
-// Current version
-const Version = "1.0.27"
-
-const (
- // Default refresh rate - 200ms
- DEFAULT_REFRESH_RATE = time.Millisecond * 200
- FORMAT = "[=>-]"
-)
-
-// DEPRECATED
-// variables for backward compatibility, from now do not work
-// use pb.Format and pb.SetRefreshRate
-var (
- DefaultRefreshRate = DEFAULT_REFRESH_RATE
- BarStart, BarEnd, Empty, Current, CurrentN string
-)
-
-// Create new progress bar object
-func New(total int) *ProgressBar {
- return New64(int64(total))
-}
-
-// Create new progress bar object using int64 as total
-func New64(total int64) *ProgressBar {
- pb := &ProgressBar{
- Total: total,
- RefreshRate: DEFAULT_REFRESH_RATE,
- ShowPercent: true,
- ShowCounters: true,
- ShowBar: true,
- ShowTimeLeft: true,
- ShowElapsedTime: false,
- ShowFinalTime: true,
- Units: U_NO,
- ManualUpdate: false,
- finish: make(chan struct{}),
- }
- return pb.Format(FORMAT)
-}
-
-// Create new object and start
-func StartNew(total int) *ProgressBar {
- return New(total).Start()
-}
-
-// Callback for custom output
-// For example:
-// bar.Callback = func(s string) {
-// mySuperPrint(s)
-// }
-//
-type Callback func(out string)
-
-type ProgressBar struct {
- current int64 // current must be first member of struct (https://code.google.com/p/go/issues/detail?id=5278)
- previous int64
-
- Total int64
- RefreshRate time.Duration
- ShowPercent, ShowCounters bool
- ShowSpeed, ShowTimeLeft, ShowBar bool
- ShowFinalTime, ShowElapsedTime bool
- Output io.Writer
- Callback Callback
- NotPrint bool
- Units Units
- Width int
- ForceWidth bool
- ManualUpdate bool
- AutoStat bool
-
- // Default width for the time box.
- UnitsWidth int
- TimeBoxWidth int
-
- finishOnce sync.Once //Guards isFinish
- finish chan struct{}
- isFinish bool
-
- startTime time.Time
- startValue int64
-
- changeTime time.Time
-
- prefix, postfix string
-
- mu sync.Mutex
- lastPrint string
-
- BarStart string
- BarEnd string
- Empty string
- Current string
- CurrentN string
-
- AlwaysUpdate bool
-}
-
-// Start print
-func (pb *ProgressBar) Start() *ProgressBar {
- pb.startTime = time.Now()
- pb.startValue = atomic.LoadInt64(&pb.current)
- if atomic.LoadInt64(&pb.Total) == 0 {
- pb.ShowTimeLeft = false
- pb.ShowPercent = false
- pb.AutoStat = false
- }
- if !pb.ManualUpdate {
- pb.Update() // Initial printing of the bar before running the bar refresher.
- go pb.refresher()
- }
- return pb
-}
-
-// Increment current value
-func (pb *ProgressBar) Increment() int {
- return pb.Add(1)
-}
-
-// Get current value
-func (pb *ProgressBar) Get() int64 {
- c := atomic.LoadInt64(&pb.current)
- return c
-}
-
-// Set current value
-func (pb *ProgressBar) Set(current int) *ProgressBar {
- return pb.Set64(int64(current))
-}
-
-// Set64 sets the current value as int64
-func (pb *ProgressBar) Set64(current int64) *ProgressBar {
- atomic.StoreInt64(&pb.current, current)
- return pb
-}
-
-// Add to current value
-func (pb *ProgressBar) Add(add int) int {
- return int(pb.Add64(int64(add)))
-}
-
-func (pb *ProgressBar) Add64(add int64) int64 {
- return atomic.AddInt64(&pb.current, add)
-}
-
-// Set prefix string
-func (pb *ProgressBar) Prefix(prefix string) *ProgressBar {
- pb.mu.Lock()
- defer pb.mu.Unlock()
- pb.prefix = prefix
- return pb
-}
-
-// Set postfix string
-func (pb *ProgressBar) Postfix(postfix string) *ProgressBar {
- pb.mu.Lock()
- defer pb.mu.Unlock()
- pb.postfix = postfix
- return pb
-}
-
-// Set custom format for bar
-// Example: bar.Format("[=>_]")
-// Example: bar.Format("[\x00=\x00>\x00-\x00]") // \x00 is the delimiter
-func (pb *ProgressBar) Format(format string) *ProgressBar {
- var formatEntries []string
- if utf8.RuneCountInString(format) == 5 {
- formatEntries = strings.Split(format, "")
- } else {
- formatEntries = strings.Split(format, "\x00")
- }
- if len(formatEntries) == 5 {
- pb.BarStart = formatEntries[0]
- pb.BarEnd = formatEntries[4]
- pb.Empty = formatEntries[3]
- pb.Current = formatEntries[1]
- pb.CurrentN = formatEntries[2]
- }
- return pb
-}
-
-// Set bar refresh rate
-func (pb *ProgressBar) SetRefreshRate(rate time.Duration) *ProgressBar {
- pb.RefreshRate = rate
- return pb
-}
-
-// Set units
-// bar.SetUnits(U_NO) - by default
-// bar.SetUnits(U_BYTES) - for Mb, Kb, etc
-func (pb *ProgressBar) SetUnits(units Units) *ProgressBar {
- pb.Units = units
- return pb
-}
-
-// Set max width, if width is bigger than terminal width, will be ignored
-func (pb *ProgressBar) SetMaxWidth(width int) *ProgressBar {
- pb.Width = width
- pb.ForceWidth = false
- return pb
-}
-
-// Set bar width
-func (pb *ProgressBar) SetWidth(width int) *ProgressBar {
- pb.Width = width
- pb.ForceWidth = true
- return pb
-}
-
-// End print
-func (pb *ProgressBar) Finish() {
- //Protect multiple calls
- pb.finishOnce.Do(func() {
- close(pb.finish)
- pb.write(atomic.LoadInt64(&pb.Total), atomic.LoadInt64(&pb.current))
- pb.mu.Lock()
- defer pb.mu.Unlock()
- switch {
- case pb.Output != nil:
- fmt.Fprintln(pb.Output)
- case !pb.NotPrint:
- fmt.Println()
- }
- pb.isFinish = true
- })
-}
-
-// IsFinished return boolean
-func (pb *ProgressBar) IsFinished() bool {
- pb.mu.Lock()
- defer pb.mu.Unlock()
- return pb.isFinish
-}
-
-// End print and write string 'str'
-func (pb *ProgressBar) FinishPrint(str string) {
- pb.Finish()
- if pb.Output != nil {
- fmt.Fprintln(pb.Output, str)
- } else {
- fmt.Println(str)
- }
-}
-
-// implement io.Writer
-func (pb *ProgressBar) Write(p []byte) (n int, err error) {
- n = len(p)
- pb.Add(n)
- return
-}
-
-// implement io.Reader
-func (pb *ProgressBar) Read(p []byte) (n int, err error) {
- n = len(p)
- pb.Add(n)
- return
-}
-
-// Create new proxy reader over bar
-// Takes io.Reader or io.ReadCloser
-func (pb *ProgressBar) NewProxyReader(r io.Reader) *Reader {
- return &Reader{r, pb}
-}
-
-func (pb *ProgressBar) write(total, current int64) {
- pb.mu.Lock()
- defer pb.mu.Unlock()
- width := pb.GetWidth()
-
- var percentBox, countersBox, timeLeftBox, timeSpentBox, speedBox, barBox, end, out string
-
- // percents
- if pb.ShowPercent {
- var percent float64
- if total > 0 {
- percent = float64(current) / (float64(total) / float64(100))
- } else {
- percent = float64(current) / float64(100)
- }
- percentBox = fmt.Sprintf(" %6.02f%%", percent)
- }
-
- // counters
- if pb.ShowCounters {
- current := Format(current).To(pb.Units).Width(pb.UnitsWidth)
- if total > 0 {
- totalS := Format(total).To(pb.Units).Width(pb.UnitsWidth)
- countersBox = fmt.Sprintf(" %s / %s ", current, totalS)
- } else {
- countersBox = fmt.Sprintf(" %s / ? ", current)
- }
- }
-
- // time left
- currentFromStart := current - pb.startValue
- fromStart := time.Now().Sub(pb.startTime)
- lastChangeTime := pb.changeTime
- fromChange := lastChangeTime.Sub(pb.startTime)
-
- if pb.ShowElapsedTime {
- timeSpentBox = fmt.Sprintf(" %s ", (fromStart/time.Second)*time.Second)
- }
-
- select {
- case <-pb.finish:
- if pb.ShowFinalTime {
- var left time.Duration
- left = (fromStart / time.Second) * time.Second
- timeLeftBox = fmt.Sprintf(" %s", left.String())
- }
- default:
- if pb.ShowTimeLeft && currentFromStart > 0 {
- perEntry := fromChange / time.Duration(currentFromStart)
- var left time.Duration
- if total > 0 {
- left = time.Duration(total-currentFromStart) * perEntry
- left -= time.Since(lastChangeTime)
- left = (left / time.Second) * time.Second
- } else {
- left = time.Duration(currentFromStart) * perEntry
- left = (left / time.Second) * time.Second
- }
- if left > 0 {
- timeLeft := Format(int64(left)).To(U_DURATION).String()
- timeLeftBox = fmt.Sprintf(" %s", timeLeft)
- }
- }
- }
-
- if len(timeLeftBox) < pb.TimeBoxWidth {
- timeLeftBox = fmt.Sprintf("%s%s", strings.Repeat(" ", pb.TimeBoxWidth-len(timeLeftBox)), timeLeftBox)
- }
-
- // speed
- if pb.ShowSpeed && currentFromStart > 0 {
- fromStart := time.Now().Sub(pb.startTime)
- speed := float64(currentFromStart) / (float64(fromStart) / float64(time.Second))
- speedBox = " " + Format(int64(speed)).To(pb.Units).Width(pb.UnitsWidth).PerSec().String()
- }
-
- barWidth := escapeAwareRuneCountInString(countersBox + pb.BarStart + pb.BarEnd + percentBox + timeSpentBox + timeLeftBox + speedBox + pb.prefix + pb.postfix)
- // bar
- if pb.ShowBar {
- size := width - barWidth
- if size > 0 {
- if total > 0 {
- curSize := int(math.Ceil((float64(current) / float64(total)) * float64(size)))
- emptySize := size - curSize
- barBox = pb.BarStart
- if emptySize < 0 {
- emptySize = 0
- }
- if curSize > size {
- curSize = size
- }
-
- cursorLen := escapeAwareRuneCountInString(pb.Current)
- if emptySize <= 0 {
- barBox += strings.Repeat(pb.Current, curSize/cursorLen)
- } else if curSize > 0 {
- cursorEndLen := escapeAwareRuneCountInString(pb.CurrentN)
- cursorRepetitions := (curSize - cursorEndLen) / cursorLen
- barBox += strings.Repeat(pb.Current, cursorRepetitions)
- barBox += pb.CurrentN
- }
-
- emptyLen := escapeAwareRuneCountInString(pb.Empty)
- barBox += strings.Repeat(pb.Empty, emptySize/emptyLen)
- barBox += pb.BarEnd
- } else {
- pos := size - int(current)%int(size)
- barBox = pb.BarStart
- if pos-1 > 0 {
- barBox += strings.Repeat(pb.Empty, pos-1)
- }
- barBox += pb.Current
- if size-pos-1 > 0 {
- barBox += strings.Repeat(pb.Empty, size-pos-1)
- }
- barBox += pb.BarEnd
- }
- }
- }
-
- // check len
- out = pb.prefix + timeSpentBox + countersBox + barBox + percentBox + speedBox + timeLeftBox + pb.postfix
-
- if cl := escapeAwareRuneCountInString(out); cl < width {
- end = strings.Repeat(" ", width-cl)
- }
-
- // and print!
- pb.lastPrint = out + end
- isFinish := pb.isFinish
-
- switch {
- case isFinish:
- return
- case pb.Output != nil:
- fmt.Fprint(pb.Output, "\r"+out+end)
- case pb.Callback != nil:
- pb.Callback(out + end)
- case !pb.NotPrint:
- fmt.Print("\r" + out + end)
- }
-}
-
-// GetTerminalWidth - returns terminal width for all platforms.
-func GetTerminalWidth() (int, error) {
- return terminalWidth()
-}
-
-func (pb *ProgressBar) GetWidth() int {
- if pb.ForceWidth {
- return pb.Width
- }
-
- width := pb.Width
- termWidth, _ := terminalWidth()
- if width == 0 || termWidth <= width {
- width = termWidth
- }
-
- return width
-}
-
-// Write the current state of the progressbar
-func (pb *ProgressBar) Update() {
- c := atomic.LoadInt64(&pb.current)
- p := atomic.LoadInt64(&pb.previous)
- t := atomic.LoadInt64(&pb.Total)
- if p != c {
- pb.mu.Lock()
- pb.changeTime = time.Now()
- pb.mu.Unlock()
- atomic.StoreInt64(&pb.previous, c)
- }
- pb.write(t, c)
- if pb.AutoStat {
- if c == 0 {
- pb.startTime = time.Now()
- pb.startValue = 0
- } else if c >= t && pb.isFinish != true {
- pb.Finish()
- }
- }
-}
-
-// String return the last bar print
-func (pb *ProgressBar) String() string {
- pb.mu.Lock()
- defer pb.mu.Unlock()
- return pb.lastPrint
-}
-
-// SetTotal atomically sets new total count
-func (pb *ProgressBar) SetTotal(total int) *ProgressBar {
- return pb.SetTotal64(int64(total))
-}
-
-// SetTotal64 atomically sets new total count
-func (pb *ProgressBar) SetTotal64(total int64) *ProgressBar {
- atomic.StoreInt64(&pb.Total, total)
- return pb
-}
-
-// Reset bar and set new total count
-// Does effect only on finished bar
-func (pb *ProgressBar) Reset(total int) *ProgressBar {
- pb.mu.Lock()
- defer pb.mu.Unlock()
- if pb.isFinish {
- pb.SetTotal(total).Set(0)
- atomic.StoreInt64(&pb.previous, 0)
- }
- return pb
-}
-
-// Internal loop for refreshing the progressbar
-func (pb *ProgressBar) refresher() {
- for {
- select {
- case <-pb.finish:
- return
- case <-time.After(pb.RefreshRate):
- pb.Update()
- }
- }
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go b/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go
deleted file mode 100644
index 17168f39a..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// +build appengine js
-
-package pb
-
-import "errors"
-
-// terminalWidth returns width of the terminal, which is not supported
-// and should always failed on appengine classic which is a sandboxed PaaS.
-func terminalWidth() (int, error) {
- return 0, errors.New("Not supported")
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go b/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go
deleted file mode 100644
index 9595e8236..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// +build windows
-
-package pb
-
-import (
- "errors"
- "fmt"
- "os"
- "sync"
- "syscall"
- "unsafe"
-)
-
-var tty = os.Stdin
-
-var (
- kernel32 = syscall.NewLazyDLL("kernel32.dll")
-
- // GetConsoleScreenBufferInfo retrieves information about the
- // specified console screen buffer.
- // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx
- procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
-
- // GetConsoleMode retrieves the current input mode of a console's
- // input buffer or the current output mode of a console screen buffer.
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
- getConsoleMode = kernel32.NewProc("GetConsoleMode")
-
- // SetConsoleMode sets the input mode of a console's input buffer
- // or the output mode of a console screen buffer.
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
- setConsoleMode = kernel32.NewProc("SetConsoleMode")
-
- // SetConsoleCursorPosition sets the cursor position in the
- // specified console screen buffer.
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx
- setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
-)
-
-type (
- // Defines the coordinates of the upper left and lower right corners
- // of a rectangle.
- // See
- // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx
- smallRect struct {
- Left, Top, Right, Bottom int16
- }
-
- // Defines the coordinates of a character cell in a console screen
- // buffer. The origin of the coordinate system (0,0) is at the top, left cell
- // of the buffer.
- // See
- // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx
- coordinates struct {
- X, Y int16
- }
-
- word int16
-
- // Contains information about a console screen buffer.
- // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
- consoleScreenBufferInfo struct {
- dwSize coordinates
- dwCursorPosition coordinates
- wAttributes word
- srWindow smallRect
- dwMaximumWindowSize coordinates
- }
-)
-
-// terminalWidth returns width of the terminal.
-func terminalWidth() (width int, err error) {
- var info consoleScreenBufferInfo
- _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0)
- if e != 0 {
- return 0, error(e)
- }
- return int(info.dwSize.X) - 1, nil
-}
-
-func getCursorPos() (pos coordinates, err error) {
- var info consoleScreenBufferInfo
- _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0)
- if e != 0 {
- return info.dwCursorPosition, error(e)
- }
- return info.dwCursorPosition, nil
-}
-
-func setCursorPos(pos coordinates) error {
- _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0)
- if e != 0 {
- return error(e)
- }
- return nil
-}
-
-var ErrPoolWasStarted = errors.New("Bar pool was started")
-
-var echoLocked bool
-var echoLockMutex sync.Mutex
-
-var oldState word
-
-func lockEcho() (shutdownCh chan struct{}, err error) {
- echoLockMutex.Lock()
- defer echoLockMutex.Unlock()
- if echoLocked {
- err = ErrPoolWasStarted
- return
- }
- echoLocked = true
-
- if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 {
- err = fmt.Errorf("Can't get terminal settings: %v", e)
- return
- }
-
- newState := oldState
- const ENABLE_ECHO_INPUT = 0x0004
- const ENABLE_LINE_INPUT = 0x0002
- newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))
- if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 {
- err = fmt.Errorf("Can't set terminal settings: %v", e)
- return
- }
-
- shutdownCh = make(chan struct{})
- return
-}
-
-func unlockEcho() (err error) {
- echoLockMutex.Lock()
- defer echoLockMutex.Unlock()
- if !echoLocked {
- return
- }
- echoLocked = false
- if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 {
- err = fmt.Errorf("Can't set terminal settings")
- }
- return
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go b/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go
deleted file mode 100644
index af4251760..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// +build linux darwin freebsd netbsd openbsd solaris dragonfly
-// +build !appengine !js
-
-package pb
-
-import (
- "errors"
- "fmt"
- "os"
- "os/signal"
- "sync"
- "syscall"
-
- "golang.org/x/sys/unix"
-)
-
-var ErrPoolWasStarted = errors.New("Bar pool was started")
-
-var (
- echoLockMutex sync.Mutex
- origTermStatePtr *unix.Termios
- tty *os.File
- istty bool
-)
-
-func init() {
- echoLockMutex.Lock()
- defer echoLockMutex.Unlock()
-
- var err error
- tty, err = os.Open("/dev/tty")
- istty = true
- if err != nil {
- tty = os.Stdin
- istty = false
- }
-}
-
-// terminalWidth returns width of the terminal.
-func terminalWidth() (int, error) {
- if !istty {
- return 0, errors.New("Not Supported")
- }
- echoLockMutex.Lock()
- defer echoLockMutex.Unlock()
-
- fd := int(tty.Fd())
-
- ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
- if err != nil {
- return 0, err
- }
-
- return int(ws.Col), nil
-}
-
-func lockEcho() (shutdownCh chan struct{}, err error) {
- echoLockMutex.Lock()
- defer echoLockMutex.Unlock()
- if istty {
- if origTermStatePtr != nil {
- return shutdownCh, ErrPoolWasStarted
- }
-
- fd := int(tty.Fd())
-
- origTermStatePtr, err = unix.IoctlGetTermios(fd, ioctlReadTermios)
- if err != nil {
- return nil, fmt.Errorf("Can't get terminal settings: %v", err)
- }
-
- oldTermios := *origTermStatePtr
- newTermios := oldTermios
- newTermios.Lflag &^= syscall.ECHO
- newTermios.Lflag |= syscall.ICANON | syscall.ISIG
- newTermios.Iflag |= syscall.ICRNL
- if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newTermios); err != nil {
- return nil, fmt.Errorf("Can't set terminal settings: %v", err)
- }
-
- }
- shutdownCh = make(chan struct{})
- go catchTerminate(shutdownCh)
- return
-}
-
-func unlockEcho() error {
- echoLockMutex.Lock()
- defer echoLockMutex.Unlock()
- if istty {
- if origTermStatePtr == nil {
- return nil
- }
-
- fd := int(tty.Fd())
-
- if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, origTermStatePtr); err != nil {
- return fmt.Errorf("Can't set terminal settings: %v", err)
- }
-
- }
- origTermStatePtr = nil
-
- return nil
-}
-
-// listen exit signals and restore terminal state
-func catchTerminate(shutdownCh chan struct{}) {
- sig := make(chan os.Signal, 1)
- signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL)
- defer signal.Stop(sig)
- select {
- case <-shutdownCh:
- unlockEcho()
- case <-sig:
- unlockEcho()
- }
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pool.go b/vendor/gopkg.in/cheggaaa/pb.v1/pool.go
deleted file mode 100644
index 392e7599c..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pool.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows
-
-package pb
-
-import (
- "io"
- "sync"
- "time"
-)
-
-// Create and start new pool with given bars
-// You need call pool.Stop() after work
-func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) {
- pool = new(Pool)
- if err = pool.Start(); err != nil {
- return
- }
- pool.Add(pbs...)
- return
-}
-
-// NewPool initialises a pool with progress bars, but
-// doesn't start it. You need to call Start manually
-func NewPool(pbs ...*ProgressBar) (pool *Pool) {
- pool = new(Pool)
- pool.Add(pbs...)
- return
-}
-
-type Pool struct {
- Output io.Writer
- RefreshRate time.Duration
- bars []*ProgressBar
- lastBarsCount int
- shutdownCh chan struct{}
- workerCh chan struct{}
- m sync.Mutex
- finishOnce sync.Once
-}
-
-// Add progress bars.
-func (p *Pool) Add(pbs ...*ProgressBar) {
- p.m.Lock()
- defer p.m.Unlock()
- for _, bar := range pbs {
- bar.ManualUpdate = true
- bar.NotPrint = true
- bar.Start()
- p.bars = append(p.bars, bar)
- }
-}
-
-func (p *Pool) Start() (err error) {
- p.RefreshRate = DefaultRefreshRate
- p.shutdownCh, err = lockEcho()
- if err != nil {
- return
- }
- p.workerCh = make(chan struct{})
- go p.writer()
- return
-}
-
-func (p *Pool) writer() {
- var first = true
- defer func() {
- if first == false {
- p.print(false)
- } else {
- p.print(true)
- p.print(false)
- }
- close(p.workerCh)
- }()
-
- for {
- select {
- case <-time.After(p.RefreshRate):
- if p.print(first) {
- p.print(false)
- return
- }
- first = false
- case <-p.shutdownCh:
- return
- }
- }
-}
-
-// Restore terminal state and close pool
-func (p *Pool) Stop() error {
- p.finishOnce.Do(func() {
- if p.shutdownCh != nil {
- close(p.shutdownCh)
- }
- })
-
- // Wait for the worker to complete
- select {
- case <-p.workerCh:
- }
-
- return unlockEcho()
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go b/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go
deleted file mode 100644
index 63598d378..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// +build windows
-
-package pb
-
-import (
- "fmt"
- "log"
-)
-
-func (p *Pool) print(first bool) bool {
- p.m.Lock()
- defer p.m.Unlock()
- var out string
- if !first {
- coords, err := getCursorPos()
- if err != nil {
- log.Panic(err)
- }
- coords.Y -= int16(p.lastBarsCount)
- if coords.Y < 0 {
- coords.Y = 0
- }
- coords.X = 0
-
- err = setCursorPos(coords)
- if err != nil {
- log.Panic(err)
- }
- }
- isFinished := true
- for _, bar := range p.bars {
- if !bar.IsFinished() {
- isFinished = false
- }
- bar.Update()
- out += fmt.Sprintf("\r%s\n", bar.String())
- }
- if p.Output != nil {
- fmt.Fprint(p.Output, out)
- } else {
- fmt.Print(out)
- }
- p.lastBarsCount = len(p.bars)
- return isFinished
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go b/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go
deleted file mode 100644
index a8ae14d2f..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// +build linux darwin freebsd netbsd openbsd solaris dragonfly
-
-package pb
-
-import "fmt"
-
-func (p *Pool) print(first bool) bool {
- p.m.Lock()
- defer p.m.Unlock()
- var out string
- if !first {
- out = fmt.Sprintf("\033[%dA", p.lastBarsCount)
- }
- isFinished := true
- for _, bar := range p.bars {
- if !bar.IsFinished() {
- isFinished = false
- }
- bar.Update()
- out += fmt.Sprintf("\r%s\n", bar.String())
- }
- if p.Output != nil {
- fmt.Fprint(p.Output, out)
- } else {
- fmt.Print(out)
- }
- p.lastBarsCount = len(p.bars)
- return isFinished
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/reader.go b/vendor/gopkg.in/cheggaaa/pb.v1/reader.go
deleted file mode 100644
index 9f3148b54..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/reader.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package pb
-
-import (
- "io"
-)
-
-// It's proxy reader, implement io.Reader
-type Reader struct {
- io.Reader
- bar *ProgressBar
-}
-
-func (r *Reader) Read(p []byte) (n int, err error) {
- n, err = r.Reader.Read(p)
- r.bar.Add(n)
- return
-}
-
-// Close the reader when it implements io.Closer
-func (r *Reader) Close() (err error) {
- if closer, ok := r.Reader.(io.Closer); ok {
- return closer.Close()
- }
- return
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go b/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go
deleted file mode 100644
index c617c55ec..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/runecount.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package pb
-
-import (
- "github.com/mattn/go-runewidth"
- "regexp"
-)
-
-// Finds the control character sequences (like colors)
-var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d")
-
-func escapeAwareRuneCountInString(s string) int {
- n := runewidth.StringWidth(s)
- for _, sm := range ctrlFinder.FindAllString(s, -1) {
- n -= runewidth.StringWidth(sm)
- }
- return n
-}
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go b/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go
deleted file mode 100644
index 517ea8ed7..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build darwin freebsd netbsd openbsd dragonfly
-// +build !appengine
-
-package pb
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-const ioctlWriteTermios = syscall.TIOCSETA
diff --git a/vendor/gopkg.in/cheggaaa/pb.v1/termios_sysv.go b/vendor/gopkg.in/cheggaaa/pb.v1/termios_sysv.go
deleted file mode 100644
index b10f61859..000000000
--- a/vendor/gopkg.in/cheggaaa/pb.v1/termios_sysv.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build linux solaris
-// +build !appengine
-
-package pb
-
-import "golang.org/x/sys/unix"
-
-const ioctlReadTermios = unix.TCGETS
-const ioctlWriteTermios = unix.TCSETS