summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md451
-rw-r--r--Makefile12
-rw-r--r--cmd/podman/attach.go5
-rw-r--r--cmd/podman/build.go114
-rw-r--r--cmd/podman/checkpoint.go1
-rw-r--r--cmd/podman/cleanup.go23
-rw-r--r--cmd/podman/cliconfig/config.go19
-rw-r--r--cmd/podman/cliconfig/create.go4
-rw-r--r--cmd/podman/commands.go68
-rw-r--r--cmd/podman/commands_remoteclient.go22
-rw-r--r--cmd/podman/commit.go6
-rw-r--r--cmd/podman/common.go24
-rw-r--r--cmd/podman/common_test.go26
-rw-r--r--cmd/podman/container.go7
-rw-r--r--cmd/podman/containers_prune.go14
-rw-r--r--cmd/podman/cp.go257
-rw-r--r--cmd/podman/create.go9
-rw-r--r--cmd/podman/diff.go4
-rw-r--r--cmd/podman/exec.go2
-rw-r--r--cmd/podman/exists.go9
-rw-r--r--cmd/podman/export.go2
-rw-r--r--cmd/podman/generate.go2
-rw-r--r--cmd/podman/generate_kube.go1
-rw-r--r--cmd/podman/history.go4
-rw-r--r--cmd/podman/image.go7
-rw-r--r--cmd/podman/images.go9
-rw-r--r--cmd/podman/images_prune.go1
-rw-r--r--cmd/podman/import.go2
-rw-r--r--cmd/podman/info.go4
-rw-r--r--cmd/podman/inspect.go6
-rw-r--r--cmd/podman/kill.go2
-rw-r--r--cmd/podman/load.go2
-rw-r--r--cmd/podman/login.go29
-rw-r--r--cmd/podman/logout.go2
-rw-r--r--cmd/podman/logs.go2
-rw-r--r--cmd/podman/main.go26
-rw-r--r--cmd/podman/mount.go2
-rw-r--r--cmd/podman/pause.go2
-rw-r--r--cmd/podman/play.go15
-rw-r--r--cmd/podman/play_kube.go1
-rw-r--r--cmd/podman/pod.go2
-rw-r--r--cmd/podman/pod_create.go5
-rw-r--r--cmd/podman/pod_inspect.go3
-rw-r--r--cmd/podman/pod_kill.go1
-rw-r--r--cmd/podman/pod_pause.go1
-rw-r--r--cmd/podman/pod_ps.go1
-rw-r--r--cmd/podman/pod_restart.go1
-rw-r--r--cmd/podman/pod_rm.go5
-rw-r--r--cmd/podman/pod_start.go1
-rw-r--r--cmd/podman/pod_stats.go131
-rw-r--r--cmd/podman/pod_stop.go1
-rw-r--r--cmd/podman/pod_top.go1
-rw-r--r--cmd/podman/pod_unpause.go1
-rw-r--r--cmd/podman/port.go9
-rw-r--r--cmd/podman/ps.go4
-rw-r--r--cmd/podman/pull.go2
-rw-r--r--cmd/podman/push.go17
-rw-r--r--cmd/podman/refresh.go2
-rw-r--r--cmd/podman/restart.go2
-rw-r--r--cmd/podman/restore.go2
-rw-r--r--cmd/podman/rm.go18
-rw-r--r--cmd/podman/rmi.go3
-rw-r--r--cmd/podman/run.go47
-rw-r--r--cmd/podman/runlabel.go1
-rw-r--r--cmd/podman/save.go3
-rw-r--r--cmd/podman/search.go3
-rw-r--r--cmd/podman/shared/container.go7
-rw-r--r--cmd/podman/sign.go3
-rw-r--r--cmd/podman/start.go12
-rw-r--r--cmd/podman/stats.go3
-rw-r--r--cmd/podman/stop.go3
-rw-r--r--cmd/podman/system.go7
-rw-r--r--cmd/podman/system_prune.go4
-rw-r--r--cmd/podman/tag.go3
-rw-r--r--cmd/podman/top.go3
-rw-r--r--cmd/podman/trust.go1
-rw-r--r--cmd/podman/trust_set_show.go3
-rw-r--r--cmd/podman/umount.go3
-rw-r--r--cmd/podman/unpause.go3
-rw-r--r--cmd/podman/varlink.go7
-rw-r--r--cmd/podman/varlink/io.podman.varlink233
-rw-r--r--cmd/podman/varlink_dummy.go10
-rw-r--r--cmd/podman/version.go2
-rw-r--r--cmd/podman/volume.go10
-rw-r--r--cmd/podman/volume_create.go37
-rw-r--r--cmd/podman/volume_inspect.go23
-rw-r--r--cmd/podman/volume_ls.go43
-rw-r--r--cmd/podman/volume_prune.go30
-rw-r--r--cmd/podman/volume_rm.go37
-rw-r--r--cmd/podman/wait.go3
-rw-r--r--commands.md3
-rw-r--r--completions/bash/podman2
-rw-r--r--contrib/perftest/main.go2
-rw-r--r--docs/libpod.conf.5.md7
-rw-r--r--docs/podman-commit.1.md2
-rw-r--r--docs/podman-cp.1.md80
-rw-r--r--docs/podman-create.1.md51
-rw-r--r--docs/podman-login.1.md14
-rw-r--r--docs/podman-rm.1.md4
-rw-r--r--docs/podman-run.1.md38
-rw-r--r--docs/podman-stats.1.md9
-rw-r--r--docs/podman.1.md1
-rwxr-xr-xhack/get_ci_vm.sh33
-rw-r--r--libpod.conf2
-rw-r--r--libpod/adapter/runtime.go157
-rw-r--r--libpod/adapter/runtime_remote.go282
-rw-r--r--libpod/adapter/volumes_remote.go33
-rw-r--r--libpod/container.go15
-rw-r--r--libpod/container_attach_linux.go4
-rw-r--r--libpod/container_internal.go154
-rw-r--r--libpod/container_internal_linux.go7
-rw-r--r--libpod/errors.go16
-rw-r--r--libpod/image/utils.go17
-rw-r--r--libpod/kube.go16
-rw-r--r--libpod/oci.go1
-rw-r--r--libpod/options.go3
-rw-r--r--libpod/runtime.go26
-rw-r--r--libpod/runtime_ctr.go44
-rw-r--r--libpod/runtime_img.go2
-rw-r--r--libpod/runtime_volume.go75
-rw-r--r--libpod/volume_internal.go4
-rw-r--r--pkg/spec/createconfig.go59
-rw-r--r--pkg/util/utils.go10
-rw-r--r--pkg/varlinkapi/containers.go35
-rw-r--r--pkg/varlinkapi/containers_create.go7
-rw-r--r--pkg/varlinkapi/images.go301
-rw-r--r--pkg/varlinkapi/system.go25
-rw-r--r--pkg/varlinkapi/transfers.go2
-rw-r--r--pkg/varlinkapi/util.go35
-rw-r--r--pkg/varlinkapi/volumes.go90
-rw-r--r--test/README.md2
-rw-r--r--test/bin2img/bin2img.go229
-rw-r--r--test/copyimg/copyimg.go204
-rw-r--r--test/e2e/cp_test.go115
-rw-r--r--test/e2e/create_test.go8
-rw-r--r--test/e2e/pod_stats_test.go23
-rw-r--r--test/e2e/run_test.go2
-rw-r--r--test/e2e/volume_create_test.go2
-rw-r--r--test/e2e/volume_inspect_test.go2
-rw-r--r--test/e2e/volume_ls_test.go2
-rw-r--r--test/e2e/volume_rm_test.go3
-rw-r--r--transfer.md4
-rw-r--r--troubleshooting.md63
-rw-r--r--utils/utils.go12
-rw-r--r--vendor.conf6
-rw-r--r--vendor/github.com/containers/image/copy/copy.go87
-rw-r--r--vendor/github.com/containers/image/docker/docker_client.go33
-rw-r--r--vendor/github.com/containers/image/docker/docker_image.go2
-rw-r--r--vendor/github.com/containers/image/docker/docker_image_dest.go62
-rw-r--r--vendor/github.com/containers/image/docker/docker_image_src.go16
-rw-r--r--vendor/github.com/containers/image/docker/tarfile/src.go76
-rw-r--r--vendor/github.com/containers/image/ostree/ostree_src.go13
-rw-r--r--vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go23
-rw-r--r--vendor/github.com/containers/image/storage/storage_image.go73
-rw-r--r--vendor/github.com/containers/image/storage/storage_reference.go22
-rw-r--r--vendor/github.com/containers/image/storage/storage_transport.go5
-rw-r--r--vendor/github.com/containers/image/version/version.go2
-rw-r--r--vendor/github.com/containers/storage/images.go193
-rw-r--r--vendor/github.com/containers/storage/images_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/pkg/config/config.go96
-rw-r--r--vendor/github.com/containers/storage/store.go441
-rw-r--r--vendor/github.com/containers/storage/vendor.conf9
-rw-r--r--vendor/github.com/urfave/cli/LICENSE21
-rw-r--r--vendor/github.com/urfave/cli/README.md1526
-rw-r--r--vendor/github.com/urfave/cli/app.go508
-rw-r--r--vendor/github.com/urfave/cli/category.go44
-rw-r--r--vendor/github.com/urfave/cli/cli.go22
-rw-r--r--vendor/github.com/urfave/cli/command.go383
-rw-r--r--vendor/github.com/urfave/cli/context.go287
-rw-r--r--vendor/github.com/urfave/cli/errors.go115
-rw-r--r--vendor/github.com/urfave/cli/flag.go786
-rw-r--r--vendor/github.com/urfave/cli/flag_generated.go640
-rw-r--r--vendor/github.com/urfave/cli/funcs.go44
-rw-r--r--vendor/github.com/urfave/cli/help.go345
-rw-r--r--vendor/github.com/urfave/cli/sort.go29
175 files changed, 3449 insertions, 6756 deletions
diff --git a/API.md b/API.md
index f32325d37..a2d86d893 100755
--- a/API.md
+++ b/API.md
@@ -3,9 +3,7 @@ Podman Service Interface and API description. The master version of this docume
in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository.
## Index
-[func AttachToContainer() NotImplemented](#AttachToContainer)
-
-[func BuildImage(build: BuildInfo) BuildResponse](#BuildImage)
+[func BuildImage(build: BuildInfo) MoreResponse](#BuildImage)
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) string](#Commit)
@@ -27,8 +25,6 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func CreateContainer(create: Create) string](#CreateContainer)
-[func CreateImage() NotImplemented](#CreateImage)
-
[func CreatePod(create: PodCreate) string](#CreatePod)
[func DeleteStoppedContainers() []string](#DeleteStoppedContainers)
@@ -39,19 +35,15 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ExportImage(name: string, destination: string, compress: bool, tags: []string) string](#ExportImage)
-[func GenerateKube() NotImplemented](#GenerateKube)
-
-[func GenerateKubeService() NotImplemented](#GenerateKubeService)
-
[func GetAttachSockets(name: string) Sockets](#GetAttachSockets)
-[func GetContainer(name: string) ListContainerData](#GetContainer)
+[func GetContainer(id: string) Container](#GetContainer)
[func GetContainerLogs(name: string) []string](#GetContainerLogs)
[func GetContainerStats(name: string) ContainerStats](#GetContainerStats)
-[func GetImage(name: string) ImageInList](#GetImage)
+[func GetImage(id: string) Image](#GetImage)
[func GetInfo() PodmanInfo](#GetInfo)
@@ -59,7 +51,9 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func GetPodStats(name: string) string, ContainerStats](#GetPodStats)
-[func GetVersion() Version](#GetVersion)
+[func GetVersion() string, string, string, string, string, int](#GetVersion)
+
+[func GetVolumes(args: []string, all: bool) Volume](#GetVolumes)
[func HistoryImage(name: string) ImageHistory](#HistoryImage)
@@ -67,7 +61,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ImagesPrune(all: bool) []string](#ImagesPrune)
-[func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage)
+[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage)
[func InspectContainer(name: string) string](#InspectContainer)
@@ -83,13 +77,11 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ListContainerMounts() map[string]](#ListContainerMounts)
-[func ListContainerPorts(name: string) NotImplemented](#ListContainerPorts)
-
[func ListContainerProcesses(name: string, opts: []string) []string](#ListContainerProcesses)
-[func ListContainers() ListContainerData](#ListContainers)
+[func ListContainers() Container](#ListContainers)
-[func ListImages() ImageInList](#ListImages)
+[func ListImages() Image](#ListImages)
[func ListPods() ListPodData](#ListPods)
@@ -99,29 +91,25 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func PausePod(name: string) string](#PausePod)
-[func Ping() StringResponse](#Ping)
+[func PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ?bool) string](#PullImage)
-[func PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: bool) string](#PullImage)
+[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 PushImage(name: string, tag: string, tlsverify: bool, signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) string](#PushImage)
+[func ReceiveFile(path: string, delete: bool) int](#ReceiveFile)
-[func RemoveContainer(name: string, force: bool) string](#RemoveContainer)
+[func RemoveContainer(name: string, force: bool, removeVolumes: bool) string](#RemoveContainer)
[func RemoveImage(name: string, force: bool) string](#RemoveImage)
[func RemovePod(name: string, force: bool) string](#RemovePod)
-[func RenameContainer() NotImplemented](#RenameContainer)
-
-[func ReplayKube() NotImplemented](#ReplayKube)
-
-[func ResizeContainerTty() NotImplemented](#ResizeContainerTty)
-
[func RestartContainer(name: string, timeout: int) string](#RestartContainer)
[func RestartPod(name: string) string](#RestartPod)
-[func SearchImage(name: string, limit: int) ImageSearch](#SearchImage)
+[func SearchImages(quety: string, limit: int, tlsVerify: ?bool) ImageSearchResult](#SearchImages)
+
+[func SendFile(type: string, length: int) string](#SendFile)
[func StartContainer(name: string) string](#StartContainer)
@@ -133,23 +121,25 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func TagImage(name: string, tagged: string) string](#TagImage)
-[func TopPod() NotImplemented](#TopPod)
-
[func UnmountContainer(name: string, force: bool) ](#UnmountContainer)
[func UnpauseContainer(name: string) string](#UnpauseContainer)
[func UnpausePod(name: string) string](#UnpausePod)
-[func UpdateContainer() NotImplemented](#UpdateContainer)
+[func VolumeCreate(options: VolumeCreateOpts) string](#VolumeCreate)
-[func WaitContainer(name: string) int](#WaitContainer)
+[func VolumeRemove(options: VolumeRemoveOpts) []string](#VolumeRemove)
+
+[func VolumesPrune() []string, []string](#VolumesPrune)
-[func WaitPod() NotImplemented](#WaitPod)
+[func WaitContainer(name: string) int](#WaitContainer)
[type BuildInfo](#BuildInfo)
-[type BuildResponse](#BuildResponse)
+[type BuildOptions](#BuildOptions)
+
+[type Container](#Container)
[type ContainerChanges](#ContainerChanges)
@@ -169,11 +159,11 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type IDMappingOptions](#IDMappingOptions)
-[type ImageHistory](#ImageHistory)
+[type Image](#Image)
-[type ImageInList](#ImageInList)
+[type ImageHistory](#ImageHistory)
-[type ImageSearch](#ImageSearch)
+[type ImageSearchResult](#ImageSearchResult)
[type InfoDistribution](#InfoDistribution)
@@ -185,12 +175,12 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type InfoStore](#InfoStore)
-[type ListContainerData](#ListContainerData)
-
[type ListPodContainerInfo](#ListPodContainerInfo)
[type ListPodData](#ListPodData)
+[type MoreResponse](#MoreResponse)
+
[type NotImplemented](#NotImplemented)
[type PodContainerErrorData](#PodContainerErrorData)
@@ -205,7 +195,11 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[type StringResponse](#StringResponse)
-[type Version](#Version)
+[type Volume](#Volume)
+
+[type VolumeCreateOpts](#VolumeCreateOpts)
+
+[type VolumeRemoveOpts](#VolumeRemoveOpts)
[error ContainerNotFound](#ContainerNotFound)
@@ -224,17 +218,12 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[error RuntimeError](#RuntimeError)
## Methods
-### <a name="AttachToContainer"></a>func AttachToContainer
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method AttachToContainer() [NotImplemented](#NotImplemented)</div>
-This method has not be implemented yet.
### <a name="BuildImage"></a>func BuildImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method BuildImage(build: [BuildInfo](#BuildInfo)) [BuildResponse](#BuildResponse)</div>
+method BuildImage(build: [BuildInfo](#BuildInfo)) [MoreResponse](#MoreResponse)</div>
BuildImage takes a [BuildInfo](#BuildInfo) structure and builds an image. At a minimum, you must provide the
-'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [BuildResponse](#BuildResponse) structure
+'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [MoreResponse](#MoreResponse) structure
that contains the build logs and resulting image ID.
### <a name="Commit"></a>func Commit
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -316,11 +305,6 @@ $ varlink call unix:/run/podman/io.podman/io.podman.CreateContainer '{"create":
"container": "8759dafbc0a4dc3bcfb57eeb72e4331eb73c5cc09ab968e65ce45b9ad5c4b6bb"
}
~~~
-### <a name="CreateImage"></a>func CreateImage
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method CreateImage() [NotImplemented](#NotImplemented)</div>
-This function is not implemented yet.
### <a name="CreatePod"></a>func CreatePod
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -398,18 +382,6 @@ a booleon option to force compression. It also takes in a string array of tags
tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID
of the image is returned. If the image cannot be found in local storage, an [ImageNotFound](#ImageNotFound)
error will be returned. See also [ImportImage](ImportImage).
-### <a name="GenerateKube"></a>func GenerateKube
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method GenerateKube() [NotImplemented](#NotImplemented)</div>
-GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
-and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
-### <a name="GenerateKubeService"></a>func GenerateKubeService
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method GenerateKubeService() [NotImplemented](#NotImplemented)</div>
-GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod
-and its containers. The description is in YAML. See also [GenerateKube](GenerateKube).
### <a name="GetAttachSockets"></a>func GetAttachSockets
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -432,10 +404,11 @@ $ varlink call -m unix:/run/io.podman/io.podman.GetAttachSockets '{"name": "b762
### <a name="GetContainer"></a>func GetContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method GetContainer(name: [string](https://godoc.org/builtin#string)) [ListContainerData](#ListContainerData)</div>
-GetContainer takes a name or ID of a container and returns single ListContainerData
-structure. A [ContainerNotFound](#ContainerNotFound) error will be returned if the container cannot be found.
-See also [ListContainers](ListContainers) and [InspectContainer](#InspectContainer).
+method GetContainer(id: [string](https://godoc.org/builtin#string)) [Container](#Container)</div>
+GetContainer returns information about a single container. If a container
+with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound)
+error will be returned. See also [ListContainers](ListContainers) and
+[InspectContainer](#InspectContainer).
### <a name="GetContainerLogs"></a>func GetContainerLogs
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -476,9 +449,9 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.GetContainerStats '{"name
### <a name="GetImage"></a>func GetImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method GetImage(name: [string](https://godoc.org/builtin#string)) [ImageInList](#ImageInList)</div>
-GetImage returns a single image in an [ImageInList](#ImageInList) struct. You must supply an image name as a string.
-If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned.
+method GetImage(id: [string](https://godoc.org/builtin#string)) [Image](#Image)</div>
+GetImage returns information about a single image in storage.
+If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will be returned.
### <a name="GetInfo"></a>func GetInfo
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -553,9 +526,13 @@ $ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b
### <a name="GetVersion"></a>func GetVersion
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method GetVersion() [Version](#Version)</div>
-GetVersion returns a Version structure describing the libpod setup on their
-system.
+method GetVersion() [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [string](https://godoc.org/builtin#string), [int](https://godoc.org/builtin#int)</div>
+GetVersion returns version and build information of the podman service
+### <a name="GetVolumes"></a>func GetVolumes
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method GetVolumes(args: [[]string](#[]string), all: [bool](https://godoc.org/builtin#bool)) [Volume](#Volume)</div>
+GetVolumes gets slice of the volumes on a remote host
### <a name="HistoryImage"></a>func HistoryImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -586,7 +563,7 @@ the IDs of the removed images are returned.
### <a name="ImportImage"></a>func ImportImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method ImportImage(source: [string](https://godoc.org/builtin#string), reference: [string](https://godoc.org/builtin#string), message: [string](https://godoc.org/builtin#string), changes: [[]string](#[]string)) [string](https://godoc.org/builtin#string)</div>
+method ImportImage(source: [string](https://godoc.org/builtin#string), reference: [string](https://godoc.org/builtin#string), message: [string](https://godoc.org/builtin#string), changes: [[]string](#[]string), delete: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
ImportImage imports an image from a source (like tarball) into local storage. The image can have additional
descriptions added to it using the message and changes options. See also [ExportImage](ExportImage).
### <a name="InspectContainer"></a>func InspectContainer
@@ -656,11 +633,6 @@ $ varlink call unix:/run/podman/io.podman/io.podman.ListContainerMounts
}
}
~~~
-### <a name="ListContainerPorts"></a>func ListContainerPorts
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method ListContainerPorts(name: [string](https://godoc.org/builtin#string)) [NotImplemented](#NotImplemented)</div>
-This function is not implemented yet.
### <a name="ListContainerProcesses"></a>func ListContainerProcesses
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -684,15 +656,15 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ListContainerProcesses '{
### <a name="ListContainers"></a>func ListContainers
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method ListContainers() [ListContainerData](#ListContainerData)</div>
-ListContainers returns a list of containers in no particular order. There are
-returned as an array of ListContainerData structs. See also [GetContainer](#GetContainer).
+method ListContainers() [Container](#Container)</div>
+ListContainers returns information about all containers.
+See also [GetContainer](#GetContainer).
### <a name="ListImages"></a>func ListImages
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method ListImages() [ImageInList](#ImageInList)</div>
-ListImages returns an array of ImageInList structures which provide basic information about
-an image currently in storage. See also [InspectImage](InspectImage).
+method ListImages() [Image](#Image)</div>
+ListImages returns information about the images that are currently in storage.
+See also [InspectImage](InspectImage).
### <a name="ListPods"></a>func ListPods
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -778,20 +750,6 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "fooba
"pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
}
~~~
-### <a name="Ping"></a>func Ping
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method Ping() [StringResponse](#StringResponse)</div>
-Ping provides a response for developers to ensure their varlink setup is working.
-#### Example
-~~~
-$ varlink call -m unix:/run/podman/io.podman/io.podman.Ping
-{
- "ping": {
- "message": "OK"
- }
-}
-~~~
### <a name="PullImage"></a>func PullImage
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -808,17 +766,22 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.PullImage '{"name": "regi
### <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)) [string](https://godoc.org/builtin#string)</div>
+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>
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
-the image cannot be found in local storage; otherwise the ID of the image will be returned on success.
+the image cannot be found in local storage; otherwise it will return a [MoreResponse](#MoreResponse)
+### <a name="ReceiveFile"></a>func ReceiveFile
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ReceiveFile(path: [string](https://godoc.org/builtin#string), delete: [bool](https://godoc.org/builtin#bool)) [int](https://godoc.org/builtin#int)</div>
+ReceiveFile allows the host to send a remote client a file
### <a name="RemoveContainer"></a>func RemoveContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method RemoveContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
+method RemoveContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running
-container can be stopped and removed. Upon successful removal of the container, its ID is returned. If the
+container can be stopped and removed. It also takes a flag on whether or not to remove builtin volumes. Upon successful removal of the container, its ID is returned. If the
container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
#### Example
~~~
@@ -859,22 +822,6 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.RemovePod '{"name": "62f4
"pod": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
}
~~~
-### <a name="RenameContainer"></a>func RenameContainer
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method RenameContainer() [NotImplemented](#NotImplemented)</div>
-This method has not be implemented yet.
-### <a name="ReplayKube"></a>func ReplayKube
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method ReplayKube() [NotImplemented](#NotImplemented)</div>
-ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML)
-like that created by GenerateKube. See also [GenerateKube](GenerateKube).
-### <a name="ResizeContainerTty"></a>func ResizeContainerTty
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method ResizeContainerTty() [NotImplemented](#NotImplemented)</div>
-This method has not be implemented yet.
### <a name="RestartContainer"></a>func RestartContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -900,13 +847,18 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.RestartPod '{"name": "135
"pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6"
}
~~~
-### <a name="SearchImage"></a>func SearchImage
+### <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>
+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.
+### <a name="SendFile"></a>func SendFile
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method SearchImage(name: [string](https://godoc.org/builtin#string), limit: [int](https://godoc.org/builtin#int)) [ImageSearch](#ImageSearch)</div>
-SearchImage takes the string of an image name and a limit of searches from each registries to be returned. SearchImage
-will then use a glob-like match to find the image you are searching for. The images are returned in an array of
-ImageSearch structures which contain information about the image as well as its fully-qualified name.
+method SendFile(type: [string](https://godoc.org/builtin#string), length: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div>
+Sendfile allows a remote client to send a file to the host
### <a name="StartContainer"></a>func StartContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -968,11 +920,6 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.StopPod '{"name": "135d71
method TagImage(name: [string](https://godoc.org/builtin#string), tagged: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
TagImage takes the name or ID of an image in local storage as well as the desired tag name. If the image cannot
be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success.
-### <a name="TopPod"></a>func TopPod
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method TopPod() [NotImplemented](#NotImplemented)</div>
-This method has not been implemented yet.
### <a name="UnmountContainer"></a>func UnmountContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -1007,11 +954,21 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.UnpausePod '{"name": "foo
"pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f"
}
~~~
-### <a name="UpdateContainer"></a>func UpdateContainer
+### <a name="VolumeCreate"></a>func VolumeCreate
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method UpdateContainer() [NotImplemented](#NotImplemented)</div>
-This method has not be implemented yet.
+method VolumeCreate(options: [VolumeCreateOpts](#VolumeCreateOpts)) [string](https://godoc.org/builtin#string)</div>
+VolumeCreate creates a volume on a remote host
+### <a name="VolumeRemove"></a>func VolumeRemove
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method VolumeRemove(options: [VolumeRemoveOpts](#VolumeRemoveOpts)) [[]string](#[]string)</div>
+VolumeRemove removes a volume on a remote host
+### <a name="VolumesPrune"></a>func VolumesPrune
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method VolumesPrune() [[]string](#[]string), [[]string](#[]string)</div>
+VolumesPrune removes unused volumes on the host
### <a name="WaitContainer"></a>func WaitContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -1019,70 +976,122 @@ method WaitContainer(name: [string](https://godoc.org/builtin#string)) [int](htt
WaitContainer takes the name or ID of a container and waits until the container stops. Upon stopping, the return
code of the container is returned. If the container container cannot be found by ID or name,
a [ContainerNotFound](#ContainerNotFound) error is returned.
-### <a name="WaitPod"></a>func WaitPod
-<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-
-method WaitPod() [NotImplemented](#NotImplemented)</div>
-This method has not be implemented yet.
## Types
### <a name="BuildInfo"></a>type BuildInfo
BuildInfo is used to describe user input for building images
-dockerfile [[]string](#[]string)
+additionalTags [[]string](#[]string)
-tags [[]string](#[]string)
+annotations [[]string](#[]string)
-add_hosts [[]string](#[]string)
+buildArgs [map[string]](#map[string])
-cgroup_parent [string](https://godoc.org/builtin#string)
+buildOptions [BuildOptions](#BuildOptions)
-cpu_period [int](https://godoc.org/builtin#int)
+cniConfigDir [string](https://godoc.org/builtin#string)
-cpu_quota [int](https://godoc.org/builtin#int)
+cniPluginDir [string](https://godoc.org/builtin#string)
-cpu_shares [int](https://godoc.org/builtin#int)
+compression [string](https://godoc.org/builtin#string)
-cpuset_cpus [string](https://godoc.org/builtin#string)
+contextDir [string](https://godoc.org/builtin#string)
-cpuset_mems [string](https://godoc.org/builtin#string)
+defaultsMountFilePath [string](https://godoc.org/builtin#string)
-memory [string](https://godoc.org/builtin#string)
+dockerfiles [[]string](#[]string)
-memory_swap [string](https://godoc.org/builtin#string)
+err [string](https://godoc.org/builtin#string)
-security_opts [[]string](#[]string)
+forceRmIntermediateCtrs [bool](https://godoc.org/builtin#bool)
-shm_size [string](https://godoc.org/builtin#string)
+iidfile [string](https://godoc.org/builtin#string)
-ulimit [[]string](#[]string)
+label [[]string](#[]string)
-volume [[]string](#[]string)
+layers [bool](https://godoc.org/builtin#bool)
+
+nocache [bool](https://godoc.org/builtin#bool)
+
+out [string](https://godoc.org/builtin#string)
+
+output [string](https://godoc.org/builtin#string)
+
+outputFormat [string](https://godoc.org/builtin#string)
+
+pullPolicy [string](https://godoc.org/builtin#string)
+
+quiet [bool](https://godoc.org/builtin#bool)
+
+remoteIntermediateCtrs [bool](https://godoc.org/builtin#bool)
+
+reportWriter [string](https://godoc.org/builtin#string)
+
+runtimeArgs [[]string](#[]string)
+
+signaturePolicyPath [string](https://godoc.org/builtin#string)
squash [bool](https://godoc.org/builtin#bool)
+### <a name="BuildOptions"></a>type BuildOptions
-pull [bool](https://godoc.org/builtin#bool)
+BuildOptions are are used to describe describe physical attributes of the build
-pull_always [bool](https://godoc.org/builtin#bool)
+addHosts [[]string](#[]string)
-force_rm [bool](https://godoc.org/builtin#bool)
+cgroupParent [string](https://godoc.org/builtin#string)
-rm [bool](https://godoc.org/builtin#bool)
+cpuPeriod [int](https://godoc.org/builtin#int)
-label [[]string](#[]string)
+cpuQuota [int](https://godoc.org/builtin#int)
-annotations [[]string](#[]string)
+cpuShares [int](https://godoc.org/builtin#int)
+
+cpusetCpus [string](https://godoc.org/builtin#string)
-build_args [map[string]](#map[string])
+cpusetMems [string](https://godoc.org/builtin#string)
-image_format [string](https://godoc.org/builtin#string)
-### <a name="BuildResponse"></a>type BuildResponse
+memory [int](https://godoc.org/builtin#int)
+
+memorySwap [int](https://godoc.org/builtin#int)
+
+shmSize [string](https://godoc.org/builtin#string)
+
+ulimit [[]string](#[]string)
+
+volume [[]string](#[]string)
+### <a name="Container"></a>type Container
-BuildResponse is used to describe the responses for building images
-logs [[]string](#[]string)
id [string](https://godoc.org/builtin#string)
+
+image [string](https://godoc.org/builtin#string)
+
+imageid [string](https://godoc.org/builtin#string)
+
+command [[]string](#[]string)
+
+createdat [string](https://godoc.org/builtin#string)
+
+runningfor [string](https://godoc.org/builtin#string)
+
+status [string](https://godoc.org/builtin#string)
+
+ports [ContainerPortMappings](#ContainerPortMappings)
+
+rootfssize [int](https://godoc.org/builtin#int)
+
+rwsize [int](https://godoc.org/builtin#int)
+
+names [string](https://godoc.org/builtin#string)
+
+labels [map[string]](#map[string])
+
+mounts [ContainerMount](#ContainerMount)
+
+containerrunning [bool](https://godoc.org/builtin#bool)
+
+namespaces [ContainerNameSpace](#ContainerNameSpace)
### <a name="ContainerChanges"></a>type ContainerChanges
ContainerChanges describes the return struct for ListContainerChanges
@@ -1366,25 +1375,9 @@ host_gid_mapping [bool](https://godoc.org/builtin#bool)
uid_map [IDMap](#IDMap)
gid_map [IDMap](#IDMap)
-### <a name="ImageHistory"></a>type ImageHistory
-
-ImageHistory describes the returned structure from ImageHistory.
-
-id [string](https://godoc.org/builtin#string)
-
-created [string](https://godoc.org/builtin#string)
-
-createdBy [string](https://godoc.org/builtin#string)
+### <a name="Image"></a>type Image
-tags [[]string](#[]string)
-size [int](https://godoc.org/builtin#int)
-
-comment [string](https://godoc.org/builtin#string)
-### <a name="ImageInList"></a>type ImageInList
-
-ImageInList describes the structure that is returned in
-ListImages.
id [string](https://godoc.org/builtin#string)
@@ -1405,10 +1398,24 @@ containers [int](https://godoc.org/builtin#int)
labels [map[string]](#map[string])
isParent [bool](https://godoc.org/builtin#bool)
-### <a name="ImageSearch"></a>type ImageSearch
+### <a name="ImageHistory"></a>type ImageHistory
+
+ImageHistory describes the returned structure from ImageHistory.
+
+id [string](https://godoc.org/builtin#string)
-ImageSearch is the returned structure for SearchImage. It is returned
-in array form.
+created [string](https://godoc.org/builtin#string)
+
+createdBy [string](https://godoc.org/builtin#string)
+
+tags [[]string](#[]string)
+
+size [int](https://godoc.org/builtin#int)
+
+comment [string](https://godoc.org/builtin#string)
+### <a name="ImageSearchResult"></a>type ImageSearchResult
+
+Represents a single search result from SearchImages
description [string](https://godoc.org/builtin#string)
@@ -1490,39 +1497,6 @@ graph_root [string](https://godoc.org/builtin#string)
graph_status [InfoGraphStatus](#InfoGraphStatus)
run_root [string](https://godoc.org/builtin#string)
-### <a name="ListContainerData"></a>type ListContainerData
-
-ListContainerData is the returned struct for an individual container
-
-id [string](https://godoc.org/builtin#string)
-
-image [string](https://godoc.org/builtin#string)
-
-imageid [string](https://godoc.org/builtin#string)
-
-command [[]string](#[]string)
-
-createdat [string](https://godoc.org/builtin#string)
-
-runningfor [string](https://godoc.org/builtin#string)
-
-status [string](https://godoc.org/builtin#string)
-
-ports [ContainerPortMappings](#ContainerPortMappings)
-
-rootfssize [int](https://godoc.org/builtin#int)
-
-rwsize [int](https://godoc.org/builtin#int)
-
-names [string](https://godoc.org/builtin#string)
-
-labels [map[string]](#map[string])
-
-mounts [ContainerMount](#ContainerMount)
-
-containerrunning [bool](https://godoc.org/builtin#bool)
-
-namespaces [ContainerNameSpace](#ContainerNameSpace)
### <a name="ListPodContainerInfo"></a>type ListPodContainerInfo
ListPodContainerInfo is a returned struct for describing containers
@@ -1552,6 +1526,13 @@ labels [map[string]](#map[string])
numberofcontainers [string](https://godoc.org/builtin#string)
containersinfo [ListPodContainerInfo](#ListPodContainerInfo)
+### <a name="MoreResponse"></a>type MoreResponse
+
+MoreResponse is a struct for when responses from varlink requires longer output
+
+logs [[]string](#[]string)
+
+id [string](https://godoc.org/builtin#string)
### <a name="NotImplemented"></a>type NotImplemented
@@ -1639,21 +1620,41 @@ control_socket [string](https://godoc.org/builtin#string)
message [string](https://godoc.org/builtin#string)
-### <a name="Version"></a>type Version
+### <a name="Volume"></a>type Volume
-Version is the structure returned by GetVersion
-version [string](https://godoc.org/builtin#string)
-go_version [string](https://godoc.org/builtin#string)
+name [string](https://godoc.org/builtin#string)
-git_commit [string](https://godoc.org/builtin#string)
+labels [map[string]](#map[string])
+
+mountPoint [string](https://godoc.org/builtin#string)
-built [int](https://godoc.org/builtin#int)
+driver [string](https://godoc.org/builtin#string)
+
+options [map[string]](#map[string])
+
+scope [string](https://godoc.org/builtin#string)
+### <a name="VolumeCreateOpts"></a>type VolumeCreateOpts
+
+
+
+volumeName [string](https://godoc.org/builtin#string)
+
+driver [string](https://godoc.org/builtin#string)
+
+labels [map[string]](#map[string])
+
+options [map[string]](#map[string])
+### <a name="VolumeRemoveOpts"></a>type VolumeRemoveOpts
+
+
+
+volumes [[]string](#[]string)
-os_arch [string](https://godoc.org/builtin#string)
+all [bool](https://godoc.org/builtin#bool)
-remote_api_version [int](https://godoc.org/builtin#int)
+force [bool](https://godoc.org/builtin#bool)
## Errors
### <a name="ContainerNotFound"></a>type ContainerNotFound
diff --git a/Makefile b/Makefile
index 08dd586be..f634fcc81 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo
PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e | grep -v system )
COMMIT_NO ?= $(shell git rev-parse HEAD 2> /dev/null || true)
-GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
+GIT_COMMIT ?= $(if $(shell git status --porcelain --untracked-files=no),${COMMIT_NO}-dirty,${COMMIT_NO})
BUILD_INFO ?= $(shell date +%s)
LIBPOD := ${PROJECT}/libpod
LDFLAGS_PODMAN ?= $(LDFLAGS) -X $(LIBPOD).gitCommit=$(GIT_COMMIT) -X $(LIBPOD).buildInfo=$(BUILD_INFO)
@@ -94,6 +94,7 @@ help:
ifeq ("$(wildcard $(GOPKGDIR))","")
mkdir -p "$(GOPKGBASEDIR)"
ln -sf "$(CURDIR)" "$(GOPKGBASEDIR)"
+ ln -sf "$(CURDIR)/vendor/github.com/varlink" "$(FIRST_GOPATH)/src/github.com/varlink"
endif
touch $@
@@ -105,11 +106,6 @@ gofmt: ## Verify the source code gofmt
find . -name '*.go' ! -path './vendor/*' -exec gofmt -s -w {} \+
git diff --exit-code
-test/bin2img/bin2img: .gopathok $(wildcard test/bin2img/*.go)
- $(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/bin2img
-
-test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go)
- $(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/copyimg
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
$(GO) build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
@@ -140,9 +136,7 @@ clean: ## Clean artifacts
_output \
bin \
build \
- test/bin2img/bin2img \
test/checkseccomp/checkseccomp \
- test/copyimg/copyimg \
test/goecho/goecho \
test/testdata/redis-image \
cmd/podman/varlink/iopodman.go \
@@ -210,7 +204,7 @@ binaries: varlink_generate podman podman-remote ## Build podman
install.catatonit:
./hack/install_catatonit.sh
-test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp test/goecho/goecho install.catatonit
+test-binaries: test/checkseccomp/checkseccomp test/goecho/goecho install.catatonit
MANPAGES_MD ?= $(wildcard docs/*.md pkg/*/docs/*.md)
MANPAGES ?= $(MANPAGES_MD:%.md=%)
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go
index 8b0fd7ffd..ed175bdf4 100644
--- a/cmd/podman/attach.go
+++ b/cmd/podman/attach.go
@@ -28,14 +28,13 @@ var (
func init() {
attachCommand.Command = _attachCommand
+ attachCommand.SetUsageTemplate(UsageTemplate())
flags := attachCommand.Flags()
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")
-
- rootCmd.AddCommand(attachCommand.Command)
}
func attachCmd(c *cliconfig.AttachValues) error {
@@ -75,7 +74,7 @@ func attachCmd(c *cliconfig.AttachValues) error {
inputStream = nil
}
- if err := startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false); err != nil {
+ if err := startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false); err != nil && errors.Cause(err) != libpod.ErrDetach {
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
}
diff --git a/cmd/podman/build.go b/cmd/podman/build.go
index 20f621e84..30a734377 100644
--- a/cmd/podman/build.go
+++ b/cmd/podman/build.go
@@ -1,7 +1,6 @@
package main
import (
- "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -9,10 +8,9 @@ import (
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
buildahcli "github.com/containers/buildah/pkg/cli"
- "github.com/containers/buildah/pkg/parse"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/libpod/adapter"
+ "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -48,6 +46,7 @@ var (
func init() {
buildCommand.Command = _buildCommand
+ buildCommand.SetUsageTemplate(UsageTemplate())
flags := buildCommand.Flags()
flags.SetInterspersed(false)
@@ -58,8 +57,6 @@ func init() {
flags.AddFlagSet(&budFlags)
flags.AddFlagSet(&fromAndBugFlags)
-
- rootCmd.AddCommand(buildCommand.Command)
}
func getDockerfiles(files []string) []string {
@@ -77,7 +74,6 @@ func getDockerfiles(files []string) []string {
func buildCmd(c *cliconfig.BuildValues) error {
// The following was taken directly from containers/buildah/cmd/bud.go
// TODO Find a away to vendor more of this in rather than copy from bud
-
output := ""
tags := []string{}
if c.Flag("tag").Changed {
@@ -87,6 +83,7 @@ func buildCmd(c *cliconfig.BuildValues) error {
tags = tags[1:]
}
}
+
pullPolicy := imagebuildah.PullNever
if c.Pull {
pullPolicy = imagebuildah.PullIfMissing
@@ -174,16 +171,17 @@ func buildCmd(c *cliconfig.BuildValues) error {
dockerfiles = append(dockerfiles, filepath.Join(contextDir, "Dockerfile"))
}
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+
runtimeFlags := []string{}
for _, arg := range c.RuntimeOpts {
runtimeFlags = append(runtimeFlags, "--"+arg)
}
// end from buildah
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
- if err != nil {
- return errors.Wrapf(err, "could not get runtime")
- }
defer runtime.Shutdown(false)
var stdout, stderr, reporter *os.File
@@ -202,72 +200,64 @@ func buildCmd(c *cliconfig.BuildValues) error {
reporter = f
}
- systemContext, err := parse.SystemContextFromOptions(c.PodmanCommand.Command)
- if err != nil {
- return errors.Wrapf(err, "error building system context")
- }
- systemContext.AuthFilePath = getAuthFile(c.Authfile)
- commonOpts, err := parse.CommonBuildOptions(c.PodmanCommand.Command)
- if err != nil {
- return err
+ var memoryLimit, memorySwap int64
+ if c.Flags().Changed("memory") {
+ memoryLimit, err = units.RAMInBytes(c.Memory)
+ if err != nil {
+ return err
+ }
}
- namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c.PodmanCommand.Command)
- if err != nil {
- return errors.Wrapf(err, "error parsing namespace-related options")
- }
- usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command)
- if err != nil {
- return errors.Wrapf(err, "error parsing ID mapping options")
+ if c.Flags().Changed("memory-swap") {
+ memorySwap, err = units.RAMInBytes(c.MemorySwap)
+ if err != nil {
+ return err
+ }
}
- namespaceOptions.AddOrReplace(usernsOption...)
- ociruntime := runtime.GetOCIRuntimePath()
- if c.Flag("runtime").Changed {
- ociruntime = c.Runtime
+ buildOpts := buildah.CommonBuildOptions{
+ AddHost: c.AddHost,
+ CgroupParent: c.CgroupParent,
+ CPUPeriod: c.CPUPeriod,
+ CPUQuota: c.CPUQuota,
+ CPUShares: c.CPUShares,
+ CPUSetCPUs: c.CPUSetCPUs,
+ CPUSetMems: c.CPUSetMems,
+ Memory: memoryLimit,
+ MemorySwap: memorySwap,
+ ShmSize: c.ShmSize,
+ Ulimit: c.Ulimit,
+ Volumes: c.Volume,
}
+
options := imagebuildah.BuildOptions{
- ContextDirectory: contextDir,
- PullPolicy: pullPolicy,
- Compression: imagebuildah.Gzip,
- Quiet: c.Quiet,
- SignaturePolicyPath: c.SignaturePolicy,
- Args: args,
- Output: output,
+ CommonBuildOpts: &buildOpts,
AdditionalTags: tags,
- Out: stdout,
- Err: stderr,
- ReportWriter: reporter,
- Runtime: ociruntime,
- RuntimeArgs: runtimeFlags,
- OutputFormat: format,
- SystemContext: systemContext,
- NamespaceOptions: namespaceOptions,
- ConfigureNetwork: networkPolicy,
- CNIPluginPath: c.CNIPlugInPath,
+ Annotations: c.Annotation,
+ Args: args,
CNIConfigDir: c.CNIConfigDir,
- IDMappingOptions: idmappingOptions,
- CommonBuildOpts: commonOpts,
+ CNIPluginPath: c.CNIPlugInPath,
+ Compression: imagebuildah.Gzip,
+ ContextDirectory: contextDir,
DefaultMountsFilePath: c.GlobalFlags.DefaultMountsFile,
+ Err: stderr,
+ ForceRmIntermediateCtrs: c.ForceRm,
IIDFile: c.Iidfile,
- Squash: c.Squash,
Labels: c.Label,
- Annotations: c.Annotation,
Layers: layers,
NoCache: c.NoCache,
+ Out: stdout,
+ Output: output,
+ OutputFormat: format,
+ PullPolicy: pullPolicy,
+ Quiet: c.Quiet,
RemoveIntermediateCtrs: c.Rm,
- ForceRmIntermediateCtrs: c.ForceRm,
- }
-
- if c.Quiet {
- options.ReportWriter = ioutil.Discard
- }
-
- if rootless.IsRootless() {
- options.Isolation = buildah.IsolationOCIRootless
+ ReportWriter: reporter,
+ RuntimeArgs: runtimeFlags,
+ SignaturePolicyPath: c.SignaturePolicy,
+ Squash: c.Squash,
}
-
- return runtime.Build(getContext(), options, dockerfiles...)
+ return runtime.Build(getContext(), c, options, dockerfiles)
}
// Tail returns a string slice after the first element unless there are
diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go
index 2a978bea8..aa4034ccd 100644
--- a/cmd/podman/checkpoint.go
+++ b/cmd/podman/checkpoint.go
@@ -35,6 +35,7 @@ var (
func init() {
checkpointCommand.Command = _checkpointCommand
+ checkpointCommand.SetUsageTemplate(UsageTemplate())
flags := checkpointCommand.Flags()
flags.BoolVarP(&checkpointCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go
index 1a7617d5c..e465a30e6 100644
--- a/cmd/podman/cleanup.go
+++ b/cmd/podman/cleanup.go
@@ -32,10 +32,12 @@ var (
func init() {
cleanupCommand.Command = _cleanupCommand
+ cleanupCommand.SetUsageTemplate(UsageTemplate())
flags := cleanupCommand.Flags()
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")
}
func cleanupCmd(c *cliconfig.CleanupValues) error {
@@ -54,12 +56,25 @@ func cleanupCmd(c *cliconfig.CleanupValues) error {
ctx := getContext()
for _, ctr := range cleanupContainers {
- if err = ctr.Cleanup(ctx); err != nil {
- if lastError != nil {
- fmt.Fprintln(os.Stderr, lastError)
+ hadError := false
+ if c.Remove {
+ if err := runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
+ if lastError != nil {
+ fmt.Fprintln(os.Stderr, lastError)
+ }
+ lastError = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID())
+ hadError = true
}
- lastError = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID())
} else {
+ if err := ctr.Cleanup(ctx); err != nil {
+ if lastError != nil {
+ fmt.Fprintln(os.Stderr, lastError)
+ }
+ lastError = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID())
+ hadError = true
+ }
+ }
+ if !hadError {
fmt.Println(ctr.ID())
}
}
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index a4c1bf0c0..9c9be3618 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -135,6 +135,11 @@ type PruneImagesValues struct {
All bool
}
+type PruneContainersValues struct {
+ PodmanCommand
+ Force bool
+}
+
type ImportValues struct {
PodmanCommand
Change []string
@@ -172,12 +177,13 @@ type LoadValues struct {
type LoginValues struct {
PodmanCommand
- Password string
- Username string
- Authfile string
- CertDir string
- GetLogin bool
- TlsVerify bool
+ Password string
+ StdinPassword bool
+ Username string
+ Authfile string
+ CertDir string
+ GetLogin bool
+ TlsVerify bool
}
type LogoutValues struct {
@@ -532,6 +538,7 @@ type CleanupValues struct {
PodmanCommand
All bool
Latest bool
+ Remove bool
}
type SystemPruneValues struct {
diff --git a/cmd/podman/cliconfig/create.go b/cmd/podman/cliconfig/create.go
index 68ba4d857..b5ca1be9c 100644
--- a/cmd/podman/cliconfig/create.go
+++ b/cmd/podman/cliconfig/create.go
@@ -20,3 +20,7 @@ type BuildValues struct {
*buildahcli.NameSpaceResults
*buildahcli.LayerResults
}
+
+type CpValues struct {
+ PodmanCommand
+}
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index e1eba1f31..fa3839a53 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -6,24 +6,65 @@ import (
"github.com/spf13/cobra"
)
+// Commands that the local client implements
+func getMainCommands() []*cobra.Command {
+ rootCommands := []*cobra.Command{
+ _attachCommand,
+ _commitCommand,
+ _createCommand,
+ _diffCommand,
+ _execCommand,
+ _killCommand,
+ generateCommand.Command,
+ podCommand.Command,
+ _containerKubeCommand,
+ _psCommand,
+ _loadCommand,
+ _loginCommand,
+ _logoutCommand,
+ _logsCommand,
+ _mountCommand,
+ _pauseCommand,
+ _portCommand,
+ _refreshCommand,
+ _restartCommand,
+ _restoreCommand,
+ _rmCommand,
+ _runCommand,
+ _saveCommand,
+ _searchCommand,
+ _signCommand,
+ _startCommand,
+ _statsCommand,
+ _stopCommand,
+ _topCommand,
+ _umountCommand,
+ _unpauseCommand,
+ volumeCommand.Command,
+ _waitCommand,
+ }
+
+ if len(_varlinkCommand.Use) > 0 {
+ rootCommands = append(rootCommands, _varlinkCommand)
+ }
+ return rootCommands
+}
+
+// Commands that the local client implements
func getImageSubCommands() []*cobra.Command {
return []*cobra.Command{
- _buildCommand,
- _importCommand,
_loadCommand,
- _pullCommand,
- _rmiCommand,
_saveCommand,
_signCommand,
}
}
+// Commands that the local client implements
func getContainerSubCommands() []*cobra.Command {
return []*cobra.Command{
_attachCommand,
_checkpointCommand,
_cleanupCommand,
- _containerExistsCommand,
_commitCommand,
_createCommand,
_diffCommand,
@@ -40,7 +81,7 @@ func getContainerSubCommands() []*cobra.Command {
_restartCommand,
_restoreCommand,
_rmCommand,
- _runCommmand,
+ _runCommand,
_runlabelCommand,
_startCommand,
_statsCommand,
@@ -52,6 +93,7 @@ func getContainerSubCommands() []*cobra.Command {
}
}
+// Commands that the local client implements
func getPodSubCommands() []*cobra.Command {
return []*cobra.Command{
_podCreateCommand,
@@ -70,28 +112,20 @@ func getPodSubCommands() []*cobra.Command {
}
}
-func getVolumeSubCommands() []*cobra.Command {
- return []*cobra.Command{
- _volumeCreateCommand,
- _volumeLsCommand,
- _volumeRmCommand,
- _volumeInspectCommand,
- _volumePruneCommand,
- }
-}
-
func getGenerateSubCommands() []*cobra.Command {
return []*cobra.Command{
_containerKubeCommand,
}
}
+// Commands that the local client implements
func getPlaySubCommands() []*cobra.Command {
return []*cobra.Command{
_playKubeCommand,
}
}
+// Commands that the local client implements
func getTrustSubCommands() []*cobra.Command {
return []*cobra.Command{
_setTrustCommand,
@@ -99,9 +133,9 @@ func getTrustSubCommands() []*cobra.Command {
}
}
+// Commands that the local client implements
func getSystemSubCommands() []*cobra.Command {
return []*cobra.Command{
- _infoCommand,
_pruneSystemCommand,
}
}
diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go
index 80083eab9..ba0a4d47e 100644
--- a/cmd/podman/commands_remoteclient.go
+++ b/cmd/podman/commands_remoteclient.go
@@ -6,43 +6,47 @@ import (
"github.com/spf13/cobra"
)
-//import "github.com/urfave/cli"
-//
+// commands that only the remoteclient implements
+func getMainCommands() []*cobra.Command {
+ return []*cobra.Command{}
+}
+
+// commands that only the remoteclient implements
func getAppCommands() []*cobra.Command {
return []*cobra.Command{}
}
+// commands that only the remoteclient implements
func getImageSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
+// commands that only the remoteclient implements
func getContainerSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
+// commands that only the remoteclient implements
func getPodSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
-func getVolumeSubCommands() []*cobra.Command {
- return []*cobra.Command{}
-}
-
+// commands that only the remoteclient implements
func getGenerateSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
+// commands that only the remoteclient implements
func getPlaySubCommands() []*cobra.Command {
return []*cobra.Command{}
}
+// commands that only the remoteclient implements
func getTrustSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
-//func getMainAppFlags() []cli.Flag {
-// return []cli.Flag{}
-//}
+// commands that only the remoteclient implements
func getSystemSubCommands() []*cobra.Command {
return []*cobra.Command{}
}
diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go
index 6fd6b9761..d8ced0e36 100644
--- a/cmd/podman/commit.go
+++ b/cmd/podman/commit.go
@@ -33,12 +33,15 @@ var (
commitCommand.GlobalFlags = MainGlobalOpts
return commitCmd(&commitCommand)
},
- Example: "CONTAINER [REPOSITORY[:TAG]]",
+ Example: `podman commit -q --message "committing container to image" reverent_golick image-commited
+ podman commit -q --author "firstName lastName" reverent_golick image-commited
+ podman commit -q --pause=false containerID image-commited`,
}
)
func init() {
commitCommand.Command = _commitCommand
+ commitCommand.SetUsageTemplate(UsageTemplate())
flags := commitCommand.Flags()
flags.StringSliceVarP(&commitCommand.Change, "change", "c", []string{}, fmt.Sprintf("Apply the following possible instructions to the created image (default []): %s", strings.Join(libpod.ChangeCmds, " | ")))
flags.StringVarP(&commitCommand.Format, "format", "f", "oci", "`Format` of the image manifest and metadata")
@@ -47,7 +50,6 @@ func init() {
flags.BoolVarP(&commitCommand.Pause, "pause", "p", false, "Pause container during commit")
flags.BoolVarP(&commitCommand.Quiet, "quiet", "q", false, "Suppress output")
- rootCmd.AddCommand(commitCommand.Command)
}
func commitCmd(c *cliconfig.CommitValues) error {
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 5fbdfce50..ec755c4a8 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -500,3 +500,27 @@ func scrubServer(server string) string {
server = strings.TrimPrefix(server, "https://")
return strings.TrimPrefix(server, "http://")
}
+
+// UsageTemplate returns the usage template for podman commands
+// This blocks the desplaying of the global options. The main podman
+// command should not use this.
+func UsageTemplate() string {
+ return `Usage:{{if .Runnable}}
+ {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+
+ {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
+
+Aliases:
+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Examples:
+ {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
+
+Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
+
+Flags:
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
+{{end}}
+`
+}
diff --git a/cmd/podman/common_test.go b/cmd/podman/common_test.go
index 042568d7e..a24173003 100644
--- a/cmd/podman/common_test.go
+++ b/cmd/podman/common_test.go
@@ -3,34 +3,8 @@ package main
import (
"os/user"
"testing"
-
- "flag"
-
- "github.com/urfave/cli"
)
-func TestGetStore(t *testing.T) {
- t.Skip("FIX THIS!")
-
- //cmd/podman/common_test.go:27: cannot use c (type *cli.Context) as type *libkpod.Config in argument to getStore
-
- // Make sure the tests are running as root
- skipTestIfNotRoot(t)
-
- set := flag.NewFlagSet("test", 0)
- globalSet := flag.NewFlagSet("test", 0)
- globalSet.String("root", "", "path to the root directory in which data, including images, is stored")
- globalCtx := cli.NewContext(nil, globalSet, nil)
- command := cli.Command{Name: "imagesCommand"}
- c := cli.NewContext(nil, set, globalCtx)
- c.Command = command
-
- //_, err := getStore(c)
- //if err != nil {
- //t.Error(err)
- //}
-}
-
func skipTestIfNotRoot(t *testing.T) {
u, err := user.Current()
if err != nil {
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index 969cb2dc8..d2450fdd3 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -15,7 +15,14 @@ var containerCommand = cliconfig.PodmanCommand{
},
}
+// Commands that are universally implemented.
+var containerCommands = []*cobra.Command{
+ _containerExistsCommand,
+}
+
func init() {
+ containerCommand.AddCommand(containerCommands...)
containerCommand.AddCommand(getContainerSubCommands()...)
+ containerCommand.SetUsageTemplate(UsageTemplate())
rootCmd.AddCommand(containerCommand.Command)
}
diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go
index 3f9b46035..bae578e1d 100644
--- a/cmd/podman/containers_prune.go
+++ b/cmd/podman/containers_prune.go
@@ -13,13 +13,12 @@ import (
)
var (
- pruneContainersCommand cliconfig.ContainersPrune
+ pruneContainersCommand cliconfig.PruneContainersValues
pruneContainersDescription = `
podman container prune
Removes all exited containers
`
-
_pruneContainersCommand = &cobra.Command{
Use: "prune",
Short: "Remove all stopped containers",
@@ -34,9 +33,12 @@ var (
func init() {
pruneContainersCommand.Command = _pruneContainersCommand
+ pruneContainersCommand.SetUsageTemplate(UsageTemplate())
+ flags := pruneContainersCommand.Flags()
+ flags.BoolVarP(&pruneContainersCommand.Force, "force", "f", false, "Force removal of a running container. The default is false")
}
-func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force bool) error {
+func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force, volumes bool) error {
var deleteFuncs []shared.ParallelWorkerInput
filter := func(c *libpod.Container) bool {
@@ -56,7 +58,7 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork
for _, container := range delContainers {
con := container
f := func() error {
- return runtime.RemoveContainer(ctx, con, force)
+ return runtime.RemoveContainer(ctx, con, force, volumes)
}
deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{
@@ -69,7 +71,7 @@ func pruneContainers(runtime *adapter.LocalRuntime, ctx context.Context, maxWork
return printParallelOutput(deleteErrors, errCount)
}
-func pruneContainersCmd(c *cliconfig.ContainersPrune) error {
+func pruneContainersCmd(c *cliconfig.PruneContainersValues) error {
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
@@ -82,5 +84,5 @@ func pruneContainersCmd(c *cliconfig.ContainersPrune) error {
}
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
- return pruneContainers(runtime, getContext(), maxWorkers, c.Bool("force"))
+ return pruneContainers(runtime, getContext(), maxWorkers, c.Bool("force"), c.Bool("volumes"))
}
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go
new file mode 100644
index 000000000..89114fda1
--- /dev/null
+++ b/cmd/podman/cp.go
@@ -0,0 +1,257 @@
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/buildah/util"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/chrootuser"
+ "github.com/containers/storage"
+ "github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/chrootarchive"
+ "github.com/containers/storage/pkg/idtools"
+ digest "github.com/opencontainers/go-digest"
+ specs "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/cobra"
+)
+
+var (
+ cpCommand cliconfig.CpValues
+
+ cpDescription = "Copy files/folders between a container and the local filesystem"
+ _cpCommand = &cobra.Command{
+ Use: "cp",
+ Short: "Copy files/folders between a container and the local filesystem",
+ Long: cpDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ cpCommand.InputArgs = args
+ cpCommand.GlobalFlags = MainGlobalOpts
+ return cpCmd(&cpCommand)
+ },
+ Example: "[CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH",
+ }
+)
+
+func init() {
+ cpCommand.Command = _cpCommand
+ rootCmd.AddCommand(cpCommand.Command)
+}
+
+func cpCmd(c *cliconfig.CpValues) error {
+ args := c.InputArgs
+ if len(args) != 2 {
+ return errors.Errorf("you must provide a source path and a destination path")
+ }
+
+ runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ return copyBetweenHostAndContainer(runtime, args[0], args[1])
+}
+
+func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest string) error {
+
+ srcCtr, srcPath := parsePath(runtime, src)
+ destCtr, destPath := parsePath(runtime, dest)
+
+ if (srcCtr == nil && destCtr == nil) || (srcCtr != nil && destCtr != nil) {
+ return errors.Errorf("invalid arguments %s, %s you must use just one container", src, dest)
+ }
+
+ if len(srcPath) == 0 || len(destPath) == 0 {
+ return errors.Errorf("invalid arguments %s, %s you must specify paths", src, dest)
+ }
+ ctr := srcCtr
+ isFromHostToCtr := (ctr == nil)
+ if isFromHostToCtr {
+ ctr = destCtr
+ }
+
+ mountPoint, err := ctr.Mount()
+ if err != nil {
+ return err
+ }
+ defer ctr.Unmount(false)
+ user, err := getUser(mountPoint, ctr.User())
+ if err != nil {
+ return err
+ }
+ idMappingOpts, err := ctr.IDMappings()
+ if err != nil {
+ return errors.Wrapf(err, "error getting IDMappingOptions")
+ }
+ containerOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)}
+ hostUID, hostGID, err := util.GetHostIDs(convertIDMap(idMappingOpts.UIDMap), convertIDMap(idMappingOpts.GIDMap), user.UID, user.GID)
+ if err != nil {
+ return err
+ }
+
+ hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
+
+ var glob []string
+ if isFromHostToCtr {
+ if filepath.IsAbs(destPath) {
+ destPath = filepath.Join(mountPoint, destPath)
+
+ } else {
+ if err = idtools.MkdirAllAndChownNew(filepath.Join(mountPoint, ctr.WorkingDir()), 0755, hostOwner); err != nil {
+ return errors.Wrapf(err, "error creating directory %q", destPath)
+ }
+ destPath = filepath.Join(mountPoint, ctr.WorkingDir(), destPath)
+ }
+ } else {
+ if filepath.IsAbs(srcPath) {
+ srcPath = filepath.Join(mountPoint, srcPath)
+ } else {
+ srcPath = filepath.Join(mountPoint, ctr.WorkingDir(), srcPath)
+ }
+ }
+ glob, err = filepath.Glob(srcPath)
+ if err != nil {
+ return errors.Wrapf(err, "invalid glob %q", srcPath)
+ }
+ if len(glob) == 0 {
+ glob = append(glob, srcPath)
+ }
+ if !filepath.IsAbs(destPath) {
+ dir, err := os.Getwd()
+ if err != nil {
+ return errors.Wrapf(err, "err getting current working directory")
+ }
+ destPath = filepath.Join(dir, destPath)
+ }
+
+ var lastError error
+ for _, src := range glob {
+ err := copy(src, destPath, dest, idMappingOpts, &containerOwner)
+ if lastError != nil {
+ logrus.Error(lastError)
+ }
+ lastError = err
+ }
+ return lastError
+}
+
+func getUser(mountPoint string, userspec string) (specs.User, error) {
+ uid, gid, err := chrootuser.GetUser(mountPoint, userspec)
+ u := specs.User{
+ UID: uid,
+ GID: gid,
+ Username: userspec,
+ }
+ if !strings.Contains(userspec, ":") {
+ groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
+ if err2 != nil {
+ if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil {
+ err = err2
+ }
+ } else {
+ u.AdditionalGids = groups
+ }
+
+ }
+ return u, err
+}
+
+func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) {
+ pathArr := strings.SplitN(path, ":", 2)
+ if len(pathArr) == 2 {
+ ctr, err := runtime.LookupContainer(pathArr[0])
+ if err == nil {
+ return ctr, pathArr[1]
+ }
+ }
+ return nil, path
+}
+
+func getPathInfo(path string) (string, os.FileInfo, error) {
+ path, err := filepath.EvalSymlinks(path)
+ if err != nil {
+ return "", nil, errors.Wrapf(err, "error evaluating symlinks %q", path)
+ }
+ srcfi, err := os.Stat(path)
+ if err != nil {
+ return "", nil, errors.Wrapf(err, "error reading path %q", path)
+ }
+ return path, srcfi, nil
+}
+
+func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair) error {
+ srcPath, err := filepath.EvalSymlinks(src)
+ if err != nil {
+ return errors.Wrapf(err, "error evaluating symlinks %q", srcPath)
+ }
+
+ srcPath, srcfi, err := getPathInfo(srcPath)
+ if err != nil {
+ return err
+ }
+ destdir := destPath
+ if !srcfi.IsDir() && !strings.HasSuffix(dest, string(os.PathSeparator)) {
+ destdir = filepath.Dir(destPath)
+ }
+ if err = os.MkdirAll(destdir, 0755); err != nil {
+ return errors.Wrapf(err, "error creating directory %q", destdir)
+ }
+
+ // return functions for copying items
+ copyFileWithTar := chrootarchive.CopyFileWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
+ copyWithTar := chrootarchive.CopyWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
+ untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
+
+ if srcfi.IsDir() {
+
+ logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
+ if err = copyWithTar(srcPath, destPath); err != nil {
+ return errors.Wrapf(err, "error copying %q to %q", srcPath, dest)
+ }
+ return nil
+ }
+ if !archive.IsArchivePath(srcPath) {
+ // This srcPath is a file, and either it's not an
+ // archive, or we don't care whether or not it's an
+ // archive.
+ destfi, err := os.Stat(destPath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return errors.Wrapf(err, "failed to get stat of dest path %s", destPath)
+ }
+ }
+ if destfi != nil && destfi.IsDir() {
+ destPath = filepath.Join(destPath, filepath.Base(srcPath))
+ }
+ // Copy the file, preserving attributes.
+ logrus.Debugf("copying %q to %q", srcPath, destPath)
+ if err = copyFileWithTar(srcPath, destPath); err != nil {
+ return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath)
+ }
+ return nil
+ }
+ // We're extracting an archive into the destination directory.
+ logrus.Debugf("extracting contents of %q into %q", srcPath, destPath)
+ if err = untarPath(srcPath, destPath); err != nil {
+ return errors.Wrapf(err, "error extracting %q into %q", srcPath, destPath)
+ }
+ return nil
+}
+
+func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) {
+ for _, idmap := range idMaps {
+ tempIDMap := specs.LinuxIDMapping{
+ ContainerID: uint32(idmap.ContainerID),
+ HostID: uint32(idmap.HostID),
+ Size: uint32(idmap.Size),
+ }
+ convertedIDMap = append(convertedIDMap, tempIDMap)
+ }
+ return convertedIDMap
+}
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 2f8742052..7bfb070c7 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -49,7 +49,9 @@ var (
createCommand.GlobalFlags = MainGlobalOpts
return createCmd(&createCommand)
},
- Example: "IMAGE [COMMAND [ARG...]]",
+ Example: `podman create alpine ls
+ podman create --annotation HELLO=WORLD alpine ls
+ podman create -t -i --name myctr alpine ls`,
}
defaultEnvVariables = map[string]string{
@@ -60,12 +62,12 @@ var (
func init() {
createCommand.PodmanCommand.Command = _createCommand
+ createCommand.SetUsageTemplate(UsageTemplate())
getCreateFlags(&createCommand.PodmanCommand)
flags := createCommand.Flags()
flags.SetInterspersed(true)
- rootCmd.AddCommand(createCommand.Command)
}
func createCmd(c *cliconfig.CreateValues) error {
@@ -646,9 +648,10 @@ func parseCreateOpts(ctx context.Context, c *cliconfig.PodmanCommand, runtime *l
}
var ImageVolumes map[string]struct{}
- if data != nil {
+ if data != nil && c.String("image-volume") != "ignore" {
ImageVolumes = data.Config.Volumes
}
+
var imageVolType = map[string]string{
"bind": "",
"tmpfs": "",
diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go
index 04a659ab6..7d4cc1b58 100644
--- a/cmd/podman/diff.go
+++ b/cmd/podman/diff.go
@@ -52,6 +52,7 @@ var (
func init() {
diffCommand.Command = _diffCommand
+ diffCommand.SetUsageTemplate(UsageTemplate())
flags := diffCommand.Flags()
flags.BoolVar(&diffCommand.Archive, "archive", true, "Save the diff as a tar archive")
@@ -59,9 +60,8 @@ func init() {
flags.MarkHidden("archive")
- rootCmd.AddCommand(diffCommand.Command)
-
}
+
func formatJSON(output []diffOutputParams) (diffJSONOutput, error) {
jsonStruct := diffJSONOutput{}
for _, output := range output {
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index b4f66bc03..74808768e 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -35,6 +35,7 @@ var (
func init() {
execCommand.Command = _execCommand
+ execCommand.SetUsageTemplate(UsageTemplate())
flags := execCommand.Flags()
flags.SetInterspersed(false)
flags.StringSliceVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables")
@@ -46,7 +47,6 @@ func init() {
flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container")
- rootCmd.AddCommand(execCommand.Command)
}
func execCmd(c *cliconfig.ExecValues) error {
diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go
index a21e8fcf8..7645bb716 100644
--- a/cmd/podman/exists.go
+++ b/cmd/podman/exists.go
@@ -41,7 +41,7 @@ var (
imageExistsCommand.GlobalFlags = MainGlobalOpts
return imageExistsCmd(&imageExistsCommand)
},
- Example: "IMAGE-NAME",
+ Example: `podman image exists imageID`,
}
_containerExistsCommand = &cobra.Command{
@@ -54,7 +54,7 @@ var (
return containerExistsCmd(&containerExistsCommand)
},
- Example: "CONTAINER-NAME",
+ Example: `podman container exists containerID`,
}
_podExistsCommand = &cobra.Command{
@@ -66,14 +66,17 @@ var (
podExistsCommand.GlobalFlags = MainGlobalOpts
return podExistsCmd(&podExistsCommand)
},
- Example: "POD-NAME",
+ Example: `podman pod exists podID`,
}
)
func init() {
imageExistsCommand.Command = _imageExistsCommand
+ imageExistsCommand.SetUsageTemplate(UsageTemplate())
containerExistsCommand.Command = _containerExistsCommand
+ containerExistsCommand.SetUsageTemplate(UsageTemplate())
podExistsCommand.Command = _podExistsCommand
+ podExistsCommand.SetUsageTemplate(UsageTemplate())
}
func imageExistsCmd(c *cliconfig.ImageExistsValues) error {
diff --git a/cmd/podman/export.go b/cmd/podman/export.go
index bd9e38b0c..2ce8186a1 100644
--- a/cmd/podman/export.go
+++ b/cmd/podman/export.go
@@ -31,9 +31,9 @@ var (
func init() {
exportCommand.Command = _exportCommand
+ exportCommand.SetUsageTemplate(UsageTemplate())
flags := exportCommand.Flags()
flags.StringVarP(&exportCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT")
- rootCmd.AddCommand(exportCommand.Command)
}
// exportCmd saves a container to a tarball on disk
diff --git a/cmd/podman/generate.go b/cmd/podman/generate.go
index a5e15fdd6..66cb7a465 100644
--- a/cmd/podman/generate.go
+++ b/cmd/podman/generate.go
@@ -17,5 +17,5 @@ var generateCommand = cliconfig.PodmanCommand{
func init() {
generateCommand.AddCommand(getGenerateSubCommands()...)
- rootCmd.AddCommand(generateCommand.Command)
+ generateCommand.SetUsageTemplate(UsageTemplate())
}
diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go
index 8e3432d12..ddb2daa34 100644
--- a/cmd/podman/generate_kube.go
+++ b/cmd/podman/generate_kube.go
@@ -31,6 +31,7 @@ var (
func init() {
containerKubeCommand.Command = _containerKubeCommand
+ containerKubeCommand.SetUsageTemplate(UsageTemplate())
flags := containerKubeCommand.Flags()
flags.BoolVarP(&containerKubeCommand.Service, "service", "s", false, "Generate YAML for kubernetes service object")
}
diff --git a/cmd/podman/history.go b/cmd/podman/history.go
index 97e501947..6791257d9 100644
--- a/cmd/podman/history.go
+++ b/cmd/podman/history.go
@@ -53,6 +53,7 @@ var (
func init() {
historyCommand.Command = _historyCommand
+ historyCommand.SetUsageTemplate(UsageTemplate())
flags := historyCommand.Flags()
flags.StringVar(&historyCommand.Format, "format", "", "Change the output to JSON or a Go template")
flags.BoolVarP(&historyCommand.Human, "human", "H", true, "Display sizes and dates in human readable format")
@@ -60,9 +61,8 @@ func init() {
flags.BoolVar(&historyCommand.NoTrunc, "no-trunc", false, "Do not truncate the output")
flags.BoolVarP(&historyCommand.Quiet, "quiet", "q", false, "Display the numeric IDs only")
- rootCmd.AddCommand(historyCommand.Command)
-
}
+
func historyCmd(c *cliconfig.HistoryValues) error {
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index ac7ff4944..4f9c7cd6a 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -18,18 +18,21 @@ var (
//imageSubCommands are implemented both in local and remote clients
var imageSubCommands = []*cobra.Command{
+ _buildCommand,
_historyCommand,
_imageExistsCommand,
- _inspectCommand,
_imagesCommand,
+ _importCommand,
+ _inspectCommand,
_pruneImagesCommand,
+ _pullCommand,
_pushCommand,
_rmiCommand,
_tagCommand,
}
func init() {
+ imageCommand.SetUsageTemplate(UsageTemplate())
imageCommand.AddCommand(imageSubCommands...)
imageCommand.AddCommand(getImageSubCommands()...)
- rootCmd.AddCommand(imageCommand.Command)
}
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index 6f5a3e9f1..b269f6440 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -102,6 +102,7 @@ var (
func init() {
imagesCommand.Command = _imagesCommand
+ imagesCommand.SetUsageTemplate(UsageTemplate())
flags := imagesCommand.Flags()
flags.BoolVarP(&imagesCommand.All, "all", "a", false, "Show all images (default hides intermediate images)")
flags.BoolVar(&imagesCommand.Digests, "digests", false, "Show digests")
@@ -114,8 +115,6 @@ func init() {
flags.BoolVarP(&imagesCommand.Quiet, "quiet", "q", false, "Display only image IDs")
flags.StringVar(&imagesCommand.Sort, "sort", "created", "Sort by created, id, repository, size, or tag")
- rootCmd.AddCommand(imagesCommand.Command)
-
}
func imagesCmd(c *cliconfig.ImagesValues) error {
@@ -248,8 +247,12 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma
}
// get all specified repo:tag pairs and print them separately
+ repopairs, err := image.ReposToMap(img.Names())
+ if err != nil {
+ logrus.Errorf("error finding tag/digest for %s", img.ID())
+ }
outer:
- for repo, tags := range image.ReposToMap(img.Names()) {
+ for repo, tags := range repopairs {
for _, tag := range tags {
size, err := img.Size(ctx)
var sizeStr string
diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go
index ba99c5bde..cc0dcb99a 100644
--- a/cmd/podman/images_prune.go
+++ b/cmd/podman/images_prune.go
@@ -30,6 +30,7 @@ var (
func init() {
pruneImagesCommand.Command = _pruneImagesCommand
+ pruneImagesCommand.SetUsageTemplate(UsageTemplate())
flags := pruneImagesCommand.Flags()
flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones")
}
diff --git a/cmd/podman/import.go b/cmd/podman/import.go
index 52d144eb3..32f79757b 100644
--- a/cmd/podman/import.go
+++ b/cmd/podman/import.go
@@ -31,12 +31,12 @@ var (
func init() {
importCommand.Command = _importCommand
+ importCommand.SetUsageTemplate(UsageTemplate())
flags := importCommand.Flags()
flags.StringSliceVarP(&importCommand.Change, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR")
flags.StringVarP(&importCommand.Message, "message", "m", "", "Set commit message for imported image")
flags.BoolVarP(&importCommand.Quiet, "quiet", "q", false, "Suppress output")
- rootCmd.AddCommand(importCommand.Command)
}
func importCmd(c *cliconfig.ImportValues) error {
diff --git a/cmd/podman/info.go b/cmd/podman/info.go
index 0896e16a4..06dbbd748 100644
--- a/cmd/podman/info.go
+++ b/cmd/podman/info.go
@@ -26,18 +26,18 @@ var (
infoCommand.GlobalFlags = MainGlobalOpts
return infoCmd(&infoCommand)
},
- Example: "",
+ Example: `podman info`,
}
)
func init() {
infoCommand.Command = _infoCommand
+ infoCommand.SetUsageTemplate(UsageTemplate())
flags := infoCommand.Flags()
flags.BoolVarP(&infoCommand.Debug, "debug", "D", false, "Display additional debug information")
flags.StringVarP(&infoCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template")
- rootCmd.AddCommand(infoCommand.Command)
}
func infoCmd(c *cliconfig.InfoValues) error {
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index b2979c6ae..a1f3ef81f 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -34,19 +34,21 @@ var (
inspectCommand.GlobalFlags = MainGlobalOpts
return inspectCmd(&inspectCommand)
},
- Example: "CONTAINER-OR-IMAGE [CONTAINER-OR-IMAGE]...",
+ Example: `podman inspect alpine
+ podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine
+ podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`,
}
)
func init() {
inspectCommand.Command = _inspectCommand
+ inspectCommand.SetUsageTemplate(UsageTemplate())
flags := inspectCommand.Flags()
flags.StringVarP(&inspectCommand.TypeObject, "type", "t", inspectAll, "Return JSON for specified type, (e.g image, container or task)")
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")
- rootCmd.AddCommand(inspectCommand.Command)
}
func inspectCmd(c *cliconfig.InspectValues) error {
diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go
index 04c0fd531..d922cf721 100644
--- a/cmd/podman/kill.go
+++ b/cmd/podman/kill.go
@@ -34,13 +34,13 @@ var (
func init() {
killCommand.Command = _killCommand
+ killCommand.SetUsageTemplate(UsageTemplate())
flags := killCommand.Flags()
flags.BoolVarP(&killCommand.All, "all", "a", false, "Signal all running containers")
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")
- rootCmd.AddCommand(killCommand.Command)
}
// killCmd kills one or more containers with a signal
diff --git a/cmd/podman/load.go b/cmd/podman/load.go
index 90268ca8f..34a51cd0d 100644
--- a/cmd/podman/load.go
+++ b/cmd/podman/load.go
@@ -34,12 +34,12 @@ var (
func init() {
loadCommand.Command = _loadCommand
+ loadCommand.SetUsageTemplate(UsageTemplate())
flags := loadCommand.Flags()
flags.StringVarP(&loadCommand.Input, "input", "i", "/dev/stdin", "Read from archive file, default is STDIN")
flags.BoolVarP(&loadCommand.Quiet, "quiet", "q", false, "Suppress the output")
flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)")
- rootCmd.AddCommand(loadCommand.Command)
}
// loadCmd gets the image/file to be loaded from the command line
diff --git a/cmd/podman/login.go b/cmd/podman/login.go
index 5ab0713e5..3eacab54a 100644
--- a/cmd/podman/login.go
+++ b/cmd/podman/login.go
@@ -29,12 +29,15 @@ var (
loginCommand.GlobalFlags = MainGlobalOpts
return loginCmd(&loginCommand)
},
- Example: "REGISTRY",
+ Example: `podman login -u testuser -p testpassword localhost:5000
+ podman login --authfile authdir/myauths.json quay.io
+ podman login -u testuser -p testpassword localhost:5000`,
}
)
func init() {
loginCommand.Command = _loginCommand
+ loginCommand.SetUsageTemplate(UsageTemplate())
flags := loginCommand.Flags()
flags.StringVar(&loginCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
@@ -43,8 +46,8 @@ func init() {
flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry")
flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry")
+ flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin")
- rootCmd.AddCommand(loginCommand.Command)
}
// loginCmd uses the authentication package to store a user's authenticated credentials
@@ -90,8 +93,26 @@ func loginCmd(c *cliconfig.LoginValues) error {
}
ctx := getContext()
+
+ password := c.Password
+
+ if c.Flag("password-stdin").Changed {
+ var stdinPasswordStrBuilder strings.Builder
+ if c.Password != "" {
+ return errors.Errorf("Can't specify both --password-stdin and --password")
+ }
+ if c.Username == "" {
+ return errors.Errorf("Must provide --username with --password-stdin")
+ }
+ scanner := bufio.NewScanner(os.Stdin)
+ for scanner.Scan() {
+ fmt.Fprint(&stdinPasswordStrBuilder, scanner.Text())
+ }
+ password = stdinPasswordStrBuilder.String()
+ }
+
// If no username and no password is specified, try to use existing ones.
- if c.Username == "" && c.Password == "" {
+ if c.Username == "" && password == "" {
fmt.Println("Authenticating with existing credentials...")
if err := docker.CheckAuth(ctx, sc, userFromAuthFile, passFromAuthFile, server); err == nil {
fmt.Println("Existing credentials are valid. Already logged in to", server)
@@ -100,7 +121,7 @@ func loginCmd(c *cliconfig.LoginValues) error {
fmt.Println("Existing credentials are invalid, please enter valid username and password")
}
- username, password, err := getUserAndPass(c.Username, c.Password, userFromAuthFile)
+ username, password, err := getUserAndPass(c.Username, password, userFromAuthFile)
if err != nil {
return errors.Wrapf(err, "error getting username and password")
}
diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go
index 02bde7857..5fecf814e 100644
--- a/cmd/podman/logout.go
+++ b/cmd/podman/logout.go
@@ -28,11 +28,11 @@ var (
func init() {
logoutCommand.Command = _logoutCommand
+ logoutCommand.SetUsageTemplate(UsageTemplate())
flags := logoutCommand.Flags()
flags.BoolVarP(&logoutCommand.All, "all", "a", false, "Remove the cached credentials for all registries in the auth file")
flags.StringVar(&logoutCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
- rootCmd.AddCommand(logoutCommand.Command)
}
// logoutCmd uses the authentication package to remove the authenticated of a registry
diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go
index 83f900e63..6962a1f6d 100644
--- a/cmd/podman/logs.go
+++ b/cmd/podman/logs.go
@@ -32,6 +32,7 @@ var (
func init() {
logsCommand.Command = _logsCommand
+ logsCommand.SetUsageTemplate(UsageTemplate())
flags := logsCommand.Flags()
flags.BoolVar(&logsCommand.Details, "details", false, "Show extra details provided to the logs")
flags.BoolVarP(&logsCommand.Follow, "follow", "f", false, "Follow log output. The default is false")
@@ -43,7 +44,6 @@ func init() {
flags.SetInterspersed(false)
- rootCmd.AddCommand(logsCommand.Command)
}
func logsCmd(c *cliconfig.LogsValues) error {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 32ecaede7..f9820c075 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -27,6 +27,26 @@ var (
exitCode = 125
)
+// Commands that the remote and local client have
+// implemented.
+var mainCommands = []*cobra.Command{
+ _buildCommand,
+ _exportCommand,
+ _historyCommand,
+ _imagesCommand,
+ _importCommand,
+ _infoCommand,
+ _inspectCommand,
+ _killCommand,
+ _pullCommand,
+ _pushCommand,
+ _rmiCommand,
+ _tagCommand,
+ _versionCommand,
+ imageCommand.Command,
+ systemCommand.Command,
+}
+
var cmdsNotRequiringRootless = map[*cobra.Command]bool{
_versionCommand: true,
_createCommand: true,
@@ -40,7 +60,7 @@ var cmdsNotRequiringRootless = map[*cobra.Command]bool{
_killCommand: true,
_pauseCommand: true,
_restartCommand: true,
- _runCommmand: true,
+ _runCommand: true,
_unpauseCommand: true,
_searchCommand: true,
_statsCommand: true,
@@ -92,6 +112,8 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console")
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory")
+ rootCmd.AddCommand(mainCommands...)
+ rootCmd.AddCommand(getMainCommands()...)
}
func initConfig() {
@@ -185,7 +207,7 @@ func main() {
exitCode = status.ExitStatus()
}
}
- fmt.Fprintln(os.Stderr, err.Error())
+ fmt.Fprintln(os.Stderr, "Error:", err.Error())
}
} else {
// The exitCode modified from 125, indicates an application
diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go
index 36ca8bcfb..ce7c22d60 100644
--- a/cmd/podman/mount.go
+++ b/cmd/podman/mount.go
@@ -39,13 +39,13 @@ var (
func init() {
mountCommand.Command = _mountCommand
+ mountCommand.SetUsageTemplate(UsageTemplate())
flags := mountCommand.Flags()
flags.BoolVarP(&mountCommand.All, "all", "a", false, "Mount all containers")
flags.StringVar(&mountCommand.Format, "format", "", "Change the output format to Go template")
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")
- rootCmd.AddCommand(mountCommand.Command)
}
// jsonMountPoint stores info about each container
diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go
index 466a3b136..a840cbe49 100644
--- a/cmd/podman/pause.go
+++ b/cmd/podman/pause.go
@@ -34,10 +34,10 @@ var (
func init() {
pauseCommand.Command = _pauseCommand
+ pauseCommand.SetUsageTemplate(UsageTemplate())
flags := pauseCommand.Flags()
flags.BoolVarP(&pauseCommand.All, "all", "a", false, "Pause all running containers")
- rootCmd.AddCommand(pauseCommand.Command)
}
func pauseCmd(c *cliconfig.PauseValues) error {
diff --git a/cmd/podman/play.go b/cmd/podman/play.go
index ff8320a6b..495a1f170 100644
--- a/cmd/podman/play.go
+++ b/cmd/podman/play.go
@@ -5,19 +5,18 @@ import (
"github.com/spf13/cobra"
)
-var playCommand cliconfig.PodmanCommand
-
-func init() {
- var playDescription = "Play a pod and its containers from a structured file."
- playCommand.Command = &cobra.Command{
+var (
+ playCommand cliconfig.PodmanCommand
+ playDescription = "Play a pod and its containers from a structured file."
+ _playCommand = &cobra.Command{
Use: "play",
Short: "Play a pod",
Long: playDescription,
}
-
-}
+)
func init() {
+ playCommand.Command = _playCommand
+ playCommand.SetUsageTemplate(UsageTemplate())
playCommand.AddCommand(getPlaySubCommands()...)
- rootCmd.AddCommand(playCommand.Command)
}
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
index b314c1278..4ecd30cd4 100644
--- a/cmd/podman/play_kube.go
+++ b/cmd/podman/play_kube.go
@@ -43,6 +43,7 @@ var (
func init() {
playKubeCommand.Command = _playKubeCommand
+ playKubeCommand.SetUsageTemplate(UsageTemplate())
flags := playKubeCommand.Flags()
flags.StringVar(&playKubeCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&playKubeCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
index 4104c39c7..e988875ab 100644
--- a/cmd/podman/pod.go
+++ b/cmd/podman/pod.go
@@ -20,5 +20,5 @@ var podCommand = cliconfig.PodmanCommand{
func init() {
podCommand.AddCommand(getPodSubCommands()...)
- rootCmd.AddCommand(podCommand.Command)
+ podCommand.SetUsageTemplate(UsageTemplate())
}
diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go
index 9ac5d94a9..0a0b86aab 100644
--- a/cmd/podman/pod_create.go
+++ b/cmd/podman/pod_create.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -39,6 +38,7 @@ var (
func init() {
podCreateCommand.Command = _podCreateCommand
+ podCreateCommand.SetUsageTemplate(UsageTemplate())
flags := podCreateCommand.Flags()
flags.SetInterspersed(false)
@@ -82,9 +82,6 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error {
if !c.Infra {
return errors.Errorf("you must have an infra container to publish port bindings to the host")
}
- if rootless.IsRootless() {
- return errors.Errorf("rootless networking does not allow port binding to the host")
- }
}
if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" {
diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go
index f4c357e96..58b15328e 100644
--- a/cmd/podman/pod_inspect.go
+++ b/cmd/podman/pod_inspect.go
@@ -23,12 +23,13 @@ var (
podInspectCommand.GlobalFlags = MainGlobalOpts
return podInspectCmd(&podInspectCommand)
},
- Example: "[POD_NAME_OR_ID]",
+ Example: `podman pod inspect podID`,
}
)
func init() {
podInspectCommand.Command = _podInspectCommand
+ podInspectCommand.SetUsageTemplate(UsageTemplate())
flags := podInspectCommand.Flags()
flags.BoolVarP(&podInspectCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go
index 43411a988..febc820cd 100644
--- a/cmd/podman/pod_kill.go
+++ b/cmd/podman/pod_kill.go
@@ -30,6 +30,7 @@ var (
func init() {
podKillCommand.Command = _podKillCommand
+ podKillCommand.SetUsageTemplate(UsageTemplate())
flags := podKillCommand.Flags()
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")
diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go
index a54783d88..2059727ae 100644
--- a/cmd/podman/pod_pause.go
+++ b/cmd/podman/pod_pause.go
@@ -27,6 +27,7 @@ var (
func init() {
podPauseCommand.Command = _podPauseCommand
+ podPauseCommand.SetUsageTemplate(UsageTemplate())
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")
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
index 991e294bf..49af91a1e 100644
--- a/cmd/podman/pod_ps.go
+++ b/cmd/podman/pod_ps.go
@@ -131,6 +131,7 @@ var (
func init() {
podPsCommand.Command = _podPsCommand
+ podPsCommand.SetUsageTemplate(UsageTemplate())
flags := podPsCommand.Flags()
flags.BoolVar(&podPsCommand.CtrNames, "ctr-names", false, "Display the container names")
flags.BoolVar(&podPsCommand.CtrIDs, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated")
diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go
index 4d05205ea..f75d84956 100644
--- a/cmd/podman/pod_restart.go
+++ b/cmd/podman/pod_restart.go
@@ -28,6 +28,7 @@ var (
func init() {
podRestartCommand.Command = _podRestartCommand
+ podRestartCommand.SetUsageTemplate(UsageTemplate())
flags := podRestartCommand.Flags()
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")
diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go
index 445a641df..54cee2a50 100644
--- a/cmd/podman/pod_rm.go
+++ b/cmd/podman/pod_rm.go
@@ -26,12 +26,15 @@ If --force is specified, all containers will be stopped, then removed.
podRmCommand.GlobalFlags = MainGlobalOpts
return podRmCmd(&podRmCommand)
},
- Example: "[POD ...]",
+ Example: `podman pod rm mywebserverpod
+ podman pod rm -f 860a4b23
+ podman pod rm -f -a`,
}
)
func init() {
podRmCommand.Command = _podRmCommand
+ podRmCommand.SetUsageTemplate(UsageTemplate())
flags := podRmCommand.Flags()
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")
diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go
index 8b35d3e04..829fe2107 100644
--- a/cmd/podman/pod_start.go
+++ b/cmd/podman/pod_start.go
@@ -32,6 +32,7 @@ var (
func init() {
podStartCommand.Command = _podStartCommand
+ podStartCommand.SetUsageTemplate(UsageTemplate())
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")
diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go
index ccbcf9f7c..7afab6be9 100644
--- a/cmd/podman/pod_stats.go
+++ b/cmd/podman/pod_stats.go
@@ -2,7 +2,11 @@ package main
import (
"fmt"
+ "html/template"
+ "os"
+ "reflect"
"strings"
+ "text/tabwriter"
"time"
"encoding/json"
@@ -34,6 +38,7 @@ var (
func init() {
podStatsCommand.Command = _podStatsCommand
+ podStatsCommand.SetUsageTemplate(UsageTemplate())
flags := podStatsCommand.Flags()
flags.BoolVarP(&podStatsCommand.All, "all", "a", false, "Provide stats for all running pods")
flags.StringVar(&podStatsCommand.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template")
@@ -136,6 +141,25 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error {
step = 0
}
+ headerNames := make(map[string]string)
+ if c.Format != "" {
+ // Make a map of the field names for the headers
+ v := reflect.ValueOf(podStatOut{})
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ value := strings.ToUpper(splitCamelCase(t.Field(i).Name))
+ switch value {
+ case "CPU":
+ value = value + " %"
+ case "MEM":
+ value = value + " %"
+ case "MEM USAGE":
+ value = "MEM USAGE / LIMIT"
+ }
+ headerNames[t.Field(i).Name] = value
+ }
+ }
+
for i := 0; i < times; i += step {
var newStats []*libpod.PodContainerStats
for _, p := range pods {
@@ -163,7 +187,14 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error {
outputJson(newStats)
} else {
- outputToStdOut(newStats)
+ results := podContainerStatsToPodStatOut(newStats)
+ if len(format) == 0 {
+ outputToStdOut(results)
+ } else {
+ if err := printPSFormat(c.Format, results, headerNames); err != nil {
+ return err
+ }
+ }
}
time.Sleep(time.Second)
previousPodStats := new([]*libpod.PodContainerStats)
@@ -177,28 +208,88 @@ func podStatsCmd(c *cliconfig.PodStatsValues) error {
return nil
}
-func outputToStdOut(stats []*libpod.PodContainerStats) {
- outFormat := ("%-14s %-14s %-12s %-6s %-19s %-6s %-19s %-19s %-4s\n")
- fmt.Printf(outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS")
- for _, i := range stats {
- if len(i.ContainerStats) == 0 {
- fmt.Printf(outFormat, i.Pod.ID()[:12], "--", "--", "--", "--", "--", "--", "--", "--")
- }
- for _, c := range i.ContainerStats {
- cpu := floatToPercentString(c.CPU)
- memUsage := combineHumanValues(c.MemUsage, c.MemLimit)
- memPerc := floatToPercentString(c.MemPerc)
- netIO := combineHumanValues(c.NetInput, c.NetOutput)
- blockIO := combineHumanValues(c.BlockInput, c.BlockOutput)
- pids := pidsToString(c.PIDs)
- containerName := c.Name
- if len(c.Name) > 10 {
- containerName = containerName[:10]
+func podContainerStatsToPodStatOut(stats []*libpod.PodContainerStats) []*podStatOut {
+ var out []*podStatOut
+ for _, p := range stats {
+ for _, c := range p.ContainerStats {
+ o := podStatOut{
+ CPU: floatToPercentString(c.CPU),
+ MemUsage: combineHumanValues(c.MemUsage, c.MemLimit),
+ Mem: floatToPercentString(c.MemPerc),
+ NetIO: combineHumanValues(c.NetInput, c.NetOutput),
+ BlockIO: combineHumanValues(c.BlockInput, c.BlockOutput),
+ PIDS: pidsToString(c.PIDs),
+ CID: c.ContainerID[:12],
+ Name: c.Name,
+ Pod: p.Pod.ID()[:12],
}
- fmt.Printf(outFormat, i.Pod.ID()[:12], c.ContainerID[:12], containerName, cpu, memUsage, memPerc, netIO, blockIO, pids)
+ out = append(out, &o)
+ }
+ }
+ return out
+}
+
+type podStatOut struct {
+ CPU string
+ MemUsage string
+ Mem string
+ NetIO string
+ BlockIO string
+ PIDS string
+ Pod string
+ CID string
+ Name string
+}
+
+func printPSFormat(format string, stats []*podStatOut, headerNames map[string]string) error {
+ if len(stats) == 0 {
+ return nil
+ }
+
+ // Use a tabwriter to align column format
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
+ // Spit out the header if "table" is present in the format
+ if strings.HasPrefix(format, "table") {
+ hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1)
+ format = hformat
+ headerTmpl, err := template.New("header").Parse(hformat)
+ if err != nil {
+ return err
+ }
+ if err := headerTmpl.Execute(w, headerNames); err != nil {
+ return err
+ }
+ fmt.Fprintln(w, "")
+ }
+
+ // Spit out the data rows now
+ dataTmpl, err := template.New("data").Parse(format)
+ if err != nil {
+ return err
+ }
+ for _, container := range stats {
+ if err := dataTmpl.Execute(w, container); err != nil {
+ return err
+ }
+ fmt.Fprintln(w, "")
+ }
+ // Flush the writer
+ return w.Flush()
+
+}
+
+func outputToStdOut(stats []*podStatOut) {
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+ outFormat := ("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n")
+ fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS")
+ for _, i := range stats {
+ if len(stats) == 0 {
+ fmt.Fprintf(w, outFormat, i.Pod, "--", "--", "--", "--", "--", "--", "--", "--")
+ } else {
+ fmt.Fprintf(w, outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS)
}
}
- fmt.Println()
+ w.Flush()
}
func getPreviousPodContainerStats(podID string, prev []*libpod.PodContainerStats) map[string]*libpod.ContainerStats {
diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go
index 63bd0f5cb..80082045d 100644
--- a/cmd/podman/pod_stop.go
+++ b/cmd/podman/pod_stop.go
@@ -33,6 +33,7 @@ var (
func init() {
podStopCommand.Command = _podStopCommand
+ podStopCommand.SetUsageTemplate(UsageTemplate())
flags := podStopCommand.Flags()
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")
diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go
index f1db2fac5..411c782bd 100644
--- a/cmd/podman/pod_top.go
+++ b/cmd/podman/pod_top.go
@@ -39,6 +39,7 @@ the latest pod.
func init() {
podTopCommand.Command = _podTopCommand
+ podTopCommand.SetUsageTemplate(UsageTemplate())
flags := podTopCommand.Flags()
flags.BoolVarP(&podTopCommand.Latest, "latest,", "l", false, "Act on the latest pod podman is aware of")
flags.BoolVar(&podTopCommand.ListDescriptors, "list-descriptors", false, "")
diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go
index 17b771d4a..a917919c3 100644
--- a/cmd/podman/pod_unpause.go
+++ b/cmd/podman/pod_unpause.go
@@ -28,6 +28,7 @@ var (
func init() {
podUnpauseCommand.Command = _podUnpauseCommand
+ podUnpauseCommand.SetUsageTemplate(UsageTemplate())
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")
diff --git a/cmd/podman/port.go b/cmd/podman/port.go
index 488ef2ffe..be84da065 100644
--- a/cmd/podman/port.go
+++ b/cmd/podman/port.go
@@ -34,12 +34,12 @@ var (
func init() {
portCommand.Command = _portCommand
+ portCommand.SetUsageTemplate(UsageTemplate())
flags := portCommand.Flags()
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")
- rootCmd.AddCommand(portCommand.Command)
}
func portCmd(c *cliconfig.PortValues) error {
@@ -125,8 +125,13 @@ func portCmd(c *cliconfig.PortValues) error {
if c.All {
fmt.Println(con.ID())
}
+
+ portmappings, err := con.PortMappings()
+ if err != nil {
+ return err
+ }
// Iterate mappings
- for _, v := range con.Config().PortMappings {
+ for _, v := range portmappings {
hostIP := v.HostIP
// Set host IP to 0.0.0.0 if blank
if hostIP == "" {
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index 002975b87..949f78a34 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -172,6 +172,7 @@ var (
func init() {
psCommand.Command = _psCommand
+ psCommand.SetUsageTemplate(UsageTemplate())
flags := psCommand.Flags()
flags.BoolVarP(&psCommand.All, "all", "a", false, "Show all the containers, default is only running containers")
flags.StringSliceVarP(&psCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions given")
@@ -187,9 +188,8 @@ 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")
- rootCmd.AddCommand(psCommand.Command)
-
}
+
func psCmd(c *cliconfig.PsValues) error {
var (
filterFuncs []libpod.ContainerFilter
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index b9ef3f96b..6e060d6f5 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -42,6 +42,7 @@ specified, the image with the 'latest' tag (if it exists) is pulled
func init() {
pullCommand.Command = _pullCommand
+ pullCommand.SetUsageTemplate(UsageTemplate())
flags := pullCommand.Flags()
flags.BoolVar(&pullCommand.AllTags, "all-tags", false, "All tagged images inthe repository will be pulled")
flags.StringVar(&pullCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
@@ -51,7 +52,6 @@ func init() {
flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
- rootCmd.AddCommand(pullCommand.Command)
}
// pullCmd gets the data from the command line and calls pullImage
diff --git a/cmd/podman/push.go b/cmd/podman/push.go
index a1ee00a65..bbe8a4027 100644
--- a/cmd/podman/push.go
+++ b/cmd/podman/push.go
@@ -2,8 +2,6 @@ package main
import (
"fmt"
- "github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/spf13/cobra"
"io"
"os"
"strings"
@@ -11,11 +9,13 @@ import (
"github.com/containers/image/directory"
"github.com/containers/image/manifest"
"github.com/containers/image/types"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
+ "github.com/spf13/cobra"
)
var (
@@ -40,6 +40,7 @@ var (
func init() {
pushCommand.Command = _pushCommand
+ pushCommand.SetUsageTemplate(UsageTemplate())
flags := pushCommand.Flags()
flags.MarkHidden("signature-policy")
flags.StringVar(&pushCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
@@ -52,7 +53,6 @@ func init() {
flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key")
flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
- rootCmd.AddCommand(pushCommand.Command)
}
func pushCmd(c *cliconfig.PushValues) error {
@@ -93,7 +93,7 @@ func pushCmd(c *cliconfig.PushValues) error {
registryCreds = creds
}
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
@@ -131,12 +131,7 @@ func pushCmd(c *cliconfig.PushValues) error {
SignBy: signBy,
}
- newImage, err := runtime.ImageRuntime().NewFromLocal(srcName)
- if err != nil {
- return err
- }
-
authfile := getAuthFile(c.Authfile)
- return newImage.PushImageToHeuristicDestination(getContext(), destName, manifestType, authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil)
+ return runtime.Push(getContext(), srcName, destName, manifestType, authfile, c.SignaturePolicy, writer, c.Compress, so, &dockerRegistryOptions, nil)
}
diff --git a/cmd/podman/refresh.go b/cmd/podman/refresh.go
index 3890188b4..641748452 100644
--- a/cmd/podman/refresh.go
+++ b/cmd/podman/refresh.go
@@ -27,7 +27,7 @@ var (
func init() {
refreshCommand.Command = _refreshCommand
- rootCmd.AddCommand(refreshCommand.Command)
+ refreshCommand.SetUsageTemplate(UsageTemplate())
}
func refreshCmd(c *cliconfig.RefreshValues) error {
diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go
index f94e745ed..235107b5c 100644
--- a/cmd/podman/restart.go
+++ b/cmd/podman/restart.go
@@ -32,6 +32,7 @@ var (
func init() {
restartCommand.Command = _restartCommand
+ restartCommand.SetUsageTemplate(UsageTemplate())
flags := restartCommand.Flags()
flags.BoolVarP(&restartCommand.All, "all", "a", false, "Restart all non-running containers")
flags.BoolVarP(&restartCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
@@ -39,7 +40,6 @@ 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")
- rootCmd.AddCommand(restartCommand.Command)
}
func restartCmd(c *cliconfig.RestartValues) error {
diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go
index daee635f0..556cdb774 100644
--- a/cmd/podman/restore.go
+++ b/cmd/podman/restore.go
@@ -35,6 +35,7 @@ var (
func init() {
restoreCommand.Command = _restoreCommand
+ restoreCommand.SetUsageTemplate(UsageTemplate())
flags := restoreCommand.Flags()
flags.BoolVarP(&restoreCommand.All, "all", "a", false, "Restore all checkpointed containers")
flags.BoolVarP(&restoreCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
@@ -42,7 +43,6 @@ func init() {
// TODO: add ContainerStateCheckpointed
flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
- rootCmd.AddCommand(restoreCommand.Command)
}
func restoreCmd(c *cliconfig.RestoreValues) error {
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index bb9a913c9..d170e5357 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -6,6 +6,7 @@ import (
"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/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -33,13 +34,12 @@ Running containers will not be removed without the -f option.
func init() {
rmCommand.Command = _rmCommand
+ rmCommand.SetUsageTemplate(UsageTemplate())
flags := rmCommand.Flags()
flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers")
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 (Not implemented yet)")
-
- rootCmd.AddCommand(rmCommand.Command)
+ flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container")
}
// saveCmd saves the image to either docker-archive or oci
@@ -61,16 +61,24 @@ func rmCmd(c *cliconfig.RmValues) error {
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
+ }
+ runtime.RemoveContainersFromStorage(c.InputArgs)
+ }
if len(delContainers) == 0 {
return err
}
- fmt.Println(err.Error())
+ if err != nil {
+ fmt.Println(err.Error())
+ }
}
for _, container := range delContainers {
con := container
f := func() error {
- return runtime.RemoveContainer(ctx, con, c.Force)
+ return runtime.RemoveContainer(ctx, con, c.Force, c.Volumes)
}
deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{
diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index 70f58e844..c5bd1e190 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -29,11 +29,10 @@ var (
func init() {
rmiCommand.Command = _rmiCommand
+ rmiCommand.SetUsageTemplate(UsageTemplate())
flags := rmiCommand.Flags()
flags.BoolVarP(&rmiCommand.All, "all", "a", false, "Remove all images")
flags.BoolVarP(&rmiCommand.Force, "force", "f", false, "Force Removal of the image")
-
- rootCmd.AddCommand(rmiCommand.Command)
}
func rmiCmd(c *cliconfig.RmiValues) error {
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 76b29cb84..64f8b6856 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -18,30 +18,29 @@ import (
)
var (
- runCommmand cliconfig.RunValues
+ runCommand cliconfig.RunValues
runDescription = "Runs a command in a new container from the given image"
- _runCommmand = &cobra.Command{
+ _runCommand = &cobra.Command{
Use: "run",
Short: "Run a command in a new container",
Long: runDescription,
RunE: func(cmd *cobra.Command, args []string) error {
- runCommmand.InputArgs = args
- runCommmand.GlobalFlags = MainGlobalOpts
- return runCmd(&runCommmand)
+ runCommand.InputArgs = args
+ runCommand.GlobalFlags = MainGlobalOpts
+ return runCmd(&runCommand)
},
Example: "IMAGE [COMMAND [ARG...]]",
}
)
func init() {
- runCommmand.Command = _runCommmand
- flags := runCommmand.Flags()
+ runCommand.Command = _runCommand
+ runCommand.SetUsageTemplate(UsageTemplate())
+ flags := runCommand.Flags()
flags.SetInterspersed(false)
flags.Bool("sig-proxy", true, "Proxy received signals to the process (default true)")
- getCreateFlags(&runCommmand.PodmanCommand)
-
- rootCmd.AddCommand(runCommmand.Command)
+ getCreateFlags(&runCommand.PodmanCommand)
}
func runCmd(c *cliconfig.RunValues) error {
@@ -119,13 +118,21 @@ func runCmd(c *cliconfig.RunValues) error {
}
}
if err := startAttachCtr(ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), true); err != nil {
+ // We've manually detached from the container
+ // Do not perform cleanup, or wait for container exit code
+ // Just exit immediately
+ if errors.Cause(err) == libpod.ErrDetach {
+ exitCode = 0
+ return nil
+ }
+
// This means the command did not exist
exitCode = 127
if strings.Index(err.Error(), "permission denied") > -1 {
exitCode = 126
}
if c.IsSet("rm") {
- if deleteError := runtime.RemoveContainer(ctx, ctr, true); deleteError != nil {
+ if deleteError := runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil {
logrus.Errorf("unable to remove container %s after failing to start and attach to it", ctr.ID())
}
}
@@ -148,28 +155,12 @@ func runCmd(c *cliconfig.RunValues) error {
exitCode = int(ecode)
}
- if createConfig.Rm {
- return runtime.RemoveContainer(ctx, ctr, true)
- }
-
- if err := ctr.Cleanup(ctx); err != nil {
- // If the container has been removed already, no need to error on cleanup
- // Also, if it was restarted, don't error either
- if errors.Cause(err) == libpod.ErrNoSuchCtr ||
- errors.Cause(err) == libpod.ErrCtrRemoved ||
- errors.Cause(err) == libpod.ErrCtrStateInvalid {
- return nil
- }
-
- return err
- }
-
return nil
}
// Read a container's exit file
func readExitFile(runtimeTmp, ctrID string) (int, error) {
- exitFile := filepath.Join(runtimeTmp, "exits", ctrID)
+ exitFile := filepath.Join(runtimeTmp, "exits", fmt.Sprintf("%s-old", ctrID))
logrus.Debugf("Attempting to read container %s exit code from file %s", ctrID, exitFile)
diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go
index ff8a282d3..f1c61ebda 100644
--- a/cmd/podman/runlabel.go
+++ b/cmd/podman/runlabel.go
@@ -37,6 +37,7 @@ Executes a command as described by a container image label.
func init() {
runlabelCommand.Command = _runlabelCommand
+ runlabelCommand.SetUsageTemplate(UsageTemplate())
flags := runlabelCommand.Flags()
flags.StringVar(&runlabelCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
diff --git a/cmd/podman/save.go b/cmd/podman/save.go
index 766561f1a..1ae1bc872 100644
--- a/cmd/podman/save.go
+++ b/cmd/podman/save.go
@@ -47,13 +47,12 @@ var (
func init() {
saveCommand.Command = _saveCommand
+ 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.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT")
flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output")
-
- rootCmd.AddCommand(saveCommand.Command)
}
// saveCmd saves the image to either docker-archive or oci
diff --git a/cmd/podman/search.go b/cmd/podman/search.go
index b15da1f6d..2febee689 100644
--- a/cmd/podman/search.go
+++ b/cmd/podman/search.go
@@ -43,6 +43,7 @@ var (
func init() {
searchCommand.Command = _searchCommand
+ searchCommand.SetUsageTemplate(UsageTemplate())
flags := searchCommand.Flags()
flags.StringVar(&searchCommand.Authfile, "authfile", "", "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringSliceVarP(&searchCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
@@ -50,8 +51,6 @@ func init() {
flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results")
flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output")
flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
-
- rootCmd.AddCommand(searchCommand.Command)
}
type searchParams struct {
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index c74d8fdce..81811e0f2 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -213,11 +213,16 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput
}
}
+ ports, err := ctr.PortMappings()
+ if err != nil {
+ logrus.Errorf("unable to lookup namespace container for %s", ctr.ID())
+ }
+
pso.ID = cid
pso.Image = imageName
pso.Command = command
pso.Created = created
- pso.Ports = portsToString(ctr.PortMappings())
+ pso.Ports = portsToString(ports)
pso.Names = ctr.Name()
pso.IsInfra = ctr.IsInfra()
pso.Status = status
diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go
index a87c12556..ac0d985f5 100644
--- a/cmd/podman/sign.go
+++ b/cmd/podman/sign.go
@@ -38,12 +38,11 @@ var (
func init() {
signCommand.Command = _signCommand
+ signCommand.SetUsageTemplate(UsageTemplate())
flags := signCommand.Flags()
flags.StringVarP(&signCommand.Directory, "directory", "d", "", "Define an alternate directory to store signatures")
flags.StringVar(&signCommand.SignBy, "sign-by", "", "Name of the signing key")
- rootCmd.AddCommand(signCommand.Command)
-
}
// SignatureStoreDir defines default directory to store signatures
diff --git a/cmd/podman/start.go b/cmd/podman/start.go
index 483ab4081..3a606d662 100644
--- a/cmd/podman/start.go
+++ b/cmd/podman/start.go
@@ -36,14 +36,13 @@ var (
func init() {
startCommand.Command = _startCommand
+ startCommand.SetUsageTemplate(UsageTemplate())
flags := startCommand.Flags()
flags.BoolVarP(&startCommand.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR")
flags.StringVar(&startCommand.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.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)")
-
- rootCmd.AddCommand(startCommand.Command)
}
func startCmd(c *cliconfig.StartValues) error {
@@ -109,6 +108,13 @@ func startCmd(c *cliconfig.StartValues) error {
// attach to the container and also start it not already running
err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, sigProxy, !ctrRunning)
+ if errors.Cause(err) == libpod.ErrDetach {
+ // User manually detached
+ // Exit cleanly immediately
+ exitCode = 0
+ return nil
+ }
+
if ctrRunning {
return err
}
@@ -138,7 +144,7 @@ func startCmd(c *cliconfig.StartValues) error {
logrus.Errorf("unable to detect if container %s should be deleted", ctr.ID())
}
if createArtifact.Rm {
- if rmErr := runtime.RemoveContainer(ctx, ctr, true); rmErr != nil {
+ if rmErr := runtime.RemoveContainer(ctx, ctr, true, false); rmErr != nil {
logrus.Errorf("unable to remove container %s after it failed to start", ctr.ID())
}
}
diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go
index 8c79ed290..af9bbad0e 100644
--- a/cmd/podman/stats.go
+++ b/cmd/podman/stats.go
@@ -47,14 +47,13 @@ var (
func init() {
statsCommand.Command = _statsCommand
+ statsCommand.SetUsageTemplate(UsageTemplate())
flags := statsCommand.Flags()
flags.BoolVarP(&statsCommand.All, "all", "a", false, "Show all containers. Only running containers are shown by default. The default is false")
flags.StringVar(&statsCommand.Format, "format", "", "Pretty-print container statistics to JSON or using a Go template")
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")
-
- rootCmd.AddCommand(statsCommand.Command)
}
func statsCmd(c *cliconfig.StatsValues) error {
diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go
index e2c16fe20..134d8a069 100644
--- a/cmd/podman/stop.go
+++ b/cmd/podman/stop.go
@@ -37,13 +37,12 @@ var (
func init() {
stopCommand.Command = _stopCommand
+ stopCommand.SetUsageTemplate(UsageTemplate())
flags := stopCommand.Flags()
flags.BoolVarP(&stopCommand.All, "all", "a", false, "Stop all running containers")
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")
-
- rootCmd.AddCommand(stopCommand.Command)
}
func stopCmd(c *cliconfig.StopValues) error {
diff --git a/cmd/podman/system.go b/cmd/podman/system.go
index f6b28fee1..741b79da5 100644
--- a/cmd/podman/system.go
+++ b/cmd/podman/system.go
@@ -17,7 +17,12 @@ var (
}
)
+var systemCommands = []*cobra.Command{
+ _infoCommand,
+}
+
func init() {
+ systemCommand.AddCommand(systemCommands...)
systemCommand.AddCommand(getSystemSubCommands()...)
- rootCmd.AddCommand(systemCommand.Command)
+ systemCommand.SetUsageTemplate(UsageTemplate())
}
diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go
index c918fbe3b..a91d7bf0a 100644
--- a/cmd/podman/system_prune.go
+++ b/cmd/podman/system_prune.go
@@ -34,8 +34,8 @@ var (
)
func init() {
-
pruneSystemCommand.Command = _pruneSystemCommand
+ pruneSystemCommand.SetUsageTemplate(UsageTemplate())
flags := pruneSystemCommand.Flags()
flags.BoolVarP(&pruneSystemCommand.All, "all", "a", false, "Remove all unused data")
flags.BoolVarP(&pruneSystemCommand.Force, "force", "f", false, "Do not prompt for confirmation")
@@ -76,7 +76,7 @@ Are you sure you want to continue? [y/N] `, volumeString)
ctx := getContext()
fmt.Println("Deleted Containers")
- lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false)
+ lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false, false)
if c.Bool("volumes") {
fmt.Println("Deleted Volumes")
err := volumePrune(runtime, getContext())
diff --git a/cmd/podman/tag.go b/cmd/podman/tag.go
index 2c7cf4abb..135c2f7a0 100644
--- a/cmd/podman/tag.go
+++ b/cmd/podman/tag.go
@@ -26,8 +26,7 @@ var (
func init() {
tagCommand.Command = _tagCommand
- rootCmd.AddCommand(tagCommand.Command)
-
+ tagCommand.SetUsageTemplate(UsageTemplate())
}
func tagCmd(c *cliconfig.TagValues) error {
diff --git a/cmd/podman/top.go b/cmd/podman/top.go
index a03830ee5..51b813e5a 100644
--- a/cmd/podman/top.go
+++ b/cmd/podman/top.go
@@ -48,12 +48,11 @@ the latest container.
func init() {
topCommand.Command = _topCommand
+ topCommand.SetUsageTemplate(UsageTemplate())
flags := topCommand.Flags()
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")
-
- rootCmd.AddCommand(topCommand.Command)
}
func topCmd(c *cliconfig.TopValues) error {
diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go
index 6304586c3..8b02dcdc6 100644
--- a/cmd/podman/trust.go
+++ b/cmd/podman/trust.go
@@ -16,6 +16,7 @@ var (
)
func init() {
+ trustCommand.SetUsageTemplate(UsageTemplate())
trustCommand.AddCommand(getTrustSubCommands()...)
imageCommand.AddCommand(trustCommand.Command)
}
diff --git a/cmd/podman/trust_set_show.go b/cmd/podman/trust_set_show.go
index cd9438220..f3d1cadce 100644
--- a/cmd/podman/trust_set_show.go
+++ b/cmd/podman/trust_set_show.go
@@ -49,9 +49,10 @@ var (
)
func init() {
-
setTrustCommand.Command = _setTrustCommand
+ setTrustCommand.SetUsageTemplate(UsageTemplate())
showTrustCommand.Command = _showTrustCommand
+ showTrustCommand.SetUsageTemplate(UsageTemplate())
setFlags := setTrustCommand.Flags()
setFlags.StringVar(&setTrustCommand.PolicyPath, "policypath", "", "")
setFlags.MarkHidden("policypath")
diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go
index 4622e8276..20ea410c2 100644
--- a/cmd/podman/umount.go
+++ b/cmd/podman/umount.go
@@ -37,12 +37,11 @@ An unmount can be forced with the --force flag.
func init() {
umountCommand.Command = _umountCommand
+ umountCommand.SetUsageTemplate(UsageTemplate())
flags := umountCommand.Flags()
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")
-
- rootCmd.AddCommand(umountCommand.Command)
}
func umountCmd(c *cliconfig.UmountValues) error {
diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go
index fb3a7b57a..c8f85cfd3 100644
--- a/cmd/podman/unpause.go
+++ b/cmd/podman/unpause.go
@@ -35,10 +35,9 @@ var (
func init() {
unpauseCommand.Command = _unpauseCommand
+ unpauseCommand.SetUsageTemplate(UsageTemplate())
flags := unpauseCommand.Flags()
flags.BoolVarP(&unpauseCommand.All, "all", "a", false, "Unpause all paused containers")
-
- rootCmd.AddCommand(unpauseCommand.Command)
}
func unpauseCmd(c *cliconfig.UnpauseValues) error {
diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go
index 2b76e034e..ce54cfa85 100644
--- a/cmd/podman/varlink.go
+++ b/cmd/podman/varlink.go
@@ -1,4 +1,4 @@
-// +build varlink
+// +build varlink,!remoteclient
package main
@@ -38,10 +38,9 @@ var (
func init() {
varlinkCommand.Command = _varlinkCommand
+ varlinkCommand.SetUsageTemplate(UsageTemplate())
flags := varlinkCommand.Flags()
flags.Int64VarP(&varlinkCommand.Timeout, "timeout", "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout")
-
- rootCmd.AddCommand(varlinkCommand.Command)
}
func varlinkCmd(c *cliconfig.VarlinkValues) error {
@@ -84,7 +83,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error {
logrus.Infof("varlink service expired (use --timeout to increase session time beyond %d ms, 0 means never timeout)", c.Int64("timeout"))
return nil
default:
- return errors.Errorf("unable to start varlink service")
+ return errors.Wrapf(err, "unable to start varlink service")
}
}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 101232b0c..697d9ed90 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -2,15 +2,13 @@
# in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in the upstream libpod repository.
interface io.podman
-
-# Version is the structure returned by GetVersion
-type Version (
- version: string,
- go_version: string,
- git_commit: string,
- built: int,
- os_arch: string,
- remote_api_version: int
+type Volume (
+ name: string,
+ labels: [string]string,
+ mountPoint: string,
+ driver: string,
+ options: [string]string,
+ scope: string
)
type NotImplemented (
@@ -20,6 +18,7 @@ type NotImplemented (
type StringResponse (
message: string
)
+
# ContainerChanges describes the return struct for ListContainerChanges
type ContainerChanges (
changed: []string,
@@ -27,14 +26,25 @@ type ContainerChanges (
deleted: []string
)
-# ImageInList describes the structure that is returned in
-# ListImages.
-type ImageInList (
+type VolumeCreateOpts (
+ volumeName: string,
+ driver: string,
+ labels: [string]string,
+ options: [string]string
+)
+
+type VolumeRemoveOpts (
+ volumes: []string,
+ all: bool,
+ force: bool
+)
+
+type Image (
id: string,
parentId: string,
repoTags: []string,
repoDigests: []string,
- created: string,
+ created: string, # as RFC3339
size: int,
virtualSize: int,
containers: int,
@@ -45,16 +55,15 @@ type ImageInList (
# ImageHistory describes the returned structure from ImageHistory.
type ImageHistory (
id: string,
- created: string,
+ created: string, # as RFC3339
createdBy: string,
tags: []string,
size: int,
comment: string
)
-# ImageSearch is the returned structure for SearchImage. It is returned
-# in array form.
-type ImageSearch (
+# Represents a single search result from SearchImages
+type ImageSearchResult (
description: string,
is_official: bool,
is_automated: bool,
@@ -62,13 +71,12 @@ type ImageSearch (
star_count: int
)
-# ListContainerData is the returned struct for an individual container
-type ListContainerData (
+type Container (
id: string,
image: string,
imageid: string,
command: []string,
- createdat: string,
+ createdat: string, # as RFC3339
runningfor: string,
status: string,
ports: []ContainerPortMappings,
@@ -303,37 +311,54 @@ type IDMap (
size: int
)
+# BuildOptions are are used to describe describe physical attributes of the build
+type BuildOptions (
+ addHosts: []string,
+ cgroupParent: string,
+ cpuPeriod: int,
+ cpuQuota: int,
+ cpuShares: int,
+ cpusetCpus: string,
+ cpusetMems: string,
+ memory: int,
+ memorySwap: int,
+ shmSize: string,
+ ulimit: []string,
+ volume: []string
+)
+
# BuildInfo is used to describe user input for building images
type BuildInfo (
- # paths to one or more dockerfiles
- dockerfile: []string,
- tags: []string,
- add_hosts: []string,
- cgroup_parent: string,
- cpu_period: int,
- cpu_quota: int,
- cpu_shares: int,
- cpuset_cpus: string,
- cpuset_mems: string,
- memory: string,
- memory_swap: string,
- security_opts: []string,
- shm_size: string,
- ulimit: []string,
- volume: []string,
- squash: bool,
- pull: bool,
- pull_always: bool,
- force_rm: bool,
- rm: bool,
- label: []string,
+ additionalTags: []string,
annotations: []string,
- build_args: [string]string,
- image_format: string
+ buildArgs: [string]string,
+ buildOptions: BuildOptions,
+ cniConfigDir: string,
+ cniPluginDir: string,
+ compression: string,
+ contextDir: string,
+ defaultsMountFilePath: string,
+ dockerfiles: []string,
+ err: string,
+ forceRmIntermediateCtrs: bool,
+ iidfile: string,
+ label: []string,
+ layers: bool,
+ nocache: bool,
+ out: string,
+ output: string,
+ outputFormat: string,
+ pullPolicy: string,
+ quiet: bool,
+ remoteIntermediateCtrs: bool,
+ reportWriter: string,
+ runtimeArgs: []string,
+ signaturePolicyPath: string,
+ squash: bool
)
-# BuildResponse is used to describe the responses for building images
-type BuildResponse (
+# MoreResponse is a struct for when responses from varlink requires longer output
+type MoreResponse (
logs: []string,
id: string
)
@@ -387,40 +412,35 @@ type Runlabel(
name: string,
pull: bool,
signaturePolicyPath: string,
- tlsVerify: bool,
+ tlsVerify: ?bool,
label: string,
extraArgs: []string,
opts: [string]string
)
-# Ping provides a response for developers to ensure their varlink setup is working.
-# #### Example
-# ~~~
-# $ varlink call -m unix:/run/podman/io.podman/io.podman.Ping
-# {
-# "ping": {
-# "message": "OK"
-# }
-# }
-# ~~~
-method Ping() -> (ping: StringResponse)
-
-# GetVersion returns a Version structure describing the libpod setup on their
-# system.
-method GetVersion() -> (version: Version)
+# GetVersion returns version and build information of the podman service
+method GetVersion() -> (
+ version: string,
+ go_version: string,
+ git_commit: string,
+ built: string, # as RFC3339
+ os_arch: string,
+ remote_api_version: int
+)
# GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats,
# build information of Podman, and system-wide registries.
method GetInfo() -> (info: PodmanInfo)
-# ListContainers returns a list of containers in no particular order. There are
-# returned as an array of ListContainerData structs. See also [GetContainer](#GetContainer).
-method ListContainers() -> (containers: []ListContainerData)
+# ListContainers returns information about all containers.
+# See also [GetContainer](#GetContainer).
+method ListContainers() -> (containers: []Container)
-# GetContainer takes a name or ID of a container and returns single ListContainerData
-# structure. A [ContainerNotFound](#ContainerNotFound) error will be returned if the container cannot be found.
-# See also [ListContainers](ListContainers) and [InspectContainer](#InspectContainer).
-method GetContainer(name: string) -> (container: ListContainerData)
+# GetContainer returns information about a single container. If a container
+# with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound)
+# error will be returned. See also [ListContainers](ListContainers) and
+# [InspectContainer](#InspectContainer).
+method GetContainer(id: string) -> (container: Container)
# CreateContainer creates a new container from an image. It uses a [Create](#Create) type for input. The minimum
# input required for CreateContainer is an image name. If the image name is not found, an [ImageNotFound](#ImageNotFound)
@@ -508,7 +528,7 @@ method ExportContainer(name: string, path: string) -> (tarfile: string)
method GetContainerStats(name: string) -> (container: ContainerStats)
# This method has not be implemented yet.
-method ResizeContainerTty() -> (notimplemented: NotImplemented)
+# method ResizeContainerTty() -> (notimplemented: NotImplemented)
# StartContainer starts a created or stopped container. It takes the name or ID of container. It returns
# the container ID once started. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound)
@@ -540,10 +560,10 @@ method RestartContainer(name: string, timeout: int) -> (container: string)
method KillContainer(name: string, signal: int) -> (container: string)
# This method has not be implemented yet.
-method UpdateContainer() -> (notimplemented: NotImplemented)
+# method UpdateContainer() -> (notimplemented: NotImplemented)
# This method has not be implemented yet.
-method RenameContainer() -> (notimplemented: NotImplemented)
+# method RenameContainer() -> (notimplemented: NotImplemented)
# PauseContainer takes the name or ID of container and pauses it. If the container cannot be found,
# a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise the ID of the container is returned.
@@ -556,7 +576,7 @@ method PauseContainer(name: string) -> (container: string)
method UnpauseContainer(name: string) -> (container: string)
# This method has not be implemented yet.
-method AttachToContainer() -> (notimplemented: NotImplemented)
+# method AttachToContainer() -> (notimplemented: NotImplemented)
# GetAttachSockets takes the name or ID of an existing container. It returns file paths for two sockets needed
# to properly communicate with a container. The first is the actual I/O socket that the container uses. The
@@ -580,7 +600,7 @@ method GetAttachSockets(name: string) -> (sockets: Sockets)
# a [ContainerNotFound](#ContainerNotFound) error is returned.
method WaitContainer(name: string) -> (exitcode: int)
-# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running
+# RemoveContainer takes requires the name or ID of container as well a boolean representing whether a running and a boolean indicating whether to remove builtin volumes
# container can be stopped and removed. Upon successful removal of the container, its ID is returned. If the
# container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
# #### Example
@@ -590,7 +610,7 @@ method WaitContainer(name: string) -> (exitcode: int)
# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
# }
# ~~~
-method RemoveContainer(name: string, force: bool) -> (container: string)
+method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string)
# DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted
# container IDs. See also [RemoveContainer](RemoveContainer).
@@ -608,21 +628,21 @@ method RemoveContainer(name: string, force: bool) -> (container: string)
# ~~~
method DeleteStoppedContainers() -> (containers: []string)
-# ListImages returns an array of ImageInList structures which provide basic information about
-# an image currently in storage. See also [InspectImage](InspectImage).
-method ListImages() -> (images: []ImageInList)
+# ListImages returns information about the images that are currently in storage.
+# See also [InspectImage](InspectImage).
+method ListImages() -> (images: []Image)
-# GetImage returns a single image in an [ImageInList](#ImageInList) struct. You must supply an image name as a string.
-# If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned.
-method GetImage(name: string) -> (image: ImageInList)
+# GetImage returns information about a single image in storage.
+# If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will be returned.
+method GetImage(id: string) -> (image: Image)
# BuildImage takes a [BuildInfo](#BuildInfo) structure and builds an image. At a minimum, you must provide the
-# 'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [BuildResponse](#BuildResponse) structure
+# 'dockerfile' and 'tags' options in the BuildInfo structure. It will return a [MoreResponse](#MoreResponse) structure
# that contains the build logs and resulting image ID.
-method BuildImage(build: BuildInfo) -> (image: BuildResponse)
+method BuildImage(build: BuildInfo) -> (image: MoreResponse)
# This function is not implemented yet.
-method CreateImage() -> (notimplemented: NotImplemented)
+# method CreateImage() -> (notimplemented: NotImplemented)
# InspectImage takes the name or ID of an image and returns a string respresentation of data associated with the
#image. You must serialize the string into JSON to use it further. An [ImageNotFound](#ImageNotFound) error will
@@ -637,8 +657,8 @@ method HistoryImage(name: string) -> (history: []ImageHistory)
# 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
-# the image cannot be found in local storage; otherwise the ID of the image will be returned on success.
-method PushImage(name: string, tag: string, tlsverify: bool, signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) -> (image: string)
+# the image cannot be found in local storage; otherwise it will return a [MoreResponse](#MoreResponse)
+method PushImage(name: string, tag: string, tlsverify: ?bool, signaturePolicy: string, creds: string, certDir: string, compress: bool, format: string, removeSignatures: bool, signBy: string) -> (reply: MoreResponse)
# TagImage takes the name or ID of an image in local storage as well as the desired tag name. If the image cannot
# be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success.
@@ -656,10 +676,10 @@ method TagImage(name: string, tagged: string) -> (image: string)
# ~~~
method RemoveImage(name: string, force: bool) -> (image: string)
-# SearchImage takes the string of an image name and a limit of searches from each registries to be returned. SearchImage
-# will then use a glob-like match to find the image you are searching for. The images are returned in an array of
-# ImageSearch structures which contain information about the image as well as its fully-qualified name.
-method SearchImage(name: string, limit: int) -> (images: []ImageSearch)
+# 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)
# DeleteUnusedImages deletes any images not associated with a container. The IDs of the deleted images are returned
# in a string array.
@@ -706,7 +726,7 @@ method ExportImage(name: string, destination: string, compress: bool, tags: []st
# "id": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e"
# }
# ~~~
-method PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: bool) -> (id: string)
+method PullImage(name: string, certDir: string, creds: string, signaturePolicy: string, tlsVerify: ?bool) -> (id: string)
# 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.
@@ -913,10 +933,10 @@ method UnpausePod(name: string) -> (pod: string)
method RemovePod(name: string, force: bool) -> (pod: string)
# This method has not be implemented yet.
-method WaitPod() -> (notimplemented: NotImplemented)
+# method WaitPod() -> (notimplemented: NotImplemented)
# This method has not been implemented yet.
-method TopPod() -> (notimplemented: NotImplemented)
+# method TopPod() -> (notimplemented: NotImplemented)
# GetPodStats takes the name or ID of a pod and returns a pod name and slice of ContainerStats structure which
# contains attributes like memory and cpu usage. If the pod cannot be found, a [PodNotFound](#PodNotFound)
@@ -1020,19 +1040,19 @@ method UnmountContainer(name: string, force: bool) -> ()
method ImagesPrune(all: bool) -> (pruned: []string)
# This function is not implemented yet.
-method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
+# method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
# GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
# and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
-method GenerateKube() -> (notimplemented: NotImplemented)
+# method GenerateKube() -> (notimplemented: NotImplemented)
# GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod
# and its containers. The description is in YAML. See also [GenerateKube](GenerateKube).
-method GenerateKubeService() -> (notimplemented: NotImplemented)
+# method GenerateKubeService() -> (notimplemented: NotImplemented)
# ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML)
# like that created by GenerateKube. See also [GenerateKube](GenerateKube).
-method ReplayKube() -> (notimplemented: NotImplemented)
+# method ReplayKube() -> (notimplemented: NotImplemented)
# ContainerConfig returns a container's config in string form. This call is for
# development of Podman only and generally should not be used.
@@ -1050,14 +1070,29 @@ method ContainerInspectData(name: string) -> (config: string)
# development of Podman only and generally should not be used.
method ContainerStateData(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)
+
+# ReceiveFile allows the host to send a remote client a file
method ReceiveFile(path: string, delete: bool) -> (len: int)
+# VolumeCreate creates a volume on a remote host
+method VolumeCreate(options: VolumeCreateOpts) -> (volumeName: string)
+
+# VolumeRemove removes a volume on a remote host
+method VolumeRemove(options: VolumeRemoveOpts) -> (volumeNames: []string)
+
+# GetVolumes gets slice of the volumes on a remote host
+method GetVolumes(args: []string, all: bool) -> (volumes: []Volume)
+
+# VolumesPrune removes unused volumes on the host
+method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
-error ImageNotFound (name: string)
+error ImageNotFound (id: string)
# ContainerNotFound means the container could not be found by the provided name or ID in local storage.
-error ContainerNotFound (name: string)
+error ContainerNotFound (id: string)
# NoContainerRunning means none of the containers requested are running in a command that requires a running container.
error NoContainerRunning ()
diff --git a/cmd/podman/varlink_dummy.go b/cmd/podman/varlink_dummy.go
index 8d7a7e8ca..430511d72 100644
--- a/cmd/podman/varlink_dummy.go
+++ b/cmd/podman/varlink_dummy.go
@@ -2,8 +2,10 @@
package main
-import (
- "github.com/containers/libpod/cmd/podman/cliconfig"
-)
+import "github.com/spf13/cobra"
-var varlinkCommand *cliconfig.PodmanCommand
+var (
+ _varlinkCommand = &cobra.Command{
+ Use: "",
+ }
+)
diff --git a/cmd/podman/version.go b/cmd/podman/version.go
index 0e7cd43d5..c65ba94f9 100644
--- a/cmd/podman/version.go
+++ b/cmd/podman/version.go
@@ -28,9 +28,9 @@ var (
func init() {
versionCommand.Command = _versionCommand
+ versionCommand.SetUsageTemplate(UsageTemplate())
flags := versionCommand.Flags()
flags.StringVarP(&versionCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template")
- rootCmd.AddCommand(versionCommand.Command)
}
// versionCmd gets and prints version info for version command
diff --git a/cmd/podman/volume.go b/cmd/podman/volume.go
index bae778e52..8a8664151 100644
--- a/cmd/podman/volume.go
+++ b/cmd/podman/volume.go
@@ -16,8 +16,16 @@ var volumeCommand = cliconfig.PodmanCommand{
Long: volumeDescription,
},
}
+var volumeSubcommands = []*cobra.Command{
+ _volumeCreateCommand,
+ _volumeLsCommand,
+ _volumeRmCommand,
+ _volumeInspectCommand,
+ _volumePruneCommand,
+}
func init() {
- volumeCommand.AddCommand(getVolumeSubCommands()...)
+ volumeCommand.SetUsageTemplate(UsageTemplate())
+ volumeCommand.AddCommand(volumeSubcommands...)
rootCmd.AddCommand(volumeCommand.Command)
}
diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go
index e0ff4c341..fe5d69e0b 100644
--- a/cmd/podman/volume_create.go
+++ b/cmd/podman/volume_create.go
@@ -4,8 +4,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/libpod/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -33,6 +32,7 @@ be created at.`
func init() {
volumeCreateCommand.Command = _volumeCreateCommand
+ volumeCreateCommand.SetUsageTemplate(UsageTemplate())
flags := volumeCreateCommand.Flags()
flags.StringVar(&volumeCreateCommand.Driver, "driver", "", "Specify volume driver name (default local)")
flags.StringSliceVarP(&volumeCreateCommand.Label, "label", "l", []string{}, "Set metadata for a volume (default [])")
@@ -41,13 +41,7 @@ func init() {
}
func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error {
- var (
- options []libpod.VolumeCreateOption
- err error
- volName string
- )
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@@ -57,36 +51,19 @@ func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error {
return errors.Errorf("too many arguments, create takes at most 1 argument")
}
- if len(c.InputArgs) > 0 {
- volName = c.InputArgs[0]
- options = append(options, libpod.WithVolumeName(volName))
- }
-
- if c.Flag("driver").Changed {
- options = append(options, libpod.WithVolumeDriver(c.String("driver")))
- }
-
labels, err := getAllLabels([]string{}, c.Label)
if err != nil {
return errors.Wrapf(err, "unable to process labels")
}
- if len(labels) != 0 {
- options = append(options, libpod.WithVolumeLabels(labels))
- }
opts, err := getAllLabels([]string{}, c.Opt)
if err != nil {
return errors.Wrapf(err, "unable to process options")
}
- if len(options) != 0 {
- options = append(options, libpod.WithVolumeOptions(opts))
- }
- vol, err := runtime.NewVolume(getContext(), options...)
- if err != nil {
- return err
+ volumeName, err := runtime.CreateVolume(getContext(), c, labels, opts)
+ if err == nil {
+ fmt.Println(volumeName)
}
- fmt.Printf("%s\n", vol.Name())
-
- return nil
+ return err
}
diff --git a/cmd/podman/volume_inspect.go b/cmd/podman/volume_inspect.go
index 7cb5703da..928ef37d0 100644
--- a/cmd/podman/volume_inspect.go
+++ b/cmd/podman/volume_inspect.go
@@ -2,9 +2,8 @@ package main
import (
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -31,6 +30,7 @@ from JSON to a Go template.
func init() {
volumeInspectCommand.Command = _volumeInspectCommand
+ volumeInspectCommand.SetUsageTemplate(UsageTemplate())
flags := volumeInspectCommand.Flags()
flags.BoolVarP(&volumeInspectCommand.All, "all", "a", false, "Inspect all volumes")
flags.StringVarP(&volumeInspectCommand.Format, "format", "f", "json", "Format volume output using Go template")
@@ -38,22 +38,19 @@ func init() {
}
func volumeInspectCmd(c *cliconfig.VolumeInspectValues) error {
- var err error
+ if (c.All && len(c.InputArgs) > 0) || (!c.All && len(c.InputArgs) < 1) {
+ return errors.New("provide one or more volume names or use --all")
+ }
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- opts := volumeLsOptions{
- Format: c.Format,
- }
-
- vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime)
- if lastError != nil {
- logrus.Errorf("%q", lastError)
+ vols, err := runtime.InspectVolumes(getContext(), c)
+ if err != nil {
+ return err
}
-
- return generateVolLsOutput(vols, opts, runtime)
+ return generateVolLsOutput(vols, volumeLsOptions{Format: c.Format})
}
diff --git a/cmd/podman/volume_ls.go b/cmd/podman/volume_ls.go
index 78fdfed64..0edadc5ac 100644
--- a/cmd/podman/volume_ls.go
+++ b/cmd/podman/volume_ls.go
@@ -6,8 +6,7 @@ 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/libpod"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -62,6 +61,7 @@ and the output format can be changed to JSON or a user specified Go template.
func init() {
volumeLsCommand.Command = _volumeLsCommand
+ volumeLsCommand.SetUsageTemplate(UsageTemplate())
flags := volumeLsCommand.Flags()
flags.StringVarP(&volumeLsCommand.Filter, "filter", "f", "", "Filter volume output")
@@ -70,7 +70,7 @@ func init() {
}
func volumeLsCmd(c *cliconfig.VolumeLsValues) error {
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@@ -86,7 +86,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error {
opts.Format = genVolLsFormat(c)
// Get the filter functions based on any filters set
- var filterFuncs []libpod.VolumeFilter
+ var filterFuncs []adapter.VolumeFilter
if c.Filter != "" {
filters := strings.Split(c.Filter, ",")
for _, f := range filters {
@@ -94,7 +94,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error {
if len(filterSplit) < 2 {
return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
- generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1], runtime)
+ generatedFunc, err := generateVolumeFilterFuncs(filterSplit[0], filterSplit[1])
if err != nil {
return errors.Wrapf(err, "invalid filter")
}
@@ -102,13 +102,12 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error {
}
}
- volumes, err := runtime.GetAllVolumes()
+ volumes, err := runtime.Volumes(getContext())
if err != nil {
return err
}
-
// Get the volumes that match the filter
- volsFiltered := make([]*libpod.Volume, 0, len(volumes))
+ volsFiltered := make([]*adapter.Volume, 0, len(volumes))
for _, vol := range volumes {
include := true
for _, filter := range filterFuncs {
@@ -119,7 +118,7 @@ func volumeLsCmd(c *cliconfig.VolumeLsValues) error {
volsFiltered = append(volsFiltered, vol)
}
}
- return generateVolLsOutput(volsFiltered, opts, runtime)
+ return generateVolLsOutput(volsFiltered, opts)
}
// generate the template based on conditions given
@@ -205,7 +204,7 @@ func getVolTemplateOutput(lsParams []volumeLsJSONParams, opts volumeLsOptions) (
}
// getVolJSONParams returns the volumes in JSON format
-func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) ([]volumeLsJSONParams, error) {
+func getVolJSONParams(volumes []*adapter.Volume) []volumeLsJSONParams {
var lsOutput []volumeLsJSONParams
for _, volume := range volumes {
@@ -220,25 +219,19 @@ func getVolJSONParams(volumes []*libpod.Volume, opts volumeLsOptions, runtime *l
lsOutput = append(lsOutput, params)
}
- return lsOutput, nil
+ return lsOutput
}
// generateVolLsOutput generates the output based on the format, JSON or Go Template, and prints it out
-func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime *libpod.Runtime) error {
+func generateVolLsOutput(volumes []*adapter.Volume, opts volumeLsOptions) error {
if len(volumes) == 0 && opts.Format != formats.JSONString {
return nil
}
- lsOutput, err := getVolJSONParams(volumes, opts, runtime)
- if err != nil {
- return err
- }
+ lsOutput := getVolJSONParams(volumes)
var out formats.Writer
switch opts.Format {
case formats.JSONString:
- if err != nil {
- return errors.Wrapf(err, "unable to create JSON for volume output")
- }
out = formats.JSONStructArray{Output: volLsToGeneric([]volumeLsTemplateParams{}, lsOutput)}
default:
lsOutput, err := getVolTemplateOutput(lsOutput, opts)
@@ -251,18 +244,18 @@ func generateVolLsOutput(volumes []*libpod.Volume, opts volumeLsOptions, runtime
}
// generateVolumeFilterFuncs returns the true if the volume matches the filter set, otherwise it returns false.
-func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(volume *libpod.Volume) bool, error) {
+func generateVolumeFilterFuncs(filter, filterValue string) (func(volume *adapter.Volume) bool, error) {
switch filter {
case "name":
- return func(v *libpod.Volume) bool {
+ return func(v *adapter.Volume) bool {
return strings.Contains(v.Name(), filterValue)
}, nil
case "driver":
- return func(v *libpod.Volume) bool {
+ return func(v *adapter.Volume) bool {
return v.Driver() == filterValue
}, nil
case "scope":
- return func(v *libpod.Volume) bool {
+ return func(v *adapter.Volume) bool {
return v.Scope() == filterValue
}, nil
case "label":
@@ -273,7 +266,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti
} else {
filterValue = ""
}
- return func(v *libpod.Volume) bool {
+ return func(v *adapter.Volume) bool {
for labelKey, labelValue := range v.Labels() {
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
return true
@@ -289,7 +282,7 @@ func generateVolumeFilterFuncs(filter, filterValue string, runtime *libpod.Runti
} else {
filterValue = ""
}
- return func(v *libpod.Volume) bool {
+ return func(v *adapter.Volume) bool {
for labelKey, labelValue := range v.Options() {
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
return true
diff --git a/cmd/podman/volume_prune.go b/cmd/podman/volume_prune.go
index 29dc6ead4..a2205140f 100644
--- a/cmd/podman/volume_prune.go
+++ b/cmd/podman/volume_prune.go
@@ -8,7 +8,6 @@ import (
"strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -37,29 +36,27 @@ using force.
func init() {
volumePruneCommand.Command = _volumePruneCommand
+ volumePruneCommand.SetUsageTemplate(UsageTemplate())
flags := volumePruneCommand.Flags()
flags.BoolVarP(&volumePruneCommand.Force, "force", "f", false, "Do not prompt for confirmation")
}
func volumePrune(runtime *adapter.LocalRuntime, ctx context.Context) error {
- var lastError error
-
- volumes, err := runtime.GetAllVolumes()
- if err != nil {
- return err
+ prunedNames, prunedErrors := runtime.PruneVolumes(ctx)
+ for _, name := range prunedNames {
+ fmt.Println(name)
+ }
+ if len(prunedErrors) == 0 {
+ return nil
}
+ // Grab the last error
+ lastError := prunedErrors[len(prunedErrors)-1]
+ // Remove the last error from the error slice
+ prunedErrors = prunedErrors[:len(prunedErrors)-1]
- for _, vol := range volumes {
- err = runtime.RemoveVolume(ctx, vol, false, true)
- if err == nil {
- fmt.Println(vol.Name())
- } else if err != libpod.ErrVolumeBeingUsed {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "failed to remove volume %q", vol.Name())
- }
+ for _, err := range prunedErrors {
+ logrus.Errorf("%q", err)
}
return lastError
}
@@ -84,6 +81,5 @@ func volumePruneCmd(c *cliconfig.VolumePruneValues) error {
return nil
}
}
-
return volumePrune(runtime, getContext())
}
diff --git a/cmd/podman/volume_rm.go b/cmd/podman/volume_rm.go
index b02a06ed9..f301749e9 100644
--- a/cmd/podman/volume_rm.go
+++ b/cmd/podman/volume_rm.go
@@ -4,9 +4,8 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -35,6 +34,7 @@ not being used by any containers. To remove the volumes anyways, use the
func init() {
volumeRmCommand.Command = _volumeRmCommand
+ volumeRmCommand.SetUsageTemplate(UsageTemplate())
flags := volumeRmCommand.Flags()
flags.BoolVarP(&volumeRmCommand.All, "all", "a", false, "Remove all volumes")
flags.BoolVarP(&volumeRmCommand.Force, "force", "f", false, "Remove a volume by force, even if it is being used by a container")
@@ -43,25 +43,28 @@ func init() {
func volumeRmCmd(c *cliconfig.VolumeRmValues) error {
var err error
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ if (len(c.InputArgs) > 0 && c.All) || (len(c.InputArgs) < 1 && !c.All) {
+ return errors.New("choose either one or more volumes or all")
+ }
+
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
-
- ctx := getContext()
-
- vols, lastError := getVolumesFromContext(&c.PodmanCommand, runtime)
- for _, vol := range vols {
- err = runtime.RemoveVolume(ctx, vol, c.Force, false)
- if err != nil {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "failed to remove volume %q", vol.Name())
- } else {
- fmt.Println(vol.Name())
+ deletedVolumeNames, err := runtime.RemoveVolumes(getContext(), c)
+ if err != nil {
+ if len(deletedVolumeNames) > 0 {
+ printDeleteVolumes(deletedVolumeNames)
+ return err
}
}
- return lastError
+ printDeleteVolumes(deletedVolumeNames)
+ return err
+}
+
+func printDeleteVolumes(volumes []string) {
+ for _, v := range volumes {
+ fmt.Println(v)
+ }
}
diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go
index fa195b7ce..616c8feb5 100644
--- a/cmd/podman/wait.go
+++ b/cmd/podman/wait.go
@@ -34,11 +34,10 @@ var (
func init() {
waitCommand.Command = _waitCommand
+ waitCommand.SetUsageTemplate(UsageTemplate())
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")
-
- rootCmd.AddCommand(waitCommand.Command)
}
func waitCmd(c *cliconfig.WaitValues) error {
diff --git a/commands.md b/commands.md
index c7d03d5ad..3fd27ad5d 100644
--- a/commands.md
+++ b/commands.md
@@ -16,7 +16,7 @@
| [podman-container-refresh(1)](/docs/podman-container-refresh.1.md) | Refresh all containers state in database ||
| [podman-container-restore(1)](/docs/podman-container-restore.1.md) | Restores one or more running containers ||
| [podman-container-runlabel(1)](/docs/podman-container-runlabel.1.md) | Execute Image Label Method ||
-| [podman-cp(1)](/docs/podman-cp.1.md) | Instead of providing a `podman cp` command, the man page `podman-cp` describes how to use the `podman mount` command to have even more flexibility and functionality||
+| [podman-cp(1)](/docs/podman-cp.1.md) | Copy files/folders between a container and the local filesystem ||
| [podman-create(1)](/docs/podman-create.1.md) | Create a new container ||
| [podman-diff(1)](/docs/podman-diff.1.md) | Inspect changes on a container or image's filesystem |[![...](/docs/play.png)](https://asciinema.org/a/FXfWB9CKYFwYM4EfqW3NSZy1G)|
| [podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container
@@ -73,6 +73,7 @@
| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [podman-varlink(1)](/docs/podman-varlink.1.md) | Run the varlink backend ||
| [podman-version(1)](/docs/podman-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)|
+| [podman-volume(1)](/docs/podman-volume.1.md) | Manage Volumes ||
| [podman-volume-create(1)](/docs/podman-volume-create.1.md) | Create a volume ||
| [podman-volume-inspect(1)](/docs/podman-volume-inspect.1.md) | Get detailed information on one or more volumes ||
| [podman-volume-ls(1)](/docs/podman-volume-ls.1.md) | List all the available volumes ||
diff --git a/completions/bash/podman b/completions/bash/podman
index 9df87aef4..36ac27d52 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1691,6 +1691,7 @@ _podman_container_run() {
--oom-score-adj
--pid
--pids-limit
+ --pod
--publish -p
--runtime
--rootfs
@@ -2319,6 +2320,7 @@ _podman_login() {
local boolean_options="
--help
-h
+ --password-stdin
"
_complete_ "$options_with_args" "$boolean_options"
}
diff --git a/contrib/perftest/main.go b/contrib/perftest/main.go
index 6a6725ab9..c0a91209f 100644
--- a/contrib/perftest/main.go
+++ b/contrib/perftest/main.go
@@ -218,7 +218,7 @@ func runSingleThreadedStressTest(ctx context.Context, client *libpod.Runtime, im
//Delete Container
deleteStartTime := time.Now()
- err = client.RemoveContainer(ctx, ctr, true)
+ err = client.RemoveContainer(ctx, ctr, true, false)
if err != nil {
return nil, err
}
diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md
index 98eb5bece..0c11e2013 100644
--- a/docs/libpod.conf.5.md
+++ b/docs/libpod.conf.5.md
@@ -12,8 +12,11 @@ libpod to manage containers.
**image_default_transport**=""
Default transport method for pulling and pushing images
-**runtime_path**=""
- Paths to search for a valid OCI runtime binary
+**runtime**=""
+ Default OCI runtime to use if nothing is specified
+
+**runtimes**
+ For each OCI runtime, specify a list of paths to look for. The first one found is used.
**conmon_path**=""
Paths to search for the Conmon container manager binary
diff --git a/docs/podman-commit.1.md b/docs/podman-commit.1.md
index 79e14aba6..acde51859 100644
--- a/docs/podman-commit.1.md
+++ b/docs/podman-commit.1.md
@@ -76,7 +76,7 @@ e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8
```
```
-$ podman commit -q --pause=false reverent_golick image-commited
+$ podman commit -q --pause=false containerID image-commited
e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8
```
diff --git a/docs/podman-cp.1.md b/docs/podman-cp.1.md
index 88e50e86b..37426b236 100644
--- a/docs/podman-cp.1.md
+++ b/docs/podman-cp.1.md
@@ -3,20 +3,70 @@
## NAME
podman\-cp - Copy files/folders between a container and the local filesystem
-## Description
-We chose not to implement the `cp` feature in `podman` even though the upstream Docker
-project has it. We have a much stronger capability. Using standard podman-mount
-and podman-umount, we can take advantage of the entire linux tool chain, rather
+## SYNOPSIS
+**podman cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH**
+
+## DESCRIPTION
+Copies the contents of **SRC_PATH** to the **DEST_PATH**. You can copy from the containers's filesystem to the local machine or the reverse, from the local filesystem to the container.
+
+The CONTAINER can be a running or stopped container. The **SRC_PATH** or **DEST_PATH** can be a file or directory.
+
+The **podman cp** command assumes container paths are relative to the container's / (root) directory.
+
+This means supplying the initial forward slash is optional;
+
+The command sees **compassionate_darwin:/tmp/foo/myfile.txt** and **compassionate_darwin:tmp/foo/myfile.txt** as identical.
+
+Local machine paths can be an absolute or relative value.
+The command interprets a local machine's relative paths as relative to the current working directory where **podman cp** is run.
+
+Assuming a path separator of /, a first argument of **SRC_PATH** and second argument of **DEST_PATH**, the behavior is as follows:
+
+**SRC_PATH** specifies a file
+ - **DEST_PATH** does not exist
+ - the file is saved to a file created at **DEST_PATH**
+ - **DEST_PATH** does not exist and ends with /
+ - **DEST_PATH** is created as a directory and the file is copied into this directory using the basename from **SRC_PATH**
+ - **DEST_PATH** exists and is a file
+ - the destination is overwritten with the source file's contents
+ - **DEST_PATH** exists and is a directory
+ - the file is copied into this directory using the basename from **SRC_PATH**
+
+**SRC_PATH** specifies a directory
+ - **DEST_PATH** does not exist
+ - **DEST_PATH** is created as a directory and the contents of the source directory are copied into this directory
+ - **DEST_PATH** exists and is a file
+ - Error condition: cannot copy a directory to a file
+ - **DEST_PATH** exists and is a directory
+ - **SRC_PATH** ends with /
+ - the source directory is copied into this directory
+ - **SRC_PATH** ends with /. (that is: slash followed by dot)
+ - the content of the source directory is copied into this directory
+
+The command requires **SRC_PATH** and **DEST_PATH** to exist according to the above rules.
+
+If **SRC_PATH** is local and is a symbolic link, the symbolic target, is copied by default.
+
+A colon (:) is used as a delimiter between CONTAINER and its path.
+
+You can also use : when specifying paths to a **SRC_PATH** or **DEST_PATH** on a local machine, for example, `file:name.txt`.
+
+If you use a : in a local machine path, you must be explicit with a relative or absolute path, for example:
+ `/path/to/file:name.txt` or `./file:name.txt`
+
+
+## ALTERNATIVES
+
+Podman has much stronger capabilities than just `podman cp` to achieve copy files between host and container.
+
+Using standard podman-mount and podman-umount takes advantage of the entire linux tool chain, rather
then just cp.
-If a user wants to copy contents out of a container or into a container, they
-can execute a few simple commands.
+If a user wants to copy contents out of a container or into a container, they can execute a few simple commands.
-You can copy from the container's file system to the local machine or the
-reverse, from the local filesystem to the container.
+You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container.
-If you want to copy the /etc/foobar directory out of a container and onto /tmp
-on the host, you could execute the following commands:
+If you want to copy the /etc/foobar directory out of a container and onto /tmp on the host, you could execute the following commands:
mnt=$(podman mount CONTAINERID)
cp -R ${mnt}/etc/foobar /tmp
@@ -40,5 +90,15 @@ This shows that using `podman mount` and `podman umount` you can use all of the
standard linux tools for moving files into and out of containers, not just
the cp command.
+## EXAMPLE
+
+podman cp /myapp/app.conf containerID:/myapp/app.conf
+
+podman cp /home/myuser/myfiles.tar containerID:/tmp
+
+podman cp containerID:/myapp/ /myapp/
+
+podman cp containerID:/home/myuser/. /home/myuser/
+
## SEE ALSO
podman(1), podman-mount(1), podman-umount(1)
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 98b1a2a17..342ef59c3 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -657,18 +657,21 @@ The followings examples are all valid:
Without this argument the command will be run as root in the container.
-**--userns**=""
+**--userns**=host
+**--userns**=ns:my_namespace
-Set the usernamespace mode for the container. The use of userns is disabled by default.
+Set the user namespace mode for the container. The use of userns is disabled by default.
- **host**: use the host usernamespace and enable all privileged options (e.g., `pid=host` or `--privileged`).
- **ns**: specify the usernamespace to use.
+- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
+- `ns`: run the container in the given existing user namespace.
+
+This option is incompatible with --gidmap, --uidmap, --subuid and --subgid
**--uts**=*host*
Set the UTS mode for the container
**host**: use the host's UTS namespace inside the container.
- **ns**: specify the usernamespace to use.
+ **ns**: specify the user namespace to use.
Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
**--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
@@ -780,10 +783,28 @@ can override the working directory by using the **-w** option.
## EXAMPLES
+### Create a container using a local image
+
+```
+$ podman create alpine ls
+```
+
+### Create a container using a local image and annotate it
+
+```
+$ podman create --annotation HELLO=WORLD alpine ls
+```
+
+### Create a container using a local image, allocating a pseudo-TTY, keeping stdin open and name it myctr
+
+```
+ podman create -t -i --name myctr alpine ls
+```
+
### Set UID/GID mapping in a new user namespace
-If you want to run the container in a new user namespace and define the mapping of
-the uid and gid from the host.
+Running a container in a new user namespace requires a mapping of
+the uids and gids from the host.
```
$ podman create --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
@@ -804,13 +825,27 @@ KillMode=process
WantedBy=multi-user.target
```
+### Rootless Containers
+
+Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils
+be installed. The shadow-utils package must include the newuidmap and newgidmap executables.
+
+Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released.
+
+In order for users to run rootless, there must be an entry for their username in /etc/subuid and /etc/subgid which lists the UIDs for their user namespace.
+
+Rootless podman works better if the fuse-overlayfs and slirp4netns packages are installed.
+The fuse-overlay package provides a userspace overlay storage driver, otherwise users need to use
+the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
+required for VPN, without it containers need to be run with the --net=host flag.
+
## FILES
**/etc/subuid**
**/etc/subgid**
## SEE ALSO
-subgid(5), subuid(5), libpod.conf(5), systemd.unit(5), setsebool(8)
+subgid(5), subuid(5), libpod.conf(5), systemd.unit(5), setsebool(8), slirp4netns(1), fuse-overlayfs(1)
## HISTORY
October 2017, converted from Docker documentation to podman by Dan Walsh for podman <dwalsh@redhat.com>
diff --git a/docs/podman-login.1.md b/docs/podman-login.1.md
index e72d1deca..3ac0e30ef 100644
--- a/docs/podman-login.1.md
+++ b/docs/podman-login.1.md
@@ -25,6 +25,10 @@ flag. The default path used is **${XDG\_RUNTIME_DIR}/containers/auth.json**.
Password for registry
+**--password-stdin**
+
+Take the password from stdin
+
**--username, -u**
Username for registry
@@ -86,6 +90,16 @@ $ podman login --cert-dir /etc/containers/certs.d/ -u foo -p bar localhost:5000
Login Succeeded!
```
+```
+$ podman login -u testuser --password-stdin < testpassword.txt docker.io
+Login Succeeded!
+```
+
+```
+$ echo $testpassword | podman login -u testuser --password-stdin docker.io
+Login Succeeded!
+```
+
## SEE ALSO
podman(1), podman-logout(1), crio(8)
diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md
index 4fcb0b6c5..f4513c2be 100644
--- a/docs/podman-rm.1.md
+++ b/docs/podman-rm.1.md
@@ -17,7 +17,9 @@ Remove all containers. Can be used in conjunction with -f as well.
**--force, f**
-Force the removal of a running and paused containers
+Force the removal of running and paused containers. Forcing a containers removal also
+removes containers from container storage even if the container is not known to podman.
+Containers could have been created by a different container engine.
**--latest, -l**
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 828ae96a8..cef9a6e8a 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -663,7 +663,7 @@ Without this argument the command will be run as root in the container.
**--userns**=host
**--userns**=ns:my_namespace
-Set the user namespace for the container.
+Set the user namespace mode for the container. The use of userns is disabled by default.
- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
- `ns`: run the container in the given existing user namespace.
@@ -675,7 +675,7 @@ This option is incompatible with --gidmap, --uidmap, --subuid and --subgid
Set the UTS mode for the container
`host`: use the host's UTS namespace inside the container.
-`ns`: specify the usernamespace to use.
+`ns`: specify the user namespace to use.
**NOTE**: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
@@ -709,6 +709,20 @@ Current supported mount TYPES are bind, and tmpfs.
· tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
+**--userns**=""
+
+Set the user namespace mode for the container. The use of userns is disabled by default.
+
+ **host**: use the host user namespace and enable all privileged options (e.g., `pid=host` or `--privileged`).
+ **ns**: specify the user namespace to use.
+
+**--uts**=*host*
+
+Set the UTS mode for the container
+ **host**: use the host's UTS namespace inside the container.
+ **ns**: specify the user namespace to use.
+ Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
+
**--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
@@ -1074,8 +1088,8 @@ supported sysctls.
### Set UID/GID mapping in a new user namespace
-If you want to run the container in a new user namespace and define the mapping of
-the uid and gid from the host.
+Running a container in a new user namespace requires a mapping of
+the uids and gids from the host.
```
$ podman run --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello
@@ -1096,13 +1110,27 @@ KillMode=process
WantedBy=multi-user.target
```
+### Rootless Containers
+
+Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils
+be installed. The shadow-utils package must include the newuidmap and newgidmap executables.
+
+Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released.
+
+In order for users to run rootless, there must be an entry for their username in /etc/subuid and /etc/subgid which lists the UIDs for their user namespace.
+
+Rootless podman works better if the fuse-overlayfs and slirp4netns packages are installed.
+The fuse-overlay package provides a userspace overlay storage driver, otherwise users need to use
+the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
+required for VPN, without it containers need to be run with the --net=host flag.
+
## FILES
**/etc/subuid**
**/etc/subgid**
## SEE ALSO
-subgid(5), subuid(5), libpod.conf(5), systemd.unit(5), setsebool(8)
+subgid(5), subuid(5), libpod.conf(5), systemd.unit(5), setsebool(8), slirp4netns(1), fuse-overlayfs(1)
## HISTORY
September 2018, updated by Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
diff --git a/docs/podman-stats.1.md b/docs/podman-stats.1.md
index 8fc765326..d0b56b2e6 100644
--- a/docs/podman-stats.1.md
+++ b/docs/podman-stats.1.md
@@ -36,16 +36,17 @@ Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
| --------------- | --------------- |
-| .ID | Container ID |
+| .Pod | Pod ID |
+| .CID | Container ID |
| .Name | Container Name |
-| .CPUPerc | CPU percentage |
+| .CPU | CPU percentage |
| .MemUsage | Memory usage |
-| .MemPerc | Memory percentage |
+| .Mem | Memory percentage |
| .NetIO | Network IO |
| .BlockIO | Block IO |
| .PIDS | Number of PIDs |
-
+When using a GO template, you may preceed the format with `table` to print headers.
## EXAMPLE
```
diff --git a/docs/podman.1.md b/docs/podman.1.md
index 51ef00383..760f27310 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -168,6 +168,7 @@ the exit codes follow the `chroot` standard, see below:
| [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. |
| [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. |
| [podman-version(1)](podman-version.1.md) | Display the Podman version information. |
+| [podman-volume(1)](podman-volume.1.md) | Manage Volumes. |
| [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
## FILES
diff --git a/hack/get_ci_vm.sh b/hack/get_ci_vm.sh
index b058b4273..3c2d193af 100755
--- a/hack/get_ci_vm.sh
+++ b/hack/get_ci_vm.sh
@@ -17,8 +17,14 @@ MEMORY="4Gb"
DISK="200"
PROJECT="libpod-218412"
GOSRC="/var/tmp/go/src/github.com/containers/libpod"
+GCLOUD_IMAGE=${GCLOUD_IMAGE:-quay.io/cevich/gcloud_centos:latest}
+GCLOUD_SUDO=${GCLOUD_SUDO-sudo}
+
+# Shared tmp directory between container and us
+TMPDIR=$(mktemp -d --tmpdir $(basename $0)_tmpdir_XXXXXX)
+
# Command shortcuts save some typing
-PGCLOUD="sudo podman run -it --rm -e AS_ID=$UID -e AS_USER=$USER --security-opt label=disable -v /home/$USER:$HOME -v /tmp:/tmp:ro quay.io/cevich/gcloud_centos:latest --configuration=libpod --project=$PROJECT"
+PGCLOUD="$GCLOUD_SUDO podman run -it --rm -e AS_ID=$UID -e AS_USER=$USER --security-opt label=disable -v /home/$USER:$HOME -v $TMPDIR:/tmp $GCLOUD_IMAGE --configuration=libpod --project=$PROJECT"
SCP_CMD="$PGCLOUD compute scp"
LIBPODROOT=$(realpath "$(dirname $0)/../")
@@ -39,19 +45,20 @@ showrun() {
fi
}
-TEMPFILE=$(mktemp -p '' $(basename $0)_XXXXX.tar.bz2)
cleanup() {
set +e
wait
- rm -f "$TEMPFILE"
+
+ # set GCLOUD_DEBUG to leave tmpdir behind for postmortem
+ test -z "$GCLOUD_DEBUG" && rm -rf $TMPDIR
}
trap cleanup EXIT
delvm() {
- cleanup
echo -e "\n"
echo -e "\n${YEL}Offering to Delete $VMNAME ${RED}(Might take a minute or two)${NOR}"
showrun $CLEANUP_CMD # prompts for Yes/No
+ cleanup
}
image_hints() {
@@ -128,16 +135,16 @@ parse_args $@
cd $LIBPODROOT
# Attempt to determine if named 'libpod' gcloud configuration exists
-showrun $PGCLOUD info > $TEMPFILE
-if egrep -q "Account:.*None" "$TEMPFILE"
+showrun $PGCLOUD info > $TMPDIR/gcloud-info
+if egrep -q "Account:.*None" $TMPDIR/gcloud-info
then
echo -e "\n${YEL}WARNING: Can't find gcloud configuration for libpod, running init.${NOR}"
echo -e " ${RED}Please choose "#1: Re-initialize" and "login" if asked.${NOR}"
showrun $PGCLOUD init --project=$PROJECT --console-only --skip-diagnostics
# Verify it worked (account name == someone@example.com)
- $PGCLOUD info > $TEMPFILE
- if egrep -q "Account:.*None" "$TEMPFILE"
+ $PGCLOUD info > $TMPDIR/gcloud-info-after-init
+ if egrep -q "Account:.*None" $TMPDIR/gcloud-info-after-init
then
echo -e "${RED}ERROR: Could not initialize libpod configuration in gcloud.${NOR}"
exit 5
@@ -150,8 +157,10 @@ then
fi
# Couldn't make rsync work with gcloud's ssh wrapper :(
+TARBALL_BASENAME=$VMNAME.tar.bz2
+TARBALL=/tmp/$TARBALL_BASENAME
echo -e "\n${YEL}Packing up repository into a tarball $VMNAME.${NOR}"
-showrun --background tar cjf $TEMPFILE --warning=no-file-changed -C $LIBPODROOT .
+showrun --background tar cjf $TMPDIR/$TARBALL_BASENAME --warning=no-file-changed -C $LIBPODROOT .
trap delvm INT # Allow deleting VM if CTRL-C during create
# This fails if VM already exists: permit this usage to re-init
@@ -186,13 +195,13 @@ showrun $SSH_CMD --command "mkdir -p $GOSRC"
echo -e "\n${YEL}Transfering tarball to $VMNAME.${NOR}"
wait
-showrun $SCP_CMD $TEMPFILE root@$VMNAME:$TEMPFILE
+showrun $SCP_CMD $TARBALL root@$VMNAME:$TARBALL
echo -e "\n${YEL}Unpacking tarball into $GOSRC on $VMNAME.${NOR}"
-showrun $SSH_CMD --command "tar xjf $TEMPFILE -C $GOSRC"
+showrun $SSH_CMD --command "tar xjf $TARBALL -C $GOSRC"
echo -e "\n${YEL}Removing tarball on $VMNAME.${NOR}"
-showrun $SSH_CMD --command "rm -f $TEMPFILE"
+showrun $SSH_CMD --command "rm -f $TARBALL"
echo -e "\n${YEL}Executing environment setup${NOR}"
[[ "$1" == "-p" ]] && echo -e "${RED}Using package-based dependencies.${NOR}"
diff --git a/libpod.conf b/libpod.conf
index acd6c8982..c4e7dc628 100644
--- a/libpod.conf
+++ b/libpod.conf
@@ -88,6 +88,8 @@ pause_command = "/pause"
# Default libpod support for container labeling
# label=true
+runtime = "runc"
+
# Paths to look for a valid OCI runtime (runc, runv, etc)
[runtimes]
runc = [
diff --git a/libpod/adapter/runtime.go b/libpod/adapter/runtime.go
index 46771b5b6..02ef9af07 100644
--- a/libpod/adapter/runtime.go
+++ b/libpod/adapter/runtime.go
@@ -4,18 +4,22 @@ package adapter
import (
"context"
- "github.com/pkg/errors"
"io"
"io/ioutil"
"os"
"strconv"
+ "github.com/containers/buildah"
+ "github.com/containers/buildah/imagebuildah"
+ "github.com/containers/buildah/pkg/parse"
+ "github.com/containers/image/docker/reference"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
)
// LocalRuntime describes a typical libpod runtime
@@ -34,6 +38,14 @@ type Container struct {
*libpod.Container
}
+// Volume ...
+type Volume struct {
+ *libpod.Volume
+}
+
+// VolumeFilter is for filtering volumes on the client
+type VolumeFilter func(*Volume) bool
+
// GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it
func GetRuntime(c *cliconfig.PodmanCommand) (*LocalRuntime, error) {
runtime, err := libpodruntime.GetRuntime(c)
@@ -155,3 +167,146 @@ func (r *LocalRuntime) Export(name string, path string) error {
func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) {
return r.Runtime.Import(ctx, source, reference, changes, history, quiet)
}
+
+// CreateVolume is a wrapper to create volumes
+func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCreateValues, labels, opts map[string]string) (string, error) {
+ var (
+ options []libpod.VolumeCreateOption
+ volName string
+ )
+
+ if len(c.InputArgs) > 0 {
+ volName = c.InputArgs[0]
+ options = append(options, libpod.WithVolumeName(volName))
+ }
+
+ if c.Flag("driver").Changed {
+ options = append(options, libpod.WithVolumeDriver(c.Driver))
+ }
+
+ if len(labels) != 0 {
+ options = append(options, libpod.WithVolumeLabels(labels))
+ }
+
+ if len(options) != 0 {
+ options = append(options, libpod.WithVolumeOptions(opts))
+ }
+ newVolume, err := r.NewVolume(ctx, options...)
+ if err != nil {
+ return "", err
+ }
+ return newVolume.Name(), nil
+}
+
+// RemoveVolumes is a wrapper to remove volumes
+func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmValues) ([]string, error) {
+ return r.Runtime.RemoveVolumes(ctx, c.InputArgs, c.All, c.Force)
+}
+
+// Push is a wrapper to push an image to a registry
+func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
+ newImage, err := r.ImageRuntime().NewFromLocal(srcName)
+ if err != nil {
+ return err
+ }
+ return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil)
+}
+
+// InspectVolumes returns a slice of volumes based on an arg list or --all
+func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) {
+ var (
+ volumes []*libpod.Volume
+ err error
+ )
+
+ if c.All {
+ volumes, err = r.GetAllVolumes()
+ } else {
+ for _, v := range c.InputArgs {
+ vol, err := r.GetVolume(v)
+ if err != nil {
+ return nil, err
+ }
+ volumes = append(volumes, vol)
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return libpodVolumeToVolume(volumes), nil
+}
+
+// Volumes returns a slice of localruntime volumes
+func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) {
+ vols, err := r.GetAllVolumes()
+ if err != nil {
+ return nil, err
+ }
+ return libpodVolumeToVolume(vols), nil
+}
+
+// libpodVolumeToVolume converts a slice of libpod volumes to a slice
+// of localruntime volumes (same as libpod)
+func libpodVolumeToVolume(volumes []*libpod.Volume) []*Volume {
+ var vols []*Volume
+ for _, v := range volumes {
+ newVol := Volume{
+ v,
+ }
+ vols = append(vols, &newVol)
+ }
+ return vols
+}
+
+// Build is the wrapper to build images
+func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, options imagebuildah.BuildOptions, dockerfiles []string) error {
+ namespaceOptions, networkPolicy, err := parse.NamespaceOptions(c.PodmanCommand.Command)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing namespace-related options")
+ }
+ usernsOption, idmappingOptions, err := parse.IDMappingOptions(c.PodmanCommand.Command)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing ID mapping options")
+ }
+ namespaceOptions.AddOrReplace(usernsOption...)
+
+ systemContext, err := parse.SystemContextFromOptions(c.PodmanCommand.Command)
+ if err != nil {
+ return errors.Wrapf(err, "error building system context")
+ }
+
+ authfile := c.Authfile
+ if len(c.Authfile) == 0 {
+ authfile = os.Getenv("REGISTRY_AUTH_FILE")
+ }
+
+ systemContext.AuthFilePath = authfile
+ commonOpts, err := parse.CommonBuildOptions(c.PodmanCommand.Command)
+ if err != nil {
+ return err
+ }
+
+ options.NamespaceOptions = namespaceOptions
+ options.ConfigureNetwork = networkPolicy
+ options.IDMappingOptions = idmappingOptions
+ options.CommonBuildOpts = commonOpts
+ options.SystemContext = systemContext
+
+ if c.Flag("runtime").Changed {
+ options.Runtime = r.GetOCIRuntimePath()
+ }
+ if c.Quiet {
+ options.ReportWriter = ioutil.Discard
+ }
+
+ if rootless.IsRootless() {
+ options.Isolation = buildah.IsolationOCIRootless
+ }
+
+ return r.Runtime.Build(ctx, options, dockerfiles...)
+}
+
+// PruneVolumes is a wrapper function for libpod PruneVolumes
+func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
+ return r.Runtime.PruneVolumes(ctx)
+}
diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go
index f754aaee6..f63b5875d 100644
--- a/libpod/adapter/runtime_remote.go
+++ b/libpod/adapter/runtime_remote.go
@@ -7,18 +7,22 @@ import (
"context"
"encoding/json"
"fmt"
- "github.com/pkg/errors"
"io"
+ "io/ioutil"
"os"
"strings"
"time"
+ "github.com/containers/buildah/imagebuildah"
+ "github.com/containers/image/docker/reference"
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/storage/pkg/archive"
"github.com/opencontainers/go-digest"
+ "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/varlink/go/varlink"
)
@@ -91,6 +95,18 @@ type remoteContainer struct {
state *libpod.ContainerState
}
+type VolumeFilter func(*Volume) bool
+
+// Volume is embed for libpod volumes
+type Volume struct {
+ remoteVolume
+}
+
+type remoteVolume struct {
+ Runtime *LocalRuntime
+ config *libpod.VolumeConfig
+}
+
// GetImages returns a slice of containerimages over a varlink connection
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
var newImages []*ContainerImage
@@ -112,8 +128,8 @@ func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
return newImages, nil
}
-func imageInListToContainerImage(i iopodman.ImageInList, name string, runtime *LocalRuntime) (*ContainerImage, error) {
- created, err := splitStringDate(i.Created)
+func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRuntime) (*ContainerImage, error) {
+ created, err := time.ParseInLocation(time.RFC3339, i.Created, time.UTC)
if err != nil {
return nil, err
}
@@ -147,7 +163,8 @@ func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) {
// 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.
- imageID, err := iopodman.PullImage().Call(r.Conn, srcRef.DockerReference().String(), "", "", signaturePolicyPath, true)
+ tlsverify := true
+ imageID, err := iopodman.PullImage().Call(r.Conn, srcRef.DockerReference().String(), "", "", signaturePolicyPath, &tlsverify)
if err != nil {
return nil, err
}
@@ -163,15 +180,21 @@ func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authf
if label != nil {
return nil, errors.New("the remote client function does not support checking a remote image for a label")
}
- // TODO Creds needs to be figured out here too, like above
- tlsBool := dockeroptions.DockerInsecureSkipTLSVerify
- // Remember SkipTlsVerify is the opposite of tlsverify
- // If tlsBook is true or undefined, we do not skip
- SkipTlsVerify := false
- if tlsBool == types.OptionalBoolFalse {
- SkipTlsVerify = true
+ var (
+ tlsVerify bool
+ tlsVerifyPtr *bool
+ )
+ if dockeroptions.DockerInsecureSkipTLSVerify == types.OptionalBoolFalse {
+ tlsVerify = true
+ tlsVerifyPtr = &tlsVerify
+
+ }
+ if dockeroptions.DockerInsecureSkipTLSVerify == types.OptionalBoolTrue {
+ tlsVerify = false
+ tlsVerifyPtr = &tlsVerify
}
- imageID, err := iopodman.PullImage().Call(r.Conn, name, dockeroptions.DockerCertPath, "", signaturePolicyPath, SkipTlsVerify)
+
+ imageID, err := iopodman.PullImage().Call(r.Conn, name, dockeroptions.DockerCertPath, "", signaturePolicyPath, tlsVerifyPtr)
if err != nil {
return nil, err
}
@@ -182,12 +205,6 @@ func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authf
return newImage, nil
}
-func splitStringDate(d string) (time.Time, error) {
- fields := strings.Fields(d)
- t := fmt.Sprintf("%sT%sZ", fields[0], fields[1])
- return time.ParseInLocation(time.RFC3339Nano, t, time.UTC)
-}
-
// IsParent goes through the layers in the store and checks if i.TopLayer is
// the parent of any other layer in store. Double check that image with that
// layer exists as well.
@@ -251,7 +268,7 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error)
return nil, err
}
for _, h := range reply {
- created, err := splitStringDate(h.Created)
+ created, err := time.ParseInLocation(time.RFC3339, h.Created, time.UTC)
if err != nil {
return nil, err
}
@@ -369,6 +386,108 @@ func (r *LocalRuntime) Export(name string, path string) error {
// Import implements the remote calls required to import a container image to the store
func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) {
// First we send the file to the host
+ tempFile, err := r.SendFileOverVarlink(source)
+ if err != nil {
+ return "", err
+ }
+ return iopodman.ImportImage().Call(r.Conn, strings.TrimRight(tempFile, ":"), reference, history, changes, true)
+}
+
+func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, options imagebuildah.BuildOptions, dockerfiles []string) error {
+ buildOptions := iopodman.BuildOptions{
+ AddHosts: options.CommonBuildOpts.AddHost,
+ CgroupParent: options.CommonBuildOpts.CgroupParent,
+ CpuPeriod: int64(options.CommonBuildOpts.CPUPeriod),
+ CpuQuota: options.CommonBuildOpts.CPUQuota,
+ CpuShares: int64(options.CommonBuildOpts.CPUShares),
+ CpusetCpus: options.CommonBuildOpts.CPUSetMems,
+ CpusetMems: options.CommonBuildOpts.CPUSetMems,
+ Memory: options.CommonBuildOpts.Memory,
+ MemorySwap: options.CommonBuildOpts.MemorySwap,
+ ShmSize: options.CommonBuildOpts.ShmSize,
+ Ulimit: options.CommonBuildOpts.Ulimit,
+ Volume: options.CommonBuildOpts.Volumes,
+ }
+
+ buildinfo := iopodman.BuildInfo{
+ AdditionalTags: options.AdditionalTags,
+ Annotations: options.Annotations,
+ BuildArgs: options.Args,
+ BuildOptions: buildOptions,
+ CniConfigDir: options.CNIConfigDir,
+ CniPluginDir: options.CNIPluginPath,
+ Compression: string(options.Compression),
+ DefaultsMountFilePath: options.DefaultMountsFilePath,
+ Dockerfiles: dockerfiles,
+ //Err: string(options.Err),
+ ForceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
+ Iidfile: options.IIDFile,
+ Label: options.Labels,
+ Layers: options.Layers,
+ Nocache: options.NoCache,
+ //Out:
+ Output: options.Output,
+ OutputFormat: options.OutputFormat,
+ PullPolicy: options.PullPolicy.String(),
+ Quiet: options.Quiet,
+ RemoteIntermediateCtrs: options.RemoveIntermediateCtrs,
+ //ReportWriter:
+ RuntimeArgs: options.RuntimeArgs,
+ SignaturePolicyPath: options.SignaturePolicyPath,
+ 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())
+
+ _, err = io.Copy(outputFile, input)
+ if 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)
+
+ reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo)
+ if err != nil {
+ return err
+ }
+
+ for {
+ responses, flags, err := reply()
+ if err != nil {
+ return err
+ }
+ for _, line := range responses.Logs {
+ fmt.Print(line)
+ }
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+ return err
+}
+
+// SendFileOverVarlink sends a file over varlink in an upgraded connection
+func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) {
fs, err := os.Open(source)
if err != nil {
return "", err
@@ -378,6 +497,7 @@ func (r *LocalRuntime) Import(ctx context.Context, source, reference string, cha
if err != nil {
return "", err
}
+ logrus.Debugf("sending %s over varlink connection", source)
reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", int64(fileInfo.Size()))
if err != nil {
return "", err
@@ -392,6 +512,7 @@ func (r *LocalRuntime) Import(ctx context.Context, source, reference string, cha
if err != nil {
return "", err
}
+ logrus.Debugf("file transfer complete for %s", source)
r.Conn.Writer.Flush()
// All was sent, wait for the ACK from the server
@@ -405,7 +526,8 @@ func (r *LocalRuntime) Import(ctx context.Context, source, reference string, cha
return "", err
}
- return iopodman.ImportImage().Call(r.Conn, strings.TrimRight(tempFile, ":"), reference, history, changes, true)
+
+ return tempFile, nil
}
// GetAllVolumes retrieves all the volumes
@@ -429,6 +551,124 @@ func (r *LocalRuntime) GetContainers(filters ...libpod.ContainerFilter) ([]*libp
// RemoveContainer removes the given container
// If force is specified, the container will be stopped first
// Otherwise, RemoveContainer will return an error if the container is running
-func (r *LocalRuntime) RemoveContainer(ctx context.Context, c *libpod.Container, force bool) error {
+func (r *LocalRuntime) RemoveContainer(ctx context.Context, c *libpod.Container, force, volumes bool) error {
return libpod.ErrNotImplemented
}
+
+// CreateVolume creates a volume over a varlink connection for the remote client
+func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCreateValues, labels, opts map[string]string) (string, error) {
+ cvOpts := iopodman.VolumeCreateOpts{
+ Options: opts,
+ Labels: labels,
+ }
+ if len(c.InputArgs) > 0 {
+ cvOpts.VolumeName = c.InputArgs[0]
+ }
+
+ if c.Flag("driver").Changed {
+ cvOpts.Driver = c.Driver
+ }
+
+ return iopodman.VolumeCreate().Call(r.Conn, cvOpts)
+}
+
+// RemoveVolumes removes volumes over a varlink connection for the remote client
+func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmValues) ([]string, error) {
+ rmOpts := iopodman.VolumeRemoveOpts{
+ All: c.All,
+ Force: c.Force,
+ Volumes: c.InputArgs,
+ }
+ return iopodman.VolumeRemove().Call(r.Conn, rmOpts)
+}
+
+func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
+
+ var (
+ tls *bool
+ tlsVerify bool
+ )
+ if dockerRegistryOptions.DockerInsecureSkipTLSVerify == types.OptionalBoolTrue {
+ tlsVerify = false
+ tls = &tlsVerify
+ }
+ if dockerRegistryOptions.DockerInsecureSkipTLSVerify == types.OptionalBoolFalse {
+ tlsVerify = true
+ tls = &tlsVerify
+ }
+
+ reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, tls, signaturePolicyPath, "", dockerRegistryOptions.DockerCertPath, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy)
+ if err != nil {
+ return err
+ }
+ for {
+ responses, flags, err := reply()
+ if err != nil {
+ return err
+ }
+ for _, line := range responses.Logs {
+ fmt.Print(line)
+ }
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+
+ return err
+}
+
+// InspectVolumes returns a slice of volumes based on an arg list or --all
+func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) {
+ reply, err := iopodman.GetVolumes().Call(r.Conn, c.InputArgs, c.All)
+ if err != nil {
+ return nil, err
+ }
+ return varlinkVolumeToVolume(r, reply), nil
+}
+
+//Volumes returns a slice of adapter.volumes based on information about libpod
+// volumes over a varlink connection
+func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) {
+ reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true)
+ if err != nil {
+ return nil, err
+ }
+ return varlinkVolumeToVolume(r, reply), nil
+}
+
+func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume {
+ var vols []*Volume
+ for _, v := range volumes {
+ volumeConfig := libpod.VolumeConfig{
+ Name: v.Name,
+ Labels: v.Labels,
+ MountPoint: v.MountPoint,
+ Driver: v.Driver,
+ Options: v.Options,
+ Scope: v.Scope,
+ }
+ n := remoteVolume{
+ Runtime: r,
+ config: &volumeConfig,
+ }
+ newVol := Volume{
+ n,
+ }
+ vols = append(vols, &newVol)
+ }
+ return vols
+}
+
+// PruneVolumes removes all unused volumes from the remote system
+func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
+ var errs []error
+ prunedNames, prunedErrors, err := iopodman.VolumesPrune().Call(r.Conn)
+ if err != nil {
+ return []string{}, []error{err}
+ }
+ // We need to transform the string results of the error into actual error types
+ for _, e := range prunedErrors {
+ errs = append(errs, errors.New(e))
+ }
+ return prunedNames, errs
+}
diff --git a/libpod/adapter/volumes_remote.go b/libpod/adapter/volumes_remote.go
new file mode 100644
index 000000000..beacd943a
--- /dev/null
+++ b/libpod/adapter/volumes_remote.go
@@ -0,0 +1,33 @@
+// +build remoteclient
+
+package adapter
+
+// Name returns the name of the volume
+func (v *Volume) Name() string {
+ return v.config.Name
+}
+
+//Labels returns the labels for a volume
+func (v *Volume) Labels() map[string]string {
+ return v.config.Labels
+}
+
+// Driver returns the driver for the volume
+func (v *Volume) Driver() string {
+ return v.config.Driver
+}
+
+// Options returns the options a volume was created with
+func (v *Volume) Options() map[string]string {
+ return v.config.Options
+}
+
+// MountPath returns the path the volume is mounted to
+func (v *Volume) MountPoint() string {
+ return v.config.MountPoint
+}
+
+// Scope returns the scope for an adapter.volume
+func (v *Volume) Scope() string {
+ return v.config.Scope
+}
diff --git a/libpod/container.go b/libpod/container.go
index b0589be3b..75f4a4a4f 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -358,8 +358,7 @@ type ContainerConfig struct {
ExitCommand []string `json:"exitCommand,omitempty"`
// LocalVolumes are the built-in volumes we get from the --volumes-from flag
// It picks up the built-in volumes of the container used by --volumes-from
- LocalVolumes []string
-
+ LocalVolumes []spec.Mount
// IsInfra is a bool indicating whether this container is an infra container used for
// sharing kernel namespaces in a pod
IsInfra bool `json:"pause"`
@@ -557,8 +556,16 @@ func (c *Container) NewNetNS() bool {
// PortMappings returns the ports that will be mapped into a container if
// a new network namespace is created
// If NewNetNS() is false, this value is unused
-func (c *Container) PortMappings() []ocicni.PortMapping {
- return c.config.PortMappings
+func (c *Container) PortMappings() ([]ocicni.PortMapping, error) {
+ // First check if the container belongs to a network namespace (like a pod)
+ if len(c.config.NetNsCtr) > 0 {
+ netNsCtr, err := c.runtime.LookupContainer(c.config.NetNsCtr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to lookup network namespace for container %s", c.ID())
+ }
+ return netNsCtr.PortMappings()
+ }
+ return c.config.PortMappings, nil
}
// DNSServers returns DNS servers that will be used in the container's
diff --git a/libpod/container_attach_linux.go b/libpod/container_attach_linux.go
index 1d6f0bd96..3ff6ddc76 100644
--- a/libpod/container_attach_linux.go
+++ b/libpod/container_attach_linux.go
@@ -109,8 +109,8 @@ func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSi
case err := <-receiveStdoutError:
return err
case err := <-stdinDone:
- if _, ok := err.(utils.DetachError); ok {
- return nil
+ if err == ErrDetach {
+ return err
}
if streams.AttachOutput || streams.AttachError {
return <-receiveStdoutError
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index b0dcc853e..b2ebad777 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -10,21 +10,16 @@ import (
"path/filepath"
"strconv"
"strings"
- "syscall"
"time"
- "github.com/containers/buildah/imagebuildah"
"github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
- "github.com/containers/storage/pkg/chrootarchive"
"github.com/containers/storage/pkg/mount"
- "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -489,9 +484,20 @@ func (c *Container) removeConmonFiles() error {
return errors.Wrapf(err, "error removing container %s OOM file", c.ID())
}
+ // Instead of outright deleting the exit file, rename it (if it exists).
+ // We want to retain it so we can get the exit code of containers which
+ // are removed (at least until we have a workable events system)
exitFile := filepath.Join(c.runtime.ociRuntime.exitsDir, c.ID())
- if err := os.Remove(exitFile); err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "error removing container %s exit file", c.ID())
+ oldExitFile := filepath.Join(c.runtime.ociRuntime.exitsDir, fmt.Sprintf("%s-old", c.ID()))
+ if _, err := os.Stat(exitFile); err != nil {
+ if !os.IsNotExist(err) {
+ return errors.Wrapf(err, "error running stat on container %s exit file", c.ID())
+ }
+ } else if err == nil {
+ // Rename should replace the old exit file (if it exists)
+ if err := os.Rename(exitFile, oldExitFile); err != nil {
+ return errors.Wrapf(err, "error renaming container %s exit file", c.ID())
+ }
}
return nil
@@ -1042,113 +1048,6 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error)
return filepath.Join(c.state.DestinationRunDir, destFile), nil
}
-func (c *Container) addLocalVolumes(ctx context.Context, g *generate.Generator, execUser *user.ExecUser) error {
- var uid, gid int
- mountPoint := c.state.Mountpoint
- if !c.state.Mounted {
- return errors.Wrapf(ErrInternal, "container is not mounted")
- }
- newImage, err := c.runtime.imageRuntime.NewFromLocal(c.config.RootfsImageID)
- if err != nil {
- return err
- }
- imageData, err := newImage.Inspect(ctx)
- if err != nil {
- return err
- }
- // Add the built-in volumes of the container passed in to --volumes-from
- for _, vol := range c.config.LocalVolumes {
- if imageData.Config.Volumes == nil {
- imageData.Config.Volumes = map[string]struct{}{
- vol: {},
- }
- } else {
- imageData.Config.Volumes[vol] = struct{}{}
- }
- }
-
- if c.config.User != "" {
- if execUser == nil {
- return errors.Wrapf(ErrInternal, "nil pointer passed to addLocalVolumes for execUser")
- }
- uid = execUser.Uid
- gid = execUser.Gid
- }
-
- for k := range imageData.Config.Volumes {
- mount := spec.Mount{
- Destination: k,
- Type: "bind",
- Options: []string{"private", "bind", "rw"},
- }
- if MountExists(g.Mounts(), k) {
- continue
- }
- volumePath := filepath.Join(c.config.StaticDir, "volumes", k)
-
- // Ensure the symlinks are resolved
- resolvedSymlink, err := imagebuildah.ResolveSymLink(mountPoint, k)
- if err != nil {
- return errors.Wrapf(ErrCtrStateInvalid, "cannot resolve %s in %s for container %s", k, mountPoint, c.ID())
- }
- var srcPath string
- if resolvedSymlink != "" {
- srcPath = filepath.Join(mountPoint, resolvedSymlink)
- } else {
- srcPath = filepath.Join(mountPoint, k)
- }
-
- if _, err := os.Stat(srcPath); os.IsNotExist(err) {
- logrus.Infof("Volume image mount point %s does not exist in root FS, need to create it", k)
- if err = os.MkdirAll(srcPath, 0755); err != nil {
- return errors.Wrapf(err, "error creating directory %q for volume %q in container %q", volumePath, k, c.ID())
- }
-
- if err = os.Chown(srcPath, uid, gid); err != nil {
- return errors.Wrapf(err, "error chowning directory %q for volume %q in container %q", srcPath, k, c.ID())
- }
- }
-
- if _, err := os.Stat(volumePath); os.IsNotExist(err) {
- if err = os.MkdirAll(volumePath, 0755); err != nil {
- return errors.Wrapf(err, "error creating directory %q for volume %q in container %q", volumePath, k, c.ID())
- }
-
- if err = os.Chown(volumePath, uid, gid); err != nil {
- return errors.Wrapf(err, "error chowning directory %q for volume %q in container %q", volumePath, k, c.ID())
- }
-
- if err = label.Relabel(volumePath, c.config.MountLabel, false); err != nil {
- return errors.Wrapf(err, "error relabeling directory %q for volume %q in container %q", volumePath, k, c.ID())
- }
- if err = chrootarchive.NewArchiver(nil).CopyWithTar(srcPath, volumePath); err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "error populating directory %q for volume %q in container %q using contents of %q", volumePath, k, c.ID(), srcPath)
- }
-
- // Set the volume path with the same owner and permission of source path
- sstat, _ := os.Stat(srcPath)
- st, ok := sstat.Sys().(*syscall.Stat_t)
- if !ok {
- return fmt.Errorf("could not convert to syscall.Stat_t")
- }
- uid := int(st.Uid)
- gid := int(st.Gid)
-
- if err := os.Lchown(volumePath, uid, gid); err != nil {
- return err
- }
- if os.Chmod(volumePath, sstat.Mode()); err != nil {
- return err
- }
-
- }
-
- mount.Source = volumePath
- g.AddMount(mount)
- }
- return nil
-}
-
// Save OCI spec to disk, replacing any existing specs for the container
func (c *Container) saveSpec(spec *spec.Spec) error {
// If the OCI spec already exists, we need to replace it
@@ -1292,3 +1191,30 @@ func getExcludedCGroups() (excludes []string) {
excludes = []string{"rdma"}
return
}
+
+// namedVolumes returns named volumes for the container
+func (c *Container) namedVolumes() ([]string, error) {
+ var volumes []string
+ for _, vol := range c.config.Spec.Mounts {
+ if strings.HasPrefix(vol.Source, c.runtime.config.VolumePath) {
+ volume := strings.TrimPrefix(vol.Source, c.runtime.config.VolumePath+"/")
+ split := strings.Split(volume, "/")
+ volume = split[0]
+ if _, err := c.runtime.state.Volume(volume); err == nil {
+ volumes = append(volumes, volume)
+ }
+ }
+ }
+ return volumes, nil
+}
+
+// this should be from chrootarchive.
+func (c *Container) copyWithTarFromImage(src, dest string) error {
+ mountpoint, err := c.mount()
+ if err != nil {
+ return err
+ }
+ a := archive.NewDefaultArchiver()
+ source := filepath.Join(mountpoint, src)
+ return a.CopyWithTar(source, dest)
+}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index bcdfdaee3..65cb47c8c 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -235,13 +235,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
- // Bind builtin image volumes
- if c.config.Rootfs == "" && c.config.ImageVolumes {
- if err := c.addLocalVolumes(ctx, &g, execUser); err != nil {
- return nil, errors.Wrapf(err, "error mounting image volumes")
- }
- }
-
if c.config.User != "" {
// User and Group must go together
g.SetProcessUID(uint32(execUser.Uid))
diff --git a/libpod/errors.go b/libpod/errors.go
index d6614141c..dd82d0796 100644
--- a/libpod/errors.go
+++ b/libpod/errors.go
@@ -2,15 +2,21 @@ package libpod
import (
"errors"
+
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/utils"
)
var (
// ErrNoSuchCtr indicates the requested container does not exist
- ErrNoSuchCtr = errors.New("no such container")
+ ErrNoSuchCtr = image.ErrNoSuchCtr
+
// ErrNoSuchPod indicates the requested pod does not exist
- ErrNoSuchPod = errors.New("no such pod")
+ ErrNoSuchPod = image.ErrNoSuchPod
+
// ErrNoSuchImage indicates the requested image does not exist
- ErrNoSuchImage = errors.New("no such image")
+ ErrNoSuchImage = image.ErrNoSuchImage
+
// ErrNoSuchVolume indicates the requested volume does not exist
ErrNoSuchVolume = errors.New("no such volume")
@@ -51,6 +57,10 @@ var (
// ErrInternal indicates an internal library error
ErrInternal = errors.New("internal libpod error")
+ // ErrDetach indicates that an attach session was manually detached by
+ // the user.
+ ErrDetach = utils.ErrDetach
+
// ErrRuntimeStopped indicates that the runtime has already been shut
// down and no further operations can be performed on it
ErrRuntimeStopped = errors.New("runtime has already been stopped")
diff --git a/libpod/image/utils.go b/libpod/image/utils.go
index ad027f32a..3585428ad 100644
--- a/libpod/image/utils.go
+++ b/libpod/image/utils.go
@@ -87,22 +87,29 @@ func hasTransport(image string) bool {
// ReposToMap parses the specified repotags and returns a map with repositories
// as keys and the corresponding arrays of tags as values.
-func ReposToMap(repotags []string) map[string][]string {
+func ReposToMap(repotags []string) (map[string][]string, error) {
// map format is repo -> tag
repos := make(map[string][]string)
for _, repo := range repotags {
var repository, tag string
if len(repo) > 0 {
- li := strings.LastIndex(repo, ":")
- repository = repo[0:li]
- tag = repo[li+1:]
+ named, err := reference.ParseNormalizedNamed(repo)
+ repository = named.Name()
+ if err != nil {
+ return nil, err
+ }
+ if ref, ok := named.(reference.NamedTagged); ok {
+ tag = ref.Tag()
+ } else if ref, ok := named.(reference.Canonical); ok {
+ tag = ref.Digest().String()
+ }
}
repos[repository] = append(repos[repository], tag)
}
if len(repos) == 0 {
repos["<none>"] = []string{"<none>"}
}
- return repos
+ return repos, nil
}
// GetAdditionalTags returns a list of reference.NamedTagged for the
diff --git a/libpod/kube.go b/libpod/kube.go
index f34805e39..484127870 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -228,7 +228,11 @@ func containerToV1Container(c *Container) (v1.Container, error) {
return kubeContainer, nil
}
- ports, err := ocicniPortMappingToContainerPort(c.PortMappings())
+ portmappings, err := c.PortMappings()
+ if err != nil {
+ return kubeContainer, err
+ }
+ ports, err := ocicniPortMappingToContainerPort(portmappings)
if err != nil {
return kubeContainer, nil
}
@@ -401,7 +405,7 @@ func capAddDrop(caps *specs.LinuxCapabilities) (*v1.Capabilities, error) {
func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
priv := c.Privileged()
ro := c.IsReadOnly()
- allowPrivEscalation := !c.Spec().Process.NoNewPrivileges
+ allowPrivEscalation := !c.config.Spec.Process.NoNewPrivileges
newCaps, err := capAddDrop(c.config.Spec.Process.Capabilities)
if err != nil {
@@ -421,7 +425,13 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
}
if c.User() != "" {
- // It is *possible* that
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ }
+ if err := c.syncContainer(); err != nil {
+ return nil, errors.Wrapf(err, "unable to sync container during YAML generation")
+ }
logrus.Debugf("Looking in container for user: %s", c.User())
u, err := lookup.GetUser(c.state.Mountpoint, c.User())
if err != nil {
diff --git a/libpod/oci.go b/libpod/oci.go
index e55bd57dc..26d2c6ef1 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -321,7 +321,6 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBPOD_USERNS_CONFIGURED=%s", os.Getenv("_LIBPOD_USERNS_CONFIGURED")))
cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBPOD_ROOTLESS_UID=%s", os.Getenv("_LIBPOD_ROOTLESS_UID")))
cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", os.Getenv("HOME")))
- cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
if r.reservePorts && !ctr.config.NetMode.IsSlirp4netns() {
ports, err := bindPorts(ctr.config.PortMappings)
diff --git a/libpod/options.go b/libpod/options.go
index d965c058e..06737776b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/cri-o/ocicni/pkg/ocicni"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
@@ -1058,7 +1059,7 @@ func WithUserVolumes(volumes []string) CtrCreateOption {
// from a container passed in to the --volumes-from flag.
// This stores the built-in volume information in the Config so we can
// add them when creating the container.
-func WithLocalVolumes(volumes []string) CtrCreateOption {
+func WithLocalVolumes(volumes []spec.Mount) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
diff --git a/libpod/runtime.go b/libpod/runtime.go
index c975f628b..4f5d1e292 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -130,6 +130,12 @@ type RuntimeConfig struct {
OCIRuntime string `toml:"runtime"`
// OCIRuntimes are the set of configured OCI runtimes (default is runc)
OCIRuntimes map[string][]string `toml:"runtimes"`
+ // RuntimePath is the path to OCI runtime binary for launching
+ // containers.
+ // The first path pointing to a valid file will be used
+ // This is used only when there are no OCIRuntime/OCIRuntimes defined. It
+ // is used only to be backward compatible with older versions of Podman.
+ RuntimePath []string `toml:"runtime_path"`
// ConmonPath is the path to the Conmon binary used for managing
// containers
// The first path pointing to a valid file will be used
@@ -389,7 +395,7 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
// If the configuration file was not found but we are running in rootless, a subset of the
// global config file is used.
for _, path := range []string{OverrideConfigPath, ConfigPath} {
- contents, err := ioutil.ReadFile(OverrideConfigPath)
+ contents, err := ioutil.ReadFile(path)
if err != nil {
// Ignore any error, the file might not be readable by us.
continue
@@ -403,6 +409,7 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
runtime.config.ConmonPath = tmpConfig.ConmonPath
runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars
runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes
+ runtime.config.RuntimePath = tmpConfig.RuntimePath
runtime.config.CNIPluginDir = tmpConfig.CNIPluginDir
runtime.config.NoPivotRoot = tmpConfig.NoPivotRoot
break
@@ -485,10 +492,25 @@ func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime
// Make a new runtime based on the given configuration
// Sets up containers/storage, state store, OCI runtime
func makeRuntime(runtime *Runtime) (err error) {
+
+ // Backward compatibility for `runtime_path`
+ if runtime.config.RuntimePath != nil {
+ // Don't print twice in rootless mode.
+ if os.Geteuid() == 0 {
+ logrus.Warningf("The configuration is using `runtime_path`, which is deprecated and will be removed in future. Please use `runtimes` and `runtime`")
+ logrus.Warningf("If you are using both `runtime_path` and `runtime`, the configuration from `runtime_path` is used")
+ }
+
+ // Transform `runtime_path` into `runtimes` and `runtime`.
+ name := filepath.Base(runtime.config.RuntimePath[0])
+ runtime.config.OCIRuntime = name
+ runtime.config.OCIRuntimes = map[string][]string{name: runtime.config.RuntimePath}
+ }
+
// Find a working OCI runtime binary
foundRuntime := false
// If runtime is an absolute path, then use it as it is.
- if runtime.config.OCIRuntime[0] == '/' {
+ if runtime.config.OCIRuntime != "" && runtime.config.OCIRuntime[0] == '/' {
foundRuntime = true
runtime.ociRuntimePath = OCIRuntimePath{Name: filepath.Base(runtime.config.OCIRuntime), Paths: []string{runtime.config.OCIRuntime}}
} else {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 9afdef7b6..185090cf7 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -10,7 +10,9 @@ import (
"strings"
"time"
+ "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/storage"
"github.com/containers/storage/pkg/stringid"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -175,9 +177,12 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
if err != nil {
newVol, err := r.newVolume(ctx, WithVolumeName(vol.Source))
if err != nil {
- logrus.Errorf("error creating named volume %q: %v", vol.Source, err)
+ return nil, errors.Wrapf(err, "error creating named volume %q", vol.Source)
}
ctr.config.Spec.Mounts[i].Source = newVol.MountPoint()
+ if err := ctr.copyWithTarFromImage(ctr.config.Spec.Mounts[i].Destination, ctr.config.Spec.Mounts[i].Source); err != nil && !os.IsNotExist(err) {
+ return nil, errors.Wrapf(err, "Failed to copy content into new volume mount %q", vol.Source)
+ }
continue
}
ctr.config.Spec.Mounts[i].Source = volInfo.MountPoint()
@@ -223,17 +228,19 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
// RemoveContainer removes the given container
// If force is specified, the container will be stopped first
+// If removeVolume is specified, named volumes used by the container will
+// be removed also if and only if the container is the sole user
// Otherwise, RemoveContainer will return an error if the container is running
-func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool) error {
+func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool) error {
r.lock.Lock()
defer r.lock.Unlock()
- return r.removeContainer(ctx, c, force)
+ return r.removeContainer(ctx, c, force, removeVolume)
}
// Internal function to remove a container
// Locks the container, but does not lock the runtime
-func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool) error {
+func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, removeVolume bool) error {
if !c.valid {
if ok, _ := r.state.HasContainer(c.ID()); !ok {
// Container probably already removed
@@ -246,6 +253,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool)
// To avoid races around removing a container and the pod it is in
var pod *Pod
var err error
+ runtime := c.runtime
if c.config.Pod != "" {
pod, err = r.state.Pod(c.config.Pod)
if err != nil {
@@ -331,6 +339,13 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool)
return errors.Wrapf(ErrCtrExists, "container %s has dependent containers which must be removed before it: %s", c.ID(), depsStr)
}
+ var volumes []string
+ if removeVolume {
+ volumes, err = c.namedVolumes()
+ if err != nil {
+ logrus.Errorf("unable to retrieve builtin volumes for container %v: %v", c.ID(), err)
+ }
+ }
var cleanupErr error
// Remove the container from the state
if c.config.Pod != "" {
@@ -395,6 +410,14 @@ 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 {
+ logrus.Errorf("cleanup volume (%s): %v", v, err)
+ }
+ }
+ }
+
return cleanupErr
}
@@ -564,3 +587,16 @@ func (r *Runtime) Export(name string, path string) error {
return ctr.Export(path)
}
+
+// RemoveContainersFromStorage attempt to remove containers from storage that do not exist in libpod database
+func (r *Runtime) RemoveContainersFromStorage(ctrs []string) {
+ for _, i := range ctrs {
+ // if the container does not exist in database, attempt to remove it from storage
+ if _, err := r.LookupContainer(i); err != nil && errors.Cause(err) == image.ErrNoSuchCtr {
+ r.storageService.UnmountContainerImage(i, true)
+ if err := r.storageService.DeleteContainer(i); err != nil && errors.Cause(err) != storage.ErrContainerUnknown {
+ logrus.Errorf("Failed to remove container %q from storage: %s", i, err)
+ }
+ }
+ }
+}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index c20aa77a3..1e9689362 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -43,7 +43,7 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
if len(imageCtrs) > 0 && len(img.Names()) <= 1 {
if force {
for _, ctr := range imageCtrs {
- if err := r.removeContainer(ctx, ctr, true); err != nil {
+ if err := r.removeContainer(ctx, ctr, true, false); err != nil {
return "", errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", img.ID(), ctr.ID())
}
}
diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go
index 3921758ee..beae50ac9 100644
--- a/libpod/runtime_volume.go
+++ b/libpod/runtime_volume.go
@@ -2,6 +2,9 @@ package libpod
import (
"context"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "strings"
)
// Contains the public Runtime API for volumes
@@ -38,6 +41,38 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force, prune bool
return r.removeVolume(ctx, v, force, prune)
}
+// RemoveVolumes removes a slice of volumes or all with a force bool
+func (r *Runtime) RemoveVolumes(ctx context.Context, volumes []string, all, force bool) ([]string, error) {
+ var (
+ vols []*Volume
+ err error
+ deletedVols []string
+ )
+ if all {
+ vols, err = r.Volumes()
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to get all volumes")
+ }
+ } else {
+ for _, i := range volumes {
+ vol, err := r.GetVolume(i)
+ if err != nil {
+ return nil, err
+ }
+ vols = append(vols, vol)
+ }
+ }
+
+ for _, vol := range vols {
+ if err := r.RemoveVolume(ctx, vol, force, false); err != nil {
+ return deletedVols, err
+ }
+ logrus.Debugf("removed volume %s", vol.Name())
+ deletedVols = append(deletedVols, vol.Name())
+ }
+ return deletedVols, nil
+}
+
// GetVolume retrieves a volume by its name
func (r *Runtime) GetVolume(name string) (*Volume, error) {
r.lock.RLock()
@@ -47,7 +82,21 @@ func (r *Runtime) GetVolume(name string) (*Volume, error) {
return nil, ErrRuntimeStopped
}
- return r.state.Volume(name)
+ vol, err := r.state.Volume(name)
+ if err == nil {
+ return vol, err
+ }
+
+ vols, err := r.GetAllVolumes()
+ if err != nil {
+ return nil, err
+ }
+ for _, v := range vols {
+ if strings.HasPrefix(v.Name(), name) {
+ return v, nil
+ }
+ }
+ return nil, errors.Errorf("unable to find volume %s", name)
}
// HasVolume checks to see if a volume with the given name exists
@@ -105,3 +154,27 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) {
return r.state.AllVolumes()
}
+
+// PruneVolumes removes unused volumes from the system
+func (r *Runtime) PruneVolumes(ctx context.Context) ([]string, []error) {
+ var (
+ prunedIDs []string
+ pruneErrors []error
+ )
+ vols, err := r.GetAllVolumes()
+ if err != nil {
+ pruneErrors = append(pruneErrors, err)
+ return nil, pruneErrors
+ }
+
+ for _, vol := range vols {
+ if err := r.RemoveVolume(ctx, vol, false, true); err != nil {
+ if err != ErrVolumeBeingUsed {
+ pruneErrors = append(pruneErrors, err)
+ }
+ continue
+ }
+ prunedIDs = append(prunedIDs, vol.Name())
+ }
+ return prunedIDs, pruneErrors
+}
diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go
index 800e6d106..0de8a2350 100644
--- a/libpod/volume_internal.go
+++ b/libpod/volume_internal.go
@@ -5,10 +5,6 @@ import (
"path/filepath"
)
-// VolumePath is the path under which all volumes that are created using the
-// local driver will be created
-// const VolumePath = "/var/lib/containers/storage/volumes"
-
// Creates a new volume
func newVolume(runtime *Runtime) (*Volume, error) {
volume := new(Volume)
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index f6c77d61c..8da44a2f0 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/pkg/namespaces"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
+ "github.com/containers/storage/pkg/stringid"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -134,8 +135,8 @@ type CreateConfig struct {
SeccompProfilePath string //SecurityOpts
SecurityOpts []string
Rootfs string
- LocalVolumes []string //Keeps track of the built-in volumes of container used in the --volumes-from flag
- Syslog bool // Whether to enable syslog on exit commands
+ LocalVolumes []spec.Mount //Keeps track of the built-in volumes of container used in the --volumes-from flag
+ Syslog bool // Whether to enable syslog on exit commands
}
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
@@ -216,7 +217,7 @@ func (c *CreateConfig) initFSMounts() []spec.Mount {
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
- var m []spec.Mount
+ m := c.LocalVolumes
for _, i := range c.Volumes {
var options []string
spliti := strings.Split(i, ":")
@@ -234,22 +235,31 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e
logrus.Debugf("User mount %s:%s options %v", spliti[0], spliti[1], options)
}
- // volumes from image config
- if c.ImageVolumeType != "tmpfs" {
+ if c.ImageVolumeType == "ignore" {
return m, nil
}
+
for vol := range c.BuiltinImgVolumes {
if libpod.MountExists(specMounts, vol) {
continue
}
+
mount := spec.Mount{
Destination: vol,
- Type: string(TypeTmpfs),
- Source: string(TypeTmpfs),
- Options: []string{"rprivate", "rw", "noexec", "nosuid", "nodev", "tmpcopyup"},
+ Type: c.ImageVolumeType,
+ Options: []string{"rprivate", "rw", "nodev"},
+ }
+ if c.ImageVolumeType == "tmpfs" {
+ mount.Source = "tmpfs"
+ mount.Options = append(mount.Options, "tmpcopyup")
+ } else {
+ // This will cause a new local Volume to be created on your system
+ mount.Source = stringid.GenerateNonCryptoID()
+ mount.Options = append(mount.Options, "bind")
}
m = append(m, mount)
}
+
return m, nil
}
@@ -257,6 +267,11 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e
// and adds it to c.Volumes of the current container.
func (c *CreateConfig) GetVolumesFrom() error {
var options string
+
+ if rootless.SkipStorageSetup() {
+ return nil
+ }
+
for _, vol := range c.VolumesFrom {
splitVol := strings.SplitN(vol, ":", 2)
if len(splitVol) == 2 {
@@ -266,6 +281,10 @@ func (c *CreateConfig) GetVolumesFrom() error {
if err != nil {
return errors.Wrapf(err, "error looking up container %q", splitVol[0])
}
+ inspect, err := ctr.Inspect(false)
+ if err != nil {
+ return errors.Wrapf(err, "error inspecting %q", splitVol[0])
+ }
var createArtifact CreateConfig
artifact, err := ctr.GetArtifact("create-config")
if err != nil {
@@ -274,9 +293,13 @@ func (c *CreateConfig) GetVolumesFrom() error {
if err := json.Unmarshal(artifact, &createArtifact); err != nil {
return err
}
-
for key := range createArtifact.BuiltinImgVolumes {
- c.LocalVolumes = append(c.LocalVolumes, key)
+ for _, m := range inspect.Mounts {
+ if m.Destination == key {
+ c.LocalVolumes = append(c.LocalVolumes, m)
+ break
+ }
+ }
}
for _, i := range createArtifact.Volumes {
@@ -340,7 +363,13 @@ func (c *CreateConfig) createExitCommand() []string {
if c.Syslog {
command = append(command, "--syslog")
}
- return append(command, []string{"container", "cleanup"}...)
+ command = append(command, []string{"container", "cleanup"}...)
+
+ if c.Rm {
+ command = append(command, "--rm")
+ }
+
+ return command
}
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
@@ -518,11 +547,9 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
if c.CgroupParent != "" {
options = append(options, libpod.WithCgroupParent(c.CgroupParent))
}
- // For a rootless container always cleanup the storage/network as they
- // run in a different namespace thus not reusable when we restart.
- if c.Detach || rootless.IsRootless() {
- options = append(options, libpod.WithExitCommand(c.createExitCommand()))
- }
+
+ // Always use a cleanup process to clean up Podman after termination
+ options = append(options, libpod.WithExitCommand(c.createExitCommand()))
return options, nil
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 52f431881..db8a3d5bb 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -259,8 +259,8 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) {
return opts, nil
}
-// GetRootlessVolumeInfo returns where all the name volumes will be created in rootless mode
-func GetRootlessVolumeInfo() (string, error) {
+// GetRootlessVolumePath returns where all the name volumes will be created in rootless mode
+func GetRootlessVolumePath() (string, error) {
dataDir, _, err := GetRootlessDirInfo()
if err != nil {
return "", err
@@ -307,15 +307,13 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) {
err error
)
storageOpts := storage.DefaultStoreOptions
- volumePath := "/var/lib/containers/storage"
-
+ volumePath := filepath.Join(storageOpts.GraphRoot, "volumes")
if rootless.IsRootless() {
storageOpts, err = GetRootlessStorageOpts()
if err != nil {
return storageOpts, volumePath, err
}
-
- volumePath, err = GetRootlessVolumeInfo()
+ volumePath, err = GetRootlessVolumePath()
if err != nil {
return storageOpts, volumePath, err
}
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 737e2dd96..8a52efa61 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -21,7 +21,7 @@ import (
// ListContainers ...
func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error {
var (
- listContainers []iopodman.ListContainerData
+ listContainers []iopodman.Container
)
containers, err := i.Runtime.GetAllContainers()
@@ -44,10 +44,10 @@ func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error {
}
// GetContainer ...
-func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, name string) error {
- ctr, err := i.Runtime.LookupContainer(name)
+func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
+ ctr, err := i.Runtime.LookupContainer(id)
if err != nil {
- return call.ReplyContainerNotFound(name)
+ return call.ReplyContainerNotFound(id)
}
opts := shared.PsOptions{
Namespace: true,
@@ -247,11 +247,6 @@ func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) er
return call.ReplyGetContainerStats(cs)
}
-// ResizeContainerTty ...
-func (i *LibpodAPI) ResizeContainerTty(call iopodman.VarlinkCall) error {
- return call.ReplyMethodNotImplemented("ResizeContainerTty")
-}
-
// StartContainer ...
func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
@@ -324,16 +319,6 @@ func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal
return call.ReplyKillContainer(ctr.ID())
}
-// UpdateContainer ...
-func (i *LibpodAPI) UpdateContainer(call iopodman.VarlinkCall) error {
- return call.ReplyMethodNotImplemented("UpdateContainer")
-}
-
-// RenameContainer ...
-func (i *LibpodAPI) RenameContainer(call iopodman.VarlinkCall) error {
- return call.ReplyMethodNotImplemented("RenameContainer")
-}
-
// PauseContainer ...
func (i *LibpodAPI) PauseContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
@@ -358,12 +343,6 @@ func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) err
return call.ReplyUnpauseContainer(ctr.ID())
}
-// AttachToContainer ...
-// TODO: DO we also want a different one for websocket?
-func (i *LibpodAPI) AttachToContainer(call iopodman.VarlinkCall) error {
- return call.ReplyMethodNotImplemented("AttachToContainer")
-}
-
// WaitContainer ...
func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
@@ -379,13 +358,13 @@ func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string) error
}
// RemoveContainer ...
-func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool) error {
+func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error {
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name)
}
- if err := i.Runtime.RemoveContainer(ctx, ctr, force); err != nil {
+ if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyRemoveContainer(ctr.ID())
@@ -406,7 +385,7 @@ func (i *LibpodAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error {
return call.ReplyErrorOccurred(err.Error())
}
if state != libpod.ContainerStateRunning {
- if err := i.Runtime.RemoveContainer(ctx, ctr, false); err != nil {
+ if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
deletedContainers = append(deletedContainers, ctr.ID())
diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go
index f1835a189..6b53b22c6 100644
--- a/pkg/varlinkapi/containers_create.go
+++ b/pkg/varlinkapi/containers_create.go
@@ -131,9 +131,14 @@ func varlinkCreateToCreateConfig(ctx context.Context, create iopodman.Create, ru
}
imageID := data.ID
+ var ImageVolumes map[string]struct{}
+ if data != nil && create.Image_volume_type != "ignore" {
+ ImageVolumes = data.Config.Volumes
+ }
+
config := &cc.CreateConfig{
Runtime: runtime,
- BuiltinImgVolumes: data.Config.Volumes,
+ BuiltinImgVolumes: ImageVolumes,
ConmonPidFile: create.Conmon_pidfile,
ImageVolumeType: create.Image_volume_type,
CapAdd: create.Cap_add,
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 5e0889645..b3090d2dd 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -24,10 +25,11 @@ import (
sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
- "github.com/docker/go-units"
+ "github.com/containers/storage/pkg/archive"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// ListImages lists all the images in the store
@@ -37,7 +39,7 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
if err != nil {
return call.ReplyErrorOccurred(fmt.Sprintf("unable to get list of images %q", err))
}
- var imageList []iopodman.ImageInList
+ var imageList []iopodman.Image
for _, image := range images {
labels, _ := image.Labels(getContext())
containers, _ := image.Containers()
@@ -52,12 +54,12 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
return call.ReplyErrorOccurred(err.Error())
}
- i := iopodman.ImageInList{
+ i := iopodman.Image{
Id: image.ID(),
ParentId: image.Parent,
RepoTags: image.Names(),
RepoDigests: repoDigests,
- Created: image.Created().String(),
+ Created: image.Created().Format(time.RFC3339),
Size: int64(*size),
VirtualSize: image.VirtualSize,
Containers: int64(len(containers)),
@@ -69,11 +71,11 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
return call.ReplyListImages(imageList)
}
-// GetImage returns a single image in the form of a ImageInList
-func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error {
- newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
+// GetImage returns a single image in the form of a Image
+func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error {
+ newImage, err := i.Runtime.ImageRuntime().NewFromLocal(id)
if err != nil {
- return call.ReplyImageNotFound(err.Error())
+ return call.ReplyImageNotFound(id)
}
labels, err := newImage.Labels(getContext())
if err != nil {
@@ -92,12 +94,12 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error {
return err
}
- il := iopodman.ImageInList{
+ il := iopodman.Image{
Id: newImage.ID(),
ParentId: newImage.Parent,
RepoTags: newImage.Names(),
RepoDigests: repoDigests,
- Created: newImage.Created().String(),
+ Created: newImage.Created().Format(time.RFC3339),
Size: int64(*size),
VirtualSize: newImage.VirtualSize,
Containers: int64(len(containers)),
@@ -109,83 +111,46 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error {
// BuildImage ...
func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildInfo) error {
var (
- memoryLimit int64
- memorySwap int64
- namespace []buildah.NamespaceOption
- err error
+ namespace []buildah.NamespaceOption
+ err error
)
systemContext := types.SystemContext{}
- dockerfiles := config.Dockerfile
- contextDir := ""
-
- for i := range dockerfiles {
- if strings.HasPrefix(dockerfiles[i], "http://") ||
- strings.HasPrefix(dockerfiles[i], "https://") ||
- strings.HasPrefix(dockerfiles[i], "git://") ||
- strings.HasPrefix(dockerfiles[i], "github.com/") {
- continue
- }
- absFile, err := filepath.Abs(dockerfiles[i])
- if err != nil {
- return errors.Wrapf(err, "error determining path to file %q", dockerfiles[i])
- }
- contextDir = filepath.Dir(absFile)
- dockerfiles[i], err = filepath.Rel(contextDir, absFile)
- if err != nil {
- return errors.Wrapf(err, "error determining path to file %q", dockerfiles[i])
- }
- break
- }
-
- pullPolicy := imagebuildah.PullNever
- if config.Pull {
- pullPolicy = imagebuildah.PullIfMissing
- }
-
- if config.Pull_always {
- pullPolicy = imagebuildah.PullAlways
- }
- manifestType := "oci" //nolint
- if config.Image_format != "" {
- manifestType = config.Image_format
- }
+ contextDir := config.ContextDir
- if strings.HasPrefix(manifestType, "oci") {
- manifestType = buildah.OCIv1ImageManifest
- } else if strings.HasPrefix(manifestType, "docker") {
- manifestType = buildah.Dockerv2ImageManifest
- } else {
- return call.ReplyErrorOccurred(fmt.Sprintf("unrecognized image type %q", manifestType))
+ newContextDir, err := ioutil.TempDir("", "buildTarball")
+ if err != nil {
+ call.ReplyErrorOccurred("unable to create tempdir")
}
+ logrus.Debugf("created new context dir at %s", newContextDir)
- if config.Memory != "" {
- memoryLimit, err = units.RAMInBytes(config.Memory)
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
+ reader, err := os.Open(contextDir)
+ if err != nil {
+ logrus.Errorf("failed to open the context dir tar file %s", contextDir)
+ return call.ReplyErrorOccurred(fmt.Sprintf("unable to open context dir tar file %s", contextDir))
}
-
- if config.Memory_swap != "" {
- memorySwap, err = units.RAMInBytes(config.Memory_swap)
- if err != nil {
- return call.ReplyErrorOccurred(err.Error())
- }
+ defer reader.Close()
+ if err := archive.Untar(reader, newContextDir, &archive.TarOptions{}); err != nil {
+ logrus.Errorf("fail to untar the context dir tarball (%s) to the context dir (%s)", contextDir, newContextDir)
+ return call.ReplyErrorOccurred(fmt.Sprintf("unable to untar context dir %s", contextDir))
}
+ logrus.Debugf("untar of %s successful", contextDir)
+ // All output (stdout, stderr) is captured in output as well
output := bytes.NewBuffer([]byte{})
+
commonOpts := &buildah.CommonBuildOptions{
- AddHost: config.Add_hosts,
- CgroupParent: config.Cgroup_parent,
- CPUPeriod: uint64(config.Cpu_period),
- CPUQuota: config.Cpu_quota,
- CPUSetCPUs: config.Cpuset_cpus,
- CPUSetMems: config.Cpuset_mems,
- Memory: memoryLimit,
- MemorySwap: memorySwap,
- ShmSize: config.Shm_size,
- Ulimit: config.Ulimit,
- Volumes: config.Volume,
+ AddHost: config.BuildOptions.AddHosts,
+ CgroupParent: config.BuildOptions.CgroupParent,
+ CPUPeriod: uint64(config.BuildOptions.CpuPeriod),
+ CPUQuota: config.BuildOptions.CpuQuota,
+ CPUSetCPUs: config.BuildOptions.CpusetCpus,
+ CPUSetMems: config.BuildOptions.CpusetMems,
+ Memory: config.BuildOptions.Memory,
+ MemorySwap: config.BuildOptions.MemorySwap,
+ ShmSize: config.BuildOptions.ShmSize,
+ Ulimit: config.BuildOptions.Ulimit,
+ Volumes: config.BuildOptions.Volume,
}
hostNetwork := buildah.NamespaceOption{
@@ -196,37 +161,68 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
namespace = append(namespace, hostNetwork)
options := imagebuildah.BuildOptions{
- ContextDirectory: contextDir,
- PullPolicy: pullPolicy,
- Compression: imagebuildah.Gzip,
- Quiet: false,
- //SignaturePolicyPath:
- Args: config.Build_args,
- //Output:
- AdditionalTags: config.Tags,
- //Runtime: runtime.
- //RuntimeArgs: ,
- OutputFormat: manifestType,
- SystemContext: &systemContext,
- CommonBuildOpts: commonOpts,
- Squash: config.Squash,
- Labels: config.Label,
- Annotations: config.Annotations,
- ReportWriter: output,
- NamespaceOptions: namespace,
+ CommonBuildOpts: commonOpts,
+ AdditionalTags: config.AdditionalTags,
+ Annotations: config.Annotations,
+ Args: config.BuildArgs,
+ CNIConfigDir: config.CniConfigDir,
+ CNIPluginPath: config.CniPluginDir,
+ Compression: stringCompressionToArchiveType(config.Compression),
+ ContextDirectory: newContextDir,
+ DefaultMountsFilePath: config.DefaultsMountFilePath,
+ Err: output,
+ ForceRmIntermediateCtrs: config.ForceRmIntermediateCtrs,
+ IIDFile: config.Iidfile,
+ Labels: config.Label,
+ Layers: config.Layers,
+ NoCache: config.Nocache,
+ Out: output,
+ Output: config.Output,
+ NamespaceOptions: namespace,
+ OutputFormat: config.OutputFormat,
+ PullPolicy: stringPullPolicyToType(config.PullPolicy),
+ Quiet: config.Quiet,
+ RemoveIntermediateCtrs: config.RemoteIntermediateCtrs,
+ ReportWriter: output,
+ RuntimeArgs: config.RuntimeArgs,
+ SignaturePolicyPath: config.SignaturePolicyPath,
+ Squash: config.Squash,
+ SystemContext: &systemContext,
}
if call.WantsMore() {
call.Continues = true
}
- c := build(i.Runtime, options, config.Dockerfile)
+ var newPathDockerFiles []string
+
+ for _, d := range config.Dockerfiles {
+ if strings.HasPrefix(d, "http://") ||
+ strings.HasPrefix(d, "https://") ||
+ strings.HasPrefix(d, "git://") ||
+ strings.HasPrefix(d, "github.com/") {
+ newPathDockerFiles = append(newPathDockerFiles, d)
+ continue
+ }
+ base := filepath.Base(d)
+ newPathDockerFiles = append(newPathDockerFiles, filepath.Join(newContextDir, base))
+ }
+
+ c := build(i.Runtime, options, newPathDockerFiles)
var log []string
done := false
for {
- line, err := output.ReadString('\n')
+ outputLine, err := output.ReadString('\n')
if err == nil {
- log = append(log, line)
+ log = append(log, outputLine)
+ if call.WantsMore() {
+ // we want to reply with what we have
+ br := iopodman.MoreResponse{
+ Logs: log,
+ }
+ call.ReplyBuildImage(br)
+ log = []string{}
+ }
continue
} else if err == io.EOF {
select {
@@ -236,15 +232,10 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
}
done = true
default:
- if !call.WantsMore() {
+ if call.WantsMore() {
time.Sleep(1 * time.Second)
break
}
- br := iopodman.BuildResponse{
- Logs: log,
- }
- call.ReplyBuildImage(br)
- log = []string{}
}
} else {
return call.ReplyErrorOccurred(err.Error())
@@ -254,11 +245,12 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI
}
}
call.Continues = false
- newImage, err := i.Runtime.ImageRuntime().NewFromLocal(config.Tags[0])
+
+ newImage, err := i.Runtime.ImageRuntime().NewFromLocal(config.Output)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- br := iopodman.BuildResponse{
+ br := iopodman.MoreResponse{
Logs: log,
Id: newImage.ID(),
}
@@ -276,12 +268,6 @@ func build(runtime *libpod.Runtime, options imagebuildah.BuildOptions, dockerfil
return c
}
-// CreateImage ...
-// TODO With Pull being added, should we skip Create?
-func (i *LibpodAPI) CreateImage(call iopodman.VarlinkCall) error {
- return call.ReplyMethodNotImplemented("CreateImage")
-}
-
// 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 {
@@ -315,7 +301,7 @@ func (i *LibpodAPI) HistoryImage(call iopodman.VarlinkCall, name string) error {
for _, hist := range history {
imageHistory := iopodman.ImageHistory{
Id: hist.ID,
- Created: hist.Created.String(),
+ Created: hist.Created.Format(time.RFC3339),
CreatedBy: hist.CreatedBy,
Tags: newImage.Names(),
Size: hist.Size,
@@ -327,12 +313,11 @@ func (i *LibpodAPI) HistoryImage(call iopodman.VarlinkCall, name string) error {
}
// PushImage pushes an local image to registry
-func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVerify bool, signaturePolicy, creds, certDir string, compress bool, format string, removeSignatures bool, signBy string) error {
+func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVerify *bool, signaturePolicy, creds, certDir string, compress bool, format string, removeSignatures bool, signBy string) error {
var (
registryCreds *types.DockerAuthConfig
manifestType string
)
-
newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
return call.ReplyImageNotFound(err.Error())
@@ -352,8 +337,8 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVe
DockerRegistryCreds: registryCreds,
DockerCertPath: certDir,
}
- if !tlsVerify {
- dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue
+ if tlsVerify != nil {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!*tlsVerify)
}
if format != "" {
switch format {
@@ -372,10 +357,59 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVe
SignBy: signBy,
}
- if err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", signaturePolicy, nil, compress, so, &dockerRegistryOptions, nil); err != nil {
- return call.ReplyErrorOccurred(err.Error())
+ 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)
+ c <- err
+ close(c)
+ }()
+
+ // TODO When pull output gets fixed for the remote client, we need to look into how we can turn below
+ // into something re-usable. it is in build too
+ 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 push 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.ReplyPushImage(br)
+ log = []string{}
+ }
+ } else {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if done {
+ break
+ }
}
- return call.ReplyPushImage(newImage.ID())
+ call.Continues = false
+
+ br := iopodman.MoreResponse{
+ Logs: log,
+ }
+ return call.ReplyPushImage(br)
}
// TagImage accepts an image name and tag as strings and tags an image in the local store.
@@ -405,17 +439,24 @@ func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bo
return call.ReplyRemoveImage(newImage.ID())
}
-// SearchImage searches all registries configured in /etc/containers/registries.conf for an image
+// 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) SearchImage(call iopodman.VarlinkCall, name string, limit int64) error {
+func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, tlsVerify *bool) error {
sc := image.GetSystemContext("", "", false)
+ if tlsVerify != nil {
+ sc.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!*tlsVerify)
+ }
registries, err := sysreg.GetRegistries()
if err != nil {
return call.ReplyErrorOccurred(fmt.Sprintf("unable to get system registries: %q", err))
}
- var imageResults []iopodman.ImageSearch
+ var imageResults []iopodman.ImageSearchResult
for _, reg := range registries {
- results, err := docker.SearchRegistry(getContext(), sc, reg, name, int(limit))
+ 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
@@ -426,7 +467,7 @@ func (i *LibpodAPI) SearchImage(call iopodman.VarlinkCall, name string, limit in
return call.ReplyErrorOccurred(err.Error())
}
for _, result := range results {
- i := iopodman.ImageSearch{
+ i := iopodman.ImageSearchResult{
Description: result.Description,
Is_official: result.IsOfficial,
Is_automated: result.IsAutomated,
@@ -436,7 +477,7 @@ func (i *LibpodAPI) SearchImage(call iopodman.VarlinkCall, name string, limit in
imageResults = append(imageResults, i)
}
}
- return call.ReplySearchImage(imageResults)
+ return call.ReplySearchImages(imageResults)
}
// DeleteUnusedImages deletes any images that do not have containers associated with it.
@@ -545,7 +586,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str
}
// PullImage pulls an image from a registry to the image store.
-func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, certDir, creds, signaturePolicy string, tlsVerify bool) error {
+func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, certDir, creds, signaturePolicy string, tlsVerify *bool) error {
var (
registryCreds *types.DockerAuthConfig
imageID string
@@ -562,8 +603,8 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, certDir, c
DockerRegistryCreds: registryCreds,
DockerCertPath: certDir,
}
- if tlsVerify {
- dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!tlsVerify)
+ if tlsVerify != nil {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!*tlsVerify)
}
so := image.SigningOptions{}
@@ -606,8 +647,8 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.
dockerRegistryOptions := image.DockerRegistryOptions{
DockerCertPath: input.CertDir,
}
- if !input.TlsVerify {
- dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue
+ if input.TlsVerify != nil {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!*input.TlsVerify)
}
stdErr := os.Stderr
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index 376502f21..3f32615ec 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -3,6 +3,7 @@ package varlinkapi
import (
goruntime "runtime"
"strings"
+ "time"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
@@ -15,22 +16,14 @@ func (i *LibpodAPI) GetVersion(call iopodman.VarlinkCall) error {
return err
}
- return call.ReplyGetVersion(iopodman.Version{
- Remote_api_version: versionInfo.RemoteAPIVersion,
- Version: versionInfo.Version,
- Go_version: versionInfo.GoVersion,
- Git_commit: versionInfo.GitCommit,
- Built: versionInfo.Built,
- Os_arch: versionInfo.OsArch,
- })
-}
-
-// Ping returns a simple string "OK" response for clients to make sure
-// the service is working.
-func (i *LibpodAPI) Ping(call iopodman.VarlinkCall) error {
- return call.ReplyPing(iopodman.StringResponse{
- Message: "OK",
- })
+ return call.ReplyGetVersion(
+ versionInfo.Version,
+ versionInfo.GoVersion,
+ versionInfo.GitCommit,
+ time.Unix(versionInfo.Built, 0).Format(time.RFC3339),
+ versionInfo.OsArch,
+ versionInfo.RemoteAPIVersion,
+ )
}
// GetInfo returns details about the podman host and its stores
diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go
index 0cb7e5e2e..9a97bc810 100644
--- a/pkg/varlinkapi/transfers.go
+++ b/pkg/varlinkapi/transfers.go
@@ -8,6 +8,7 @@ import (
"os"
"github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/sirupsen/logrus"
)
// SendFile allows a client to send a file to the varlink server
@@ -34,6 +35,7 @@ func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int
return err
}
+ logrus.Debugf("successfully received %s", outputFile.Name())
// Send an ACK to the client
call.Call.Writer.WriteString(fmt.Sprintf("%s:", outputFile.Name()))
call.Call.Writer.Flush()
diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go
index a80c8db41..7e487c03a 100644
--- a/pkg/varlinkapi/util.go
+++ b/pkg/varlinkapi/util.go
@@ -3,11 +3,14 @@ package varlinkapi
import (
"context"
"strconv"
+ "strings"
"time"
+ "github.com/containers/buildah"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ "github.com/containers/storage/pkg/archive"
)
// getContext returns a non-nil, empty context
@@ -15,7 +18,7 @@ func getContext() context.Context {
return context.TODO()
}
-func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct) iopodman.ListContainerData {
+func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct) iopodman.Container {
var (
mounts []iopodman.ContainerMount
ports []iopodman.ContainerPortMappings
@@ -56,12 +59,12 @@ func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct
Ipc: ns.IPC,
}
- lc := iopodman.ListContainerData{
+ lc := iopodman.Container{
Id: containerID,
Image: batchInfo.ConConfig.RootfsImageName,
Imageid: batchInfo.ConConfig.RootfsImageID,
Command: batchInfo.ConConfig.Spec.Process.Args,
- Createdat: batchInfo.ConConfig.CreatedTime.String(),
+ Createdat: batchInfo.ConConfig.CreatedTime.Format(time.RFC3339),
Runningfor: time.Since(batchInfo.ConConfig.CreatedTime).String(),
Status: batchInfo.ConState.String(),
Ports: ports,
@@ -107,7 +110,7 @@ func makeListPod(pod *libpod.Pod, batchInfo shared.PsOptions) (iopodman.ListPodD
listPodsContainers = append(listPodsContainers, makeListPodContainers(ctr.ID(), batchInfo))
}
listPod := iopodman.ListPodData{
- Createdat: pod.CreatedTime().String(),
+ Createdat: pod.CreatedTime().Format(time.RFC3339),
Id: pod.ID(),
Name: pod.Name(),
Status: status,
@@ -133,3 +136,27 @@ func handlePodCall(call iopodman.VarlinkCall, pod *libpod.Pod, ctrErrs map[strin
return nil
}
+
+func stringCompressionToArchiveType(s string) archive.Compression {
+ switch strings.ToUpper(s) {
+ case "BZIP2":
+ return archive.Bzip2
+ case "GZIP":
+ return archive.Gzip
+ case "XZ":
+ return archive.Xz
+ }
+ return archive.Uncompressed
+}
+
+func stringPullPolicyToType(s string) buildah.PullPolicy {
+ switch strings.ToUpper(s) {
+ case "PULLIFMISSING":
+ return buildah.PullIfMissing
+ case "PULLALWAYS":
+ return buildah.PullAlways
+ case "PULLNEVER":
+ return buildah.PullNever
+ }
+ return buildah.PullIfMissing
+}
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
new file mode 100644
index 000000000..02874d2b1
--- /dev/null
+++ b/pkg/varlinkapi/volumes.go
@@ -0,0 +1,90 @@
+package varlinkapi
+
+import (
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
+)
+
+// VolumeCreate creates a libpod volume based on input from a varlink connection
+func (i *LibpodAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.VolumeCreateOpts) error {
+ var volumeOptions []libpod.VolumeCreateOption
+
+ if len(options.VolumeName) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeName(options.VolumeName))
+ }
+ if len(options.Driver) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(options.Driver))
+ }
+ if len(options.Labels) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels))
+ }
+ if len(options.Options) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeOptions(options.Options))
+ }
+ newVolume, err := i.Runtime.NewVolume(getContext(), volumeOptions...)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyVolumeCreate(newVolume.Name())
+}
+
+// VolumeRemove removes volumes by options.All or options.Volumes
+func (i *LibpodAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error {
+ deletedVolumes, err := i.Runtime.RemoveVolumes(getContext(), options.Volumes, options.All, options.Force)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyVolumeRemove(deletedVolumes)
+}
+
+// GetVolumes returns all the volumes known to the remote system
+func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error {
+ var (
+ err error
+ reply []*libpod.Volume
+ volumes []iopodman.Volume
+ )
+ if all {
+ reply, err = i.Runtime.GetAllVolumes()
+ } else {
+ for _, v := range args {
+ vol, err := i.Runtime.GetVolume(v)
+ if err != nil {
+ return err
+ }
+ reply = append(reply, vol)
+ }
+ }
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ // Build the iopodman.volume struct for the return
+ for _, v := range reply {
+ newVol := iopodman.Volume{
+ Driver: v.Driver(),
+ Labels: v.Labels(),
+ MountPoint: v.MountPoint(),
+ Name: v.Name(),
+ Options: v.Options(),
+ Scope: v.Scope(),
+ }
+ volumes = append(volumes, newVol)
+ }
+ return call.ReplyGetVolumes(volumes)
+}
+
+// VolumesPrune removes unused images via a varlink call
+func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
+ var errs []string
+ prunedNames, prunedErrors := i.Runtime.PruneVolumes(getContext())
+ if len(prunedErrors) == 0 {
+ return call.ReplyVolumesPrune(prunedNames, []string{})
+ }
+
+ // We need to take the errors and capture their strings to go back over
+ // varlink
+ for _, e := range prunedErrors {
+ errs = append(errs, e.Error())
+ }
+ return call.ReplyVolumesPrune(prunedNames, errs)
+}
diff --git a/test/README.md b/test/README.md
index 90dcdfe3d..ef3bfbcf9 100644
--- a/test/README.md
+++ b/test/README.md
@@ -66,7 +66,7 @@ switch is optional.
You can run a single file of integration tests using the go test command:
```
-GOPATH=~/go go test -v test/e2e/libpod_suite_test.go test/e2e/config.go test/e2e/config_amd64.go test/e2e/your_test.go
+GOPATH=~/go go test -v test/e2e/libpod_suite_test.go test/e2e/common_test.go test/e2e/config.go test/e2e/config_amd64.go test/e2e/your_test.go
```
#### Run all tests like PAPR
diff --git a/test/bin2img/bin2img.go b/test/bin2img/bin2img.go
deleted file mode 100644
index ed493dcc1..000000000
--- a/test/bin2img/bin2img.go
+++ /dev/null
@@ -1,229 +0,0 @@
-package main
-
-import (
- "archive/tar"
- "bytes"
- "context"
- "encoding/json"
- "io"
- "os"
- "runtime"
-
- "github.com/containers/image/pkg/blobinfocache"
- "github.com/containers/image/storage"
- "github.com/containers/image/types"
- sstorage "github.com/containers/storage"
- "github.com/containers/storage/pkg/reexec"
- digest "github.com/opencontainers/go-digest"
- specs "github.com/opencontainers/image-spec/specs-go"
- "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/sirupsen/logrus"
- "github.com/urfave/cli"
-)
-
-func main() {
- if reexec.Init() {
- return
- }
-
- app := cli.NewApp()
- app.Name = "bin2img"
- app.Usage = "barebones image builder"
- app.Version = "0.0.1"
-
- app.Flags = []cli.Flag{
- cli.BoolFlag{
- Name: "debug",
- Usage: "turn on debug logging",
- },
- cli.StringFlag{
- Name: "root",
- Usage: "graph root directory",
- },
- cli.StringFlag{
- Name: "runroot",
- Usage: "run root directory",
- },
- cli.StringFlag{
- Name: "storage-driver",
- Usage: "storage driver",
- },
- cli.StringSliceFlag{
- Name: "storage-opt",
- Usage: "storage option",
- },
- cli.StringFlag{
- Name: "image-name",
- Usage: "set image name",
- Value: "kubernetes/pause",
- },
- cli.StringFlag{
- Name: "source-binary",
- Usage: "source binary",
- Value: "../../pause/pause",
- },
- cli.StringFlag{
- Name: "image-binary",
- Usage: "image binary",
- Value: "/pause",
- },
- }
-
- app.Action = func(c *cli.Context) error {
- debug := c.GlobalBool("debug")
- rootDir := c.GlobalString("root")
- runrootDir := c.GlobalString("runroot")
- storageDriver := c.GlobalString("storage-driver")
- storageOptions := c.GlobalStringSlice("storage-opt")
- imageName := c.GlobalString("image-name")
- sourceBinary := c.GlobalString("source-binary")
- imageBinary := c.GlobalString("image-binary")
-
- if debug {
- logrus.SetLevel(logrus.DebugLevel)
- } else {
- logrus.SetLevel(logrus.ErrorLevel)
- }
- if rootDir == "" && runrootDir != "" {
- logrus.Errorf("must set --root and --runroot, or neither")
- os.Exit(1)
- }
- if rootDir != "" && runrootDir == "" {
- logrus.Errorf("must set --root and --runroot, or neither")
- os.Exit(1)
- }
- storeOptions := sstorage.DefaultStoreOptions
- if rootDir != "" && runrootDir != "" {
- storeOptions.GraphDriverName = storageDriver
- storeOptions.GraphDriverOptions = storageOptions
- storeOptions.GraphRoot = rootDir
- storeOptions.RunRoot = runrootDir
- }
- store, err := sstorage.GetStore(storeOptions)
- if err != nil {
- logrus.Errorf("error opening storage: %v", err)
- os.Exit(1)
- }
- defer func() {
- _, _ = store.Shutdown(false)
- }()
-
- layerBuffer := &bytes.Buffer{}
- binary, err := os.Open(sourceBinary)
- if err != nil {
- logrus.Errorf("error opening image binary: %v", err)
- os.Exit(1)
- }
- binInfo, err := binary.Stat()
- if err != nil {
- logrus.Errorf("error statting image binary: %v", err)
- os.Exit(1)
- }
- archive := tar.NewWriter(layerBuffer)
- err = archive.WriteHeader(&tar.Header{
- Name: imageBinary,
- Size: binInfo.Size(),
- Mode: 0555,
- ModTime: binInfo.ModTime(),
- Typeflag: tar.TypeReg,
- Uname: "root",
- Gname: "root",
- })
- if err != nil {
- logrus.Errorf("error writing archive header: %v", err)
- os.Exit(1)
- }
- _, err = io.Copy(archive, binary)
- if err != nil {
- logrus.Errorf("error archiving image binary: %v", err)
- os.Exit(1)
- }
- archive.Close()
- binary.Close()
- layerInfo := types.BlobInfo{
- Digest: digest.Canonical.FromBytes(layerBuffer.Bytes()),
- Size: int64(layerBuffer.Len()),
- }
-
- ref, err := storage.Transport.ParseStoreReference(store, imageName)
- if err != nil {
- logrus.Errorf("error parsing image name: %v", err)
- os.Exit(1)
- }
- ctx := context.TODO()
- img, err := ref.NewImageDestination(ctx, nil)
- if err != nil {
- logrus.Errorf("error preparing to write image: %v", err)
- os.Exit(1)
- }
- defer img.Close()
- layer, err := img.PutBlob(ctx, layerBuffer, layerInfo, blobinfocache.NewMemoryCache(), false)
- if err != nil {
- logrus.Errorf("error preparing to write image: %v", err)
- os.Exit(1)
- }
- config := &v1.Image{
- Architecture: runtime.GOARCH,
- OS: runtime.GOOS,
- Config: v1.ImageConfig{
- User: "root",
- Entrypoint: []string{imageBinary},
- },
- RootFS: v1.RootFS{
- Type: "layers",
- DiffIDs: []digest.Digest{
- layer.Digest,
- },
- },
- }
- cbytes, err := json.Marshal(config)
- if err != nil {
- logrus.Errorf("error encoding configuration: %v", err)
- os.Exit(1)
- }
- configInfo := types.BlobInfo{
- Digest: digest.Canonical.FromBytes(cbytes),
- Size: int64(len(cbytes)),
- }
- configInfo, err = img.PutBlob(ctx, bytes.NewBuffer(cbytes), configInfo, blobinfocache.NewMemoryCache(), false)
- if err != nil {
- logrus.Errorf("error saving configuration: %v", err)
- os.Exit(1)
- }
- manifest := &v1.Manifest{
- Versioned: specs.Versioned{
- SchemaVersion: 2,
- },
- Config: v1.Descriptor{
- MediaType: v1.MediaTypeImageConfig,
- Digest: configInfo.Digest,
- Size: int64(len(cbytes)),
- },
- Layers: []v1.Descriptor{{
- MediaType: v1.MediaTypeImageLayer,
- Digest: layer.Digest,
- Size: layer.Size,
- }},
- }
- mbytes, err := json.Marshal(manifest)
- if err != nil {
- logrus.Errorf("error encoding manifest: %v", err)
- os.Exit(1)
- }
- err = img.PutManifest(ctx, mbytes)
- if err != nil {
- logrus.Errorf("error saving manifest: %v", err)
- os.Exit(1)
- }
- err = img.Commit(ctx)
- if err != nil {
- logrus.Errorf("error committing image: %v", err)
- os.Exit(1)
- }
- return nil
- }
-
- if err := app.Run(os.Args); err != nil {
- logrus.Fatal(err)
- }
-}
diff --git a/test/copyimg/copyimg.go b/test/copyimg/copyimg.go
deleted file mode 100644
index da00964fa..000000000
--- a/test/copyimg/copyimg.go
+++ /dev/null
@@ -1,204 +0,0 @@
-package main
-
-import (
- "context"
- "os"
-
- "github.com/containers/image/copy"
- "github.com/containers/image/signature"
- "github.com/containers/image/storage"
- "github.com/containers/image/transports/alltransports"
- "github.com/containers/image/types"
- sstorage "github.com/containers/storage"
- "github.com/containers/storage/pkg/reexec"
- "github.com/sirupsen/logrus"
- "github.com/urfave/cli"
-)
-
-func main() {
- if reexec.Init() {
- return
- }
-
- app := cli.NewApp()
- app.Name = "copyimg"
- app.Usage = "barebones image copier"
- app.Version = "0.0.1"
-
- app.Flags = []cli.Flag{
- cli.BoolFlag{
- Name: "debug",
- Usage: "turn on debug logging",
- },
- cli.StringFlag{
- Name: "root",
- Usage: "graph root directory",
- },
- cli.StringFlag{
- Name: "runroot",
- Usage: "run root directory",
- },
- cli.StringFlag{
- Name: "storage-driver",
- Usage: "storage driver",
- },
- cli.StringSliceFlag{
- Name: "storage-opt",
- Usage: "storage option",
- },
- cli.StringFlag{
- Name: "signature-policy",
- Usage: "signature policy",
- },
- cli.StringFlag{
- Name: "image-name",
- Usage: "set image name",
- },
- cli.StringFlag{
- Name: "add-name",
- Usage: "name to add to image",
- },
- cli.StringFlag{
- Name: "import-from",
- Usage: "import source",
- },
- cli.StringFlag{
- Name: "export-to",
- Usage: "export target",
- },
- }
-
- app.Action = func(c *cli.Context) error {
- var store sstorage.Store
- var ref, importRef, exportRef types.ImageReference
- var err error
-
- debug := c.GlobalBool("debug")
- rootDir := c.GlobalString("root")
- runrootDir := c.GlobalString("runroot")
- storageDriver := c.GlobalString("storage-driver")
- storageOptions := c.GlobalStringSlice("storage-opt")
- signaturePolicy := c.GlobalString("signature-policy")
- imageName := c.GlobalString("image-name")
- addName := c.GlobalString("add-name")
- importFrom := c.GlobalString("import-from")
- exportTo := c.GlobalString("export-to")
-
- if debug {
- logrus.SetLevel(logrus.DebugLevel)
- } else {
- logrus.SetLevel(logrus.ErrorLevel)
- }
-
- if imageName != "" {
- if rootDir == "" && runrootDir != "" {
- logrus.Errorf("must set --root and --runroot, or neither")
- os.Exit(1)
- }
- if rootDir != "" && runrootDir == "" {
- logrus.Errorf("must set --root and --runroot, or neither")
- os.Exit(1)
- }
- storeOptions := sstorage.DefaultStoreOptions
- if rootDir != "" && runrootDir != "" {
- storeOptions.GraphDriverName = storageDriver
- storeOptions.GraphDriverOptions = storageOptions
- storeOptions.GraphRoot = rootDir
- storeOptions.RunRoot = runrootDir
- }
- store, err = sstorage.GetStore(storeOptions)
- if err != nil {
- logrus.Errorf("error opening storage: %v", err)
- os.Exit(1)
- }
- defer func() {
- _, _ = store.Shutdown(false)
- }()
-
- storage.Transport.SetStore(store)
- ref, err = storage.Transport.ParseStoreReference(store, imageName)
- if err != nil {
- logrus.Errorf("error parsing image name: %v", err)
- os.Exit(1)
- }
- }
-
- systemContext := types.SystemContext{
- SignaturePolicyPath: signaturePolicy,
- }
- policy, err := signature.DefaultPolicy(&systemContext)
- if err != nil {
- logrus.Errorf("error loading signature policy: %v", err)
- os.Exit(1)
- }
- policyContext, err := signature.NewPolicyContext(policy)
- if err != nil {
- logrus.Errorf("error loading signature policy: %v", err)
- os.Exit(1)
- }
- defer func() {
- _ = policyContext.Destroy()
- }()
- options := &copy.Options{}
-
- if importFrom != "" {
- importRef, err = alltransports.ParseImageName(importFrom)
- if err != nil {
- logrus.Errorf("error parsing image name %v: %v", importFrom, err)
- os.Exit(1)
- }
- }
-
- if exportTo != "" {
- exportRef, err = alltransports.ParseImageName(exportTo)
- if err != nil {
- logrus.Errorf("error parsing image name %v: %v", exportTo, err)
- os.Exit(1)
- }
- }
-
- ctx := context.TODO()
- if imageName != "" {
- if importFrom != "" {
- _, err = copy.Image(ctx, policyContext, ref, importRef, options)
- if err != nil {
- logrus.Errorf("error importing %s: %v", importFrom, err)
- os.Exit(1)
- }
- }
- if addName != "" {
- destImage, err1 := storage.Transport.GetStoreImage(store, ref)
- if err1 != nil {
- logrus.Errorf("error finding image: %v", err1)
- os.Exit(1)
- }
- names := append(destImage.Names, imageName, addName)
- err = store.SetNames(destImage.ID, names)
- if err != nil {
- logrus.Errorf("error adding name to %s: %v", imageName, err)
- os.Exit(1)
- }
- }
- if exportTo != "" {
- _, err = copy.Image(ctx, policyContext, exportRef, ref, options)
- if err != nil {
- logrus.Errorf("error exporting %s: %v", exportTo, err)
- os.Exit(1)
- }
- }
- } else {
- if importFrom != "" && exportTo != "" {
- _, err = copy.Image(ctx, policyContext, exportRef, importRef, options)
- if err != nil {
- logrus.Errorf("error copying %s to %s: %v", importFrom, exportTo, err)
- os.Exit(1)
- }
- }
- }
- return nil
- }
-
- if err := app.Run(os.Args); err != nil {
- logrus.Fatal(err)
- }
-}
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
new file mode 100644
index 000000000..e1e760ee0
--- /dev/null
+++ b/test/e2e/cp_test.go
@@ -0,0 +1,115 @@
+// +build !remoteclient
+
+package integration
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman cp", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
+ GinkgoWriter.Write([]byte(timedResult))
+ })
+
+ It("podman cp file", func() {
+ path, err := os.Getwd()
+ if err != nil {
+ os.Exit(1)
+ }
+ filePath := filepath.Join(path, "cp_test.txt")
+ fromHostToContainer := []byte("copy from host to container")
+ err = ioutil.WriteFile(filePath, fromHostToContainer, 0644)
+ if err != nil {
+ os.Exit(1)
+ }
+
+ session := podmanTest.Podman([]string{"create", ALPINE, "cat", "foo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ name := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"cp", filepath.Join(path, "cp_test.txt"), name + ":foo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"start", "-a", name})
+ session.WaitWithDefaultTimeout()
+
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("copy from host to container"))
+
+ session = podmanTest.Podman([]string{"cp", name + ":foo", filepath.Join(path, "cp_from_container")})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ c := exec.Command("cat", filepath.Join(path, "cp_from_container"))
+ output, err := c.Output()
+ if err != nil {
+ os.Exit(1)
+ }
+ Expect(string(output)).To(Equal("copy from host to container"))
+ })
+
+ It("podman cp file to dir", func() {
+ path, err := os.Getwd()
+ if err != nil {
+ os.Exit(1)
+ }
+ filePath := filepath.Join(path, "cp_test.txt")
+ fromHostToContainer := []byte("copy from host to container directory")
+ err = ioutil.WriteFile(filePath, fromHostToContainer, 0644)
+ if err != nil {
+ os.Exit(1)
+ }
+ session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foodir/"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"ps", "-a", "-q"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ name := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"cp", filepath.Join(path, "cp_test.txt"), name + ":foodir/"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"start", "-a", name})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("cp_test.txt"))
+
+ session = podmanTest.Podman([]string{"cp", name + ":foodir/cp_test.txt", path + "/receive/"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ c := exec.Command("cat", filepath.Join(path, "receive", "cp_test.txt"))
+ output, err := c.Output()
+ if err != nil {
+ os.Exit(1)
+ }
+ Expect(string(output)).To(Equal("copy from host to container directory"))
+ })
+})
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index b28a0c428..9a526b778 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -142,7 +142,7 @@ var _ = Describe("Podman create", func() {
}
mountPath := filepath.Join(podmanTest.TempDir, "secrets")
os.Mkdir(mountPath, 0755)
- session := podmanTest.Podman([]string{"create", "--name", "test", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
+ session := podmanTest.Podman([]string{"create", "--name", "test", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"start", "test"})
@@ -153,7 +153,7 @@ var _ = Describe("Podman create", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("/create/test rw"))
- session = podmanTest.Podman([]string{"create", "--name", "test_ro", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,ro", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"create", "--name", "test_ro", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,ro", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"start", "test_ro"})
@@ -164,7 +164,7 @@ var _ = Describe("Podman create", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("/create/test ro"))
- session = podmanTest.Podman([]string{"create", "--name", "test_shared", "--rm", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,shared", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"create", "--name", "test_shared", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,shared", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"start", "test_shared"})
@@ -180,7 +180,7 @@ var _ = Describe("Podman create", func() {
mountPath = filepath.Join(podmanTest.TempDir, "scratchpad")
os.Mkdir(mountPath, 0755)
- session = podmanTest.Podman([]string{"create", "--name", "test_tmpfs", "--rm", "--mount", "type=tmpfs,target=/create/test", ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
+ session = podmanTest.Podman([]string{"create", "--name", "test_tmpfs", "--mount", "type=tmpfs,target=/create/test", ALPINE, "grep", "/create/test", "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"start", "test_tmpfs"})
diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go
index 43d089a24..e330c3a39 100644
--- a/test/e2e/pod_stats_test.go
+++ b/test/e2e/pod_stats_test.go
@@ -147,5 +147,28 @@ var _ = Describe("Podman pod stats", func() {
Expect(stats.ExitCode()).To(Equal(0))
Expect(stats.IsJSONOutputValid()).To(BeTrue())
})
+ It("podman stats with GO template", func() {
+ _, ec, podid := podmanTest.CreatePod("")
+ Expect(ec).To(Equal(0))
+
+ session := podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.CID}} {{.Pod}} {{.Mem}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}} {{.Pod}}\""})
+ stats.WaitWithDefaultTimeout()
+ Expect(stats.ExitCode()).To(Equal(0))
+ })
+
+ It("podman stats with invalid GO template", func() {
+ _, ec, podid := podmanTest.CreatePod("")
+ Expect(ec).To(Equal(0))
+
+ session := podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.ID}} \""})
+ stats.WaitWithDefaultTimeout()
+ Expect(stats.ExitCode()).ToNot(Equal(0))
+ })
})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 22a36bb6c..6bd49de33 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -322,7 +322,7 @@ var _ = Describe("Podman run", func() {
os.Setenv("NOTIFY_SOCKET", sock)
defer os.Unsetenv("NOTIFY_SOCKET")
- session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "NOTIFY_SOCKET"})
+ session := podmanTest.Podman([]string{"run", ALPINE, "printenv", "NOTIFY_SOCKET"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0))
diff --git a/test/e2e/volume_create_test.go b/test/e2e/volume_create_test.go
index 9e525786e..50ee63f2a 100644
--- a/test/e2e/volume_create_test.go
+++ b/test/e2e/volume_create_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/volume_inspect_test.go b/test/e2e/volume_inspect_test.go
index aacdbe8be..d0d5a601e 100644
--- a/test/e2e/volume_inspect_test.go
+++ b/test/e2e/volume_inspect_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go
index d2ee558c1..119d29d9b 100644
--- a/test/e2e/volume_ls_test.go
+++ b/test/e2e/volume_ls_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go
index 295b290e4..6a1e7d0e8 100644
--- a/test/e2e/volume_rm_test.go
+++ b/test/e2e/volume_rm_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
@@ -50,6 +48,7 @@ var _ = Describe("Podman volume rm", func() {
})
It("podman rm with --force flag", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"})
cid := session.OutputToString()
session.WaitWithDefaultTimeout()
diff --git a/transfer.md b/transfer.md
index af7904e5f..c2d472f08 100644
--- a/transfer.md
+++ b/transfer.md
@@ -37,11 +37,11 @@ There are other equivalents for these tools
| Existing Step | `Podman` (and friends) |
| :--- | :--- |
-| `docker attach` | [`podman exec`](./docs/podman-attach.1.md) |
+| `docker attach` | [`podman attach`](./docs/podman-attach.1.md) |
+| `docker cp` | [`podman cp`](./docs/podman-cp.1.md) |
| `docker build` | [`podman build`](./docs/podman-build.1.md) |
| `docker commit` | [`podman commit`](./docs/podman-commit.1.md) |
| `docker container`|[`podman container`](./docs/podman-container.1.md) |
-| `docker cp` | [`podman mount`](./docs/podman-cp.1.md) **** |
| `docker create` | [`podman create`](./docs/podman-create.1.md) |
| `docker diff` | [`podman diff`](./docs/podman-diff.1.md) |
| `docker export` | [`podman export`](./docs/podman-export.1.md) |
diff --git a/troubleshooting.md b/troubleshooting.md
index 3f66b56ef..24a1dc6cb 100644
--- a/troubleshooting.md
+++ b/troubleshooting.md
@@ -191,3 +191,66 @@ SELinux provides a boolean `container_manage_cgroup`, which allows container
processes to write to the cgroup file system. Turn on this boolean, on SELinux separated systems, to allow systemd to run properly in the container.
`setsebool -P container_manage_cgroup true`
+
+### 9) Newuidmap missing when running rootless Podman commands
+
+Rootless podman requires the newuidmap and newgidmap programs to be installed.
+
+#### Symptom
+
+If you are running podman or buildah as a not root user, you get an error complaining about
+a missing newuidmap executable.
+
+```
+podman run -ti fedora sh
+cannot find newuidmap: exec: "newuidmap": executable file not found in $PATH
+```
+
+#### Solution
+
+Install a version of shadow-utils that includes these executables. Note RHEL7 and Centos 7 will not have support for this until RHEL7.7 is released.
+
+### 10) podman fails to run in user namespace because /etc/subuid is not properly populated.
+
+Rootless podman requires the user running it to have a range of UIDs listed in /etc/subuid and /etc/subgid.
+
+#### Symptom
+
+If you are running podman or buildah as a user, you get an error complaining about
+a missing subuid ranges in /etc/subuid.
+
+```
+podman run -ti fedora sh
+No subuid ranges found for user "johndoe" in /etc/subuid
+```
+
+#### Solution
+
+Update the /etc/subuid and /etc/subgid with fields for users that look like:
+
+```
+cat /etc/subuid
+johndoe:100000:65536
+test:165536:65536
+```
+
+The format of this file is USERNAME:UID:RANGE
+
+* username as listed in /etc/passwd or getpwent.
+* The initial uid allocated for the user.
+* The size of the range of UIDs allocated for the user.
+
+This means johndoe is allocated UIDS 100000-165535 as well as his standard UID in the
+/etc/passwd file.
+
+You should ensure that each user has a unique range of uids, because overlapping UIDs,
+would potentially allow one user to attack another user.
+
+You could also use the usermod program to assign UIDs to a user.
+
+```
+usermod --add-subuids 200000-201000 --add-subgids 200000-201000 johndoe
+grep johndoe /etc/subuid /etc/subgid
+/etc/subuid:johndoe:200000:1001
+/etc/subgid:johndoe:200000:1001
+```
diff --git a/utils/utils.go b/utils/utils.go
index c7c5ab5cf..4a91b304f 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -9,6 +9,7 @@ import (
systemdDbus "github.com/coreos/go-systemd/dbus"
"github.com/godbus/dbus"
+ "github.com/pkg/errors"
)
// ExecCmd executes a command with args and returns its output as a string along
@@ -82,12 +83,9 @@ func newProp(name string, units interface{}) systemdDbus.Property {
}
}
-// DetachError is special error which returned in case of container detach.
-type DetachError struct{}
-
-func (DetachError) Error() string {
- return "detached from container"
-}
+// ErrDetach is an error indicating that the user manually detached from the
+// container.
+var ErrDetach = errors.New("detached from container")
// CopyDetachable is similar to io.Copy but support a detach key sequence to break out.
func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) {
@@ -108,7 +106,7 @@ func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, e
}
if i == len(keys)-1 {
// src.Close()
- return 0, DetachError{}
+ return 0, ErrDetach
}
nr, er = src.Read(buf)
}
diff --git a/vendor.conf b/vendor.conf
index 116120aa8..36452038f 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -15,8 +15,8 @@ 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 v1.3
-github.com/containers/storage v1.9
+github.com/containers/image 93bced01015eb94bec4821df1876314be8197680
+github.com/containers/storage 06b6c2e4cf254f5922a79da058c94ac2a65bb92f
github.com/containers/psgo v1.1
github.com/coreos/go-systemd v14
github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c
@@ -68,8 +68,6 @@ github.com/stretchr/testify v1.3.0
github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2
github.com/tchap/go-patricia v2.2.6
github.com/ulule/deepcopier ca99b135e50f526fde9cd88705f0ff2f3f95b77c
-# TODO: urfave/cli is not active anymore. We should transition to another library.
-github.com/urfave/cli b67dcf995b6a7b7f14fad5fcb7cc5441b05e814b
github.com/vbatts/tar-split v0.11.1
github.com/vishvananda/netlink v1.0.0
github.com/vishvananda/netns 13995c7128ccc8e51e9a6bd2b551020a27180abd
diff --git a/vendor/github.com/containers/image/copy/copy.go b/vendor/github.com/containers/image/copy/copy.go
index 89c7e580f..2d3a2a1a8 100644
--- a/vendor/github.com/containers/image/copy/copy.go
+++ b/vendor/github.com/containers/image/copy/copy.go
@@ -6,13 +6,16 @@ import (
"fmt"
"io"
"io/ioutil"
+ "os"
"reflect"
"runtime"
"strings"
"sync"
"time"
+ "github.com/containers/image/docker/reference"
"github.com/containers/image/image"
+ "github.com/containers/image/manifest"
"github.com/containers/image/pkg/blobinfocache"
"github.com/containers/image/pkg/compression"
"github.com/containers/image/signature"
@@ -22,6 +25,7 @@ import (
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh/terminal"
"golang.org/x/sync/semaphore"
pb "gopkg.in/cheggaaa/pb.v1"
)
@@ -84,6 +88,7 @@ type copier struct {
dest types.ImageDestination
rawSource types.ImageSource
reportWriter io.Writer
+ progressOutput io.Writer
progressInterval time.Duration
progress chan types.ProgressProperties
blobInfoCache types.BlobInfoCache
@@ -152,11 +157,19 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef,
}
}()
+ // If reportWriter is not a TTY (e.g., when piping to a file), do not
+ // print the progress bars to avoid long and hard to parse output.
+ // createProgressBar() will print a single line instead.
+ progressOutput := reportWriter
+ if !isTTY(reportWriter) {
+ progressOutput = ioutil.Discard
+ }
copyInParallel := dest.HasThreadSafePutBlob() && rawSource.HasThreadSafeGetBlob()
c := &copier{
dest: dest,
rawSource: rawSource,
reportWriter: reportWriter,
+ progressOutput: progressOutput,
progressInterval: options.ProgressInterval,
progress: options.Progress,
copyInParallel: copyInParallel,
@@ -201,7 +214,7 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef,
// Image copies a single (on-manifest-list) image unparsedImage, using policyContext to validate
// source image admissibility.
-func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.PolicyContext, options *Options, unparsedImage *image.UnparsedImage) (manifest []byte, retErr error) {
+func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.PolicyContext, options *Options, unparsedImage *image.UnparsedImage) (manifestBytes []byte, retErr error) {
// The caller is handling manifest lists; this could happen only if a manifest list contains a manifest list.
// Make sure we fail cleanly in such cases.
multiImage, err := isMultiImage(ctx, unparsedImage)
@@ -224,6 +237,26 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
return nil, errors.Wrapf(err, "Error initializing image from source %s", transports.ImageName(c.rawSource.Reference()))
}
+ // If the destination is a digested reference, make a note of that, determine what digest value we're
+ // expecting, and check that the source manifest matches it.
+ destIsDigestedReference := false
+ if named := c.dest.Reference().DockerReference(); named != nil {
+ if digested, ok := named.(reference.Digested); ok {
+ destIsDigestedReference = true
+ sourceManifest, _, err := src.Manifest(ctx)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Error reading manifest from source image")
+ }
+ matches, err := manifest.MatchesDigest(sourceManifest, digested.Digest())
+ if err != nil {
+ return nil, errors.Wrapf(err, "Error computing digest of source image's manifest")
+ }
+ if !matches {
+ return nil, errors.New("Digest of source image's manifest would not match destination reference")
+ }
+ }
+ }
+
if err := checkImageDestinationForCurrentRuntimeOS(ctx, options.DestinationCtx, src, c.dest); err != nil {
return nil, err
}
@@ -251,15 +284,15 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
manifestUpdates: &types.ManifestUpdateOptions{InformationOnly: types.ManifestUpdateInformation{Destination: c.dest}},
src: src,
// diffIDsAreNeeded is computed later
- canModifyManifest: len(sigs) == 0,
- // Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it.
- // This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path:
- // The signature makes the content non-repudiable, so it very much matters that the signature is made over exactly what the user intended.
- // We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk
- // that the compressed version coming from a third party may be designed to attack some other decompressor implementation,
- // and we would reuse and sign it.
- canSubstituteBlobs: len(sigs) == 0 && options.SignBy == "",
+ canModifyManifest: len(sigs) == 0 && !destIsDigestedReference,
}
+ // Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it.
+ // This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path:
+ // The signature makes the content non-repudiable, so it very much matters that the signature is made over exactly what the user intended.
+ // We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk
+ // that the compressed version coming from a third party may be designed to attack some other decompressor implementation,
+ // and we would reuse and sign it.
+ ic.canSubstituteBlobs = ic.canModifyManifest && options.SignBy == ""
if err := ic.updateEmbeddedDockerReference(); err != nil {
return nil, err
@@ -283,7 +316,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
// and at least with the OpenShift registry "acceptschema2" option, there is no way to detect the support
// without actually trying to upload something and getting a types.ManifestTypeRejectedError.
// So, try the preferred manifest MIME type. If the process succeeds, fine…
- manifest, err = ic.copyUpdatedConfigAndManifest(ctx)
+ manifestBytes, err = ic.copyUpdatedConfigAndManifest(ctx)
if err != nil {
logrus.Debugf("Writing manifest using preferred type %s failed: %v", preferredManifestMIMEType, err)
// … if it fails, _and_ the failure is because the manifest is rejected, we may have other options.
@@ -314,7 +347,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
}
// We have successfully uploaded a manifest.
- manifest = attemptedManifest
+ manifestBytes = attemptedManifest
errs = nil // Mark this as a success so that we don't abort below.
break
}
@@ -324,7 +357,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
}
if options.SignBy != "" {
- newSig, err := c.createSignature(manifest, options.SignBy)
+ newSig, err := c.createSignature(manifestBytes, options.SignBy)
if err != nil {
return nil, err
}
@@ -336,7 +369,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
return nil, errors.Wrap(err, "Error writing signatures")
}
- return manifest, nil
+ return manifestBytes, nil
}
// Printf writes a formatted string to c.reportWriter.
@@ -394,17 +427,30 @@ func shortDigest(d digest.Digest) string {
return d.Encoded()[:12]
}
-// createProgressBar creates a pb.ProgressBar.
-func createProgressBar(srcInfo types.BlobInfo, kind string, writer io.Writer) *pb.ProgressBar {
+// 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 = writer
+ 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 {
+ return terminal.IsTerminal(int(f.Fd()))
+ }
+ return false
+}
+
// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
func (ic *imageCopier) copyLayers(ctx context.Context) error {
srcInfos := ic.src.LayerInfos()
@@ -456,7 +502,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
bar.Finish()
} else {
cld.destInfo = srcLayer
- logrus.Debugf("Skipping foreign layer %q copy to %s\n", cld.destInfo.Digest, ic.c.dest.Reference().Transport().Name())
+ 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()
@@ -469,12 +515,13 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
progressBars := make([]*pb.ProgressBar, numLayers)
for i, srcInfo := range srcInfos {
- bar := createProgressBar(srcInfo, "blob", nil)
+ bar := ic.c.createProgressBar(srcInfo, "blob")
progressBars[i] = bar
}
progressPool := pb.NewPool(progressBars...)
- progressPool.Output = ic.c.reportWriter
+ progressPool.Output = ic.c.progressOutput
+
if err := progressPool.Start(); err != nil {
return errors.Wrapf(err, "error creating progress-bar pool")
}
@@ -568,7 +615,7 @@ 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 := createProgressBar(srcInfo, "config", c.reportWriter)
+ bar := c.createProgressBar(srcInfo, "config")
defer bar.Finish()
bar.Start()
destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, bar)
diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go
index 23d2ac70f..43eb22ba2 100644
--- a/vendor/github.com/containers/image/docker/docker_client.go
+++ b/vendor/github.com/containers/image/docker/docker_client.go
@@ -91,7 +91,6 @@ type dockerClient struct {
password string
signatureBase signatureStorageBase
scope authScope
- extraScope *authScope // If non-nil, a temporary extra token scope (necessary for mounting from another repo)
// The following members are detected registry properties:
// They are set after a successful detectProperties(), and never change afterwards.
scheme string // Empty value also used to indicate detectProperties() has not yet succeeded.
@@ -282,7 +281,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
client.username = username
client.password = password
- resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth)
+ resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth, nil)
if err != nil {
return err
}
@@ -362,8 +361,8 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
q.Set("n", strconv.Itoa(limit))
u.RawQuery = q.Encode()
- logrus.Debugf("trying to talk to v1 search endpoint\n")
- resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth)
+ logrus.Debugf("trying to talk to v1 search endpoint")
+ resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth, nil)
if err != nil {
logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err)
} else {
@@ -379,8 +378,8 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
}
}
- logrus.Debugf("trying to talk to v2 search endpoint\n")
- resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth)
+ logrus.Debugf("trying to talk to v2 search endpoint")
+ resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth, nil)
if err != nil {
logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err)
} else {
@@ -409,20 +408,20 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/.
-func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader, auth sendAuth) (*http.Response, error) {
+func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader, auth sendAuth, extraScope *authScope) (*http.Response, error) {
if err := c.detectProperties(ctx); err != nil {
return nil, err
}
url := fmt.Sprintf("%s://%s%s", c.scheme, c.registry, path)
- return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth)
+ return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope)
}
// makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// streamLen, if not -1, specifies the length of the data expected on stream.
// makeRequest should generally be preferred.
// TODO(runcom): too many arguments here, use a struct
-func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth) (*http.Response, error) {
+func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) {
req, err := http.NewRequest(method, url, stream)
if err != nil {
return nil, err
@@ -441,7 +440,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url
req.Header.Add("User-Agent", c.sys.DockerRegistryUserAgent)
}
if auth == v2Auth {
- if err := c.setupRequestAuth(req); err != nil {
+ if err := c.setupRequestAuth(req, extraScope); err != nil {
return nil, err
}
}
@@ -460,7 +459,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url
// 2) gcr.io is sending 401 without a WWW-Authenticate header in the real request
//
// debugging: https://github.com/containers/image/pull/211#issuecomment-273426236 and follows up
-func (c *dockerClient) setupRequestAuth(req *http.Request) error {
+func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope) error {
if len(c.challenges) == 0 {
return nil
}
@@ -474,10 +473,10 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error {
case "bearer":
cacheKey := ""
scopes := []authScope{c.scope}
- if c.extraScope != nil {
+ if extraScope != nil {
// Using ':' as a separator here is unambiguous because getBearerToken below uses the same separator when formatting a remote request (and because repository names can't contain colons).
- cacheKey = fmt.Sprintf("%s:%s", c.extraScope.remoteName, c.extraScope.actions)
- scopes = append(scopes, *c.extraScope)
+ cacheKey = fmt.Sprintf("%s:%s", extraScope.remoteName, extraScope.actions)
+ scopes = append(scopes, *extraScope)
}
var token bearerToken
t, inCache := c.tokenCache.Load(cacheKey)
@@ -564,7 +563,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {
ping := func(scheme string) error {
url := fmt.Sprintf(resolvedPingV2URL, scheme, c.registry)
- resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth)
+ resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil)
if err != nil {
logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err)
return err
@@ -591,7 +590,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {
// best effort to understand if we're talking to a V1 registry
pingV1 := func(scheme string) bool {
url := fmt.Sprintf(resolvedPingV1URL, scheme, c.registry)
- resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth)
+ resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil)
if err != nil {
logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err)
return false
@@ -625,7 +624,7 @@ func (c *dockerClient) detectProperties(ctx context.Context) error {
// using the original data structures.
func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) {
path := fmt.Sprintf(extensionsSignaturePath, reference.Path(ref.ref), manifestDigest)
- res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth)
+ res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go
index 2ab95f329..530c7513e 100644
--- a/vendor/github.com/containers/image/docker/docker_image.go
+++ b/vendor/github.com/containers/image/docker/docker_image.go
@@ -66,7 +66,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
tags := make([]string, 0)
for {
- res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth)
+ res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go
index 973d160d0..38500dd0e 100644
--- a/vendor/github.com/containers/image/docker/docker_image_dest.go
+++ b/vendor/github.com/containers/image/docker/docker_image_dest.go
@@ -12,6 +12,7 @@ import (
"net/url"
"os"
"path/filepath"
+ "strings"
"github.com/containers/image/docker/reference"
"github.com/containers/image/manifest"
@@ -113,7 +114,7 @@ func (c *sizeCounter) Write(p []byte) (n int, err error) {
// HasThreadSafePutBlob indicates whether PutBlob can be executed concurrently.
func (d *dockerImageDestination) HasThreadSafePutBlob() bool {
- return false
+ return true
}
// PutBlob writes contents of stream and returns data representing the result (with all data filled in).
@@ -140,7 +141,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader,
// FIXME? Chunked upload, progress reporting, etc.
uploadPath := fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref))
logrus.Debugf("Uploading %s", uploadPath)
- res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth)
+ res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth, nil)
if err != nil {
return types.BlobInfo{}, err
}
@@ -157,7 +158,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader,
digester := digest.Canonical.Digester()
sizeCounter := &sizeCounter{}
tee := io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter))
- res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth)
+ res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth, nil)
if err != nil {
logrus.Debugf("Error uploading layer chunked, response %#v", res)
return types.BlobInfo{}, err
@@ -176,7 +177,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader,
// TODO: check inputInfo.Digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717
locationQuery.Set("digest", computedDigest.String())
uploadLocation.RawQuery = locationQuery.Encode()
- res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth)
+ res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth, nil)
if err != nil {
return types.BlobInfo{}, err
}
@@ -194,10 +195,10 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader,
// blobExists returns true iff repo contains a blob with digest, and if so, also its size.
// If the destination does not contain the blob, or it is unknown, blobExists ordinarily returns (false, -1, nil);
// it returns a non-nil error only on an unexpected failure.
-func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest) (bool, int64, error) {
+func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest, extraScope *authScope) (bool, int64, error) {
checkPath := fmt.Sprintf(blobsPath, reference.Path(repo), digest.String())
logrus.Debugf("Checking %s", checkPath)
- res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth)
+ res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth, extraScope)
if err != nil {
return false, -1, err
}
@@ -218,7 +219,7 @@ func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.
}
// mountBlob tries to mount blob srcDigest from srcRepo to the current destination.
-func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo reference.Named, srcDigest digest.Digest) error {
+func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo reference.Named, srcDigest digest.Digest, extraScope *authScope) error {
u := url.URL{
Path: fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref)),
RawQuery: url.Values{
@@ -228,7 +229,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc
}
mountPath := u.String()
logrus.Debugf("Trying to mount %s", mountPath)
- res, err := d.c.makeRequest(ctx, "POST", mountPath, nil, nil, v2Auth)
+ res, err := d.c.makeRequest(ctx, "POST", mountPath, nil, nil, v2Auth, extraScope)
if err != nil {
return err
}
@@ -246,7 +247,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc
return errors.Wrap(err, "Error determining upload URL after a mount attempt")
}
logrus.Debugf("... started an upload instead of mounting, trying to cancel at %s", uploadLocation.String())
- res2, err := d.c.makeRequestToResolvedURL(ctx, "DELETE", uploadLocation.String(), nil, nil, -1, v2Auth)
+ res2, err := d.c.makeRequestToResolvedURL(ctx, "DELETE", uploadLocation.String(), nil, nil, -1, v2Auth, extraScope)
if err != nil {
logrus.Debugf("Error trying to cancel an inadvertent upload: %s", err)
} else {
@@ -276,7 +277,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types.
}
// First, check whether the blob happens to already exist at the destination.
- exists, size, err := d.blobExists(ctx, d.ref.ref, info.Digest)
+ exists, size, err := d.blobExists(ctx, d.ref.ref, info.Digest, nil)
if err != nil {
return false, types.BlobInfo{}, err
}
@@ -286,15 +287,6 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types.
}
// Then try reusing blobs from other locations.
-
- // Checking candidateRepo, and mounting from it, requires an expanded token scope.
- // We still want to reuse the ping information and other aspects of the client, so rather than make a fresh copy, there is this a bit ugly extraScope hack.
- if d.c.extraScope != nil {
- return false, types.BlobInfo{}, errors.New("Internal error: dockerClient.extraScope was set before TryReusingBlob")
- }
- defer func() {
- d.c.extraScope = nil
- }()
for _, candidate := range cache.CandidateLocations(d.ref.Transport(), bicTransportScope(d.ref), info.Digest, canSubstitute) {
candidateRepo, err := parseBICLocationReference(candidate.Location)
if err != nil {
@@ -314,7 +306,10 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types.
}
// Whatever happens here, don't abort the entire operation. It's likely we just don't have permissions, and if it is a critical network error, we will find out soon enough anyway.
- d.c.extraScope = &authScope{
+
+ // Checking candidateRepo, and mounting from it, requires an
+ // expanded token scope.
+ extraScope := &authScope{
remoteName: reference.Path(candidateRepo),
actions: "pull",
}
@@ -325,7 +320,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types.
// Even worse, docker/distribution does not actually reasonably implement canceling uploads
// (it would require a "delete" action in the token, and Quay does not give that to anyone, so we can't ask);
// so, be a nice client and don't create unnecesary upload sessions on the server.
- exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest)
+ exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest, extraScope)
if err != nil {
logrus.Debugf("... Failed: %v", err)
continue
@@ -335,7 +330,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types.
continue // logrus.Debug() already happened in blobExists
}
if candidateRepo.Name() != d.ref.ref.Name() {
- if err := d.mountBlob(ctx, candidateRepo, candidate.Digest); err != nil {
+ if err := d.mountBlob(ctx, candidateRepo, candidate.Digest, extraScope); err != nil {
logrus.Debugf("... Mount failed: %v", err)
continue
}
@@ -369,7 +364,7 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte) erro
if mimeType != "" {
headers["Content-Type"] = []string{mimeType}
}
- res, err := d.c.makeRequest(ctx, "PUT", path, headers, bytes.NewReader(m), v2Auth)
+ res, err := d.c.makeRequest(ctx, "PUT", path, headers, bytes.NewReader(m), v2Auth, nil)
if err != nil {
return err
}
@@ -396,14 +391,29 @@ func isManifestInvalidError(err error) bool {
if !ok || len(errors) == 0 {
return false
}
- ec, ok := errors[0].(errcode.ErrorCoder)
+ err = errors[0]
+ ec, ok := err.(errcode.ErrorCoder)
if !ok {
return false
}
+
+ switch ec.ErrorCode() {
// ErrorCodeManifestInvalid is returned by OpenShift with acceptschema2=false.
+ case v2.ErrorCodeManifestInvalid:
+ return true
// ErrorCodeTagInvalid is returned by docker/distribution (at least as of commit ec87e9b6971d831f0eff752ddb54fb64693e51cd)
// when uploading to a tag (because it can’t find a matching tag inside the manifest)
- return ec.ErrorCode() == v2.ErrorCodeManifestInvalid || ec.ErrorCode() == v2.ErrorCodeTagInvalid
+ case v2.ErrorCodeTagInvalid:
+ return true
+ // ErrorCodeUnsupported with 'Invalid JSON syntax' is returned by AWS ECR when
+ // uploading an OCI manifest that is (correctly, according to the spec) missing
+ // a top-level media type. See libpod issue #1719
+ // FIXME: remove this case when ECR behavior is fixed
+ case errcode.ErrorCodeUnsupported:
+ return strings.Contains(err.Error(), "Invalid JSON syntax")
+ default:
+ return false
+ }
}
func (d *dockerImageDestination) PutSignatures(ctx context.Context, signatures [][]byte) error {
@@ -574,7 +584,7 @@ sigExists:
}
path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), d.manifestDigest.String())
- res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth)
+ res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth, nil)
if err != nil {
return err
}
diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go
index c88ff2f34..8367792bf 100644
--- a/vendor/github.com/containers/image/docker/docker_image_src.go
+++ b/vendor/github.com/containers/image/docker/docker_image_src.go
@@ -89,7 +89,7 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin
path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest)
headers := make(map[string][]string)
headers["Accept"] = manifest.DefaultRequestedManifestMIMETypes
- res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth)
+ res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth, nil)
if err != nil {
return nil, "", err
}
@@ -137,7 +137,7 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string)
err error
)
for _, url := range urls {
- resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth)
+ resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil)
if err == nil {
if resp.StatusCode != http.StatusOK {
err = errors.Errorf("error fetching external blob from %q: %d (%s)", url, resp.StatusCode, http.StatusText(resp.StatusCode))
@@ -147,10 +147,10 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string)
break
}
}
- if resp.Body != nil && err == nil {
- return resp.Body, getBlobSize(resp), nil
+ if err != nil {
+ return nil, 0, err
}
- return nil, 0, err
+ return resp.Body, getBlobSize(resp), nil
}
func getBlobSize(resp *http.Response) int64 {
@@ -176,7 +176,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca
path := fmt.Sprintf(blobsPath, reference.Path(s.ref.ref), info.Digest.String())
logrus.Debugf("Downloading %s", path)
- res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth)
+ res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil)
if err != nil {
return nil, 0, err
}
@@ -340,7 +340,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
return err
}
getPath := fmt.Sprintf(manifestPath, reference.Path(ref.ref), refTail)
- get, err := c.makeRequest(ctx, "GET", getPath, headers, nil, v2Auth)
+ get, err := c.makeRequest(ctx, "GET", getPath, headers, nil, v2Auth, nil)
if err != nil {
return err
}
@@ -362,7 +362,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
// When retrieving the digest from a registry >= 2.3 use the following header:
// "Accept": "application/vnd.docker.distribution.manifest.v2+json"
- delete, err := c.makeRequest(ctx, "DELETE", deletePath, headers, nil, v2Auth)
+ delete, err := c.makeRequest(ctx, "DELETE", deletePath, headers, nil, v2Auth, nil)
if err != nil {
return err
}
diff --git a/vendor/github.com/containers/image/docker/tarfile/src.go b/vendor/github.com/containers/image/docker/tarfile/src.go
index 889e5f8e8..03735f8a4 100644
--- a/vendor/github.com/containers/image/docker/tarfile/src.go
+++ b/vendor/github.com/containers/image/docker/tarfile/src.go
@@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"path"
+ "sync"
"github.com/containers/image/internal/tmpdir"
"github.com/containers/image/manifest"
@@ -21,8 +22,10 @@ import (
// Source is a partial implementation of types.ImageSource for reading from tarPath.
type Source struct {
tarPath string
- removeTarPathOnClose bool // Remove temp file on close if true
+ removeTarPathOnClose bool // Remove temp file on close if true
+ cacheDataLock sync.Once // Atomic way to ensure that ensureCachedDataIsPresent is only invoked once
// The following data is only available after ensureCachedDataIsPresent() succeeds
+ cacheDataResult error // The return value of ensureCachedDataIsPresent, since it should be as safe to cache as the side effects
tarManifest *ManifestItem // nil if not available yet.
configBytes []byte
configDigest digest.Digest
@@ -199,43 +202,46 @@ func (s *Source) readTarComponent(path string) ([]byte, error) {
// ensureCachedDataIsPresent loads data necessary for any of the public accessors.
func (s *Source) ensureCachedDataIsPresent() error {
- if s.tarManifest != nil {
- return nil
- }
-
- // Read and parse manifest.json
- tarManifest, err := s.loadTarManifest()
- if err != nil {
- return err
- }
+ s.cacheDataLock.Do(func() {
+ // Read and parse manifest.json
+ tarManifest, err := s.loadTarManifest()
+ if err != nil {
+ s.cacheDataResult = err
+ return
+ }
- // Check to make sure length is 1
- if len(tarManifest) != 1 {
- return errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest))
- }
+ // Check to make sure length is 1
+ if len(tarManifest) != 1 {
+ s.cacheDataResult = errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest))
+ return
+ }
- // Read and parse config.
- configBytes, err := s.readTarComponent(tarManifest[0].Config)
- if err != nil {
- return err
- }
- var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs.
- if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
- return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config)
- }
+ // Read and parse config.
+ configBytes, err := s.readTarComponent(tarManifest[0].Config)
+ if err != nil {
+ s.cacheDataResult = err
+ return
+ }
+ var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs.
+ if err := json.Unmarshal(configBytes, &parsedConfig); err != nil {
+ s.cacheDataResult = errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config)
+ return
+ }
- knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig)
- if err != nil {
- return err
- }
+ knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig)
+ if err != nil {
+ s.cacheDataResult = err
+ return
+ }
- // Success; commit.
- s.tarManifest = &tarManifest[0]
- s.configBytes = configBytes
- s.configDigest = digest.FromBytes(configBytes)
- s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs
- s.knownLayers = knownLayers
- return nil
+ // Success; commit.
+ s.tarManifest = &tarManifest[0]
+ s.configBytes = configBytes
+ s.configDigest = digest.FromBytes(configBytes)
+ s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs
+ s.knownLayers = knownLayers
+ })
+ return s.cacheDataResult
}
// loadTarManifest loads and decodes the manifest.json.
@@ -399,7 +405,7 @@ func (r uncompressedReadCloser) Close() error {
// HasThreadSafeGetBlob indicates whether GetBlob can be executed concurrently.
func (s *Source) HasThreadSafeGetBlob() bool {
- return false
+ return true
}
// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown).
diff --git a/vendor/github.com/containers/image/ostree/ostree_src.go b/vendor/github.com/containers/image/ostree/ostree_src.go
index df432c9f3..35d852139 100644
--- a/vendor/github.com/containers/image/ostree/ostree_src.go
+++ b/vendor/github.com/containers/image/ostree/ostree_src.go
@@ -17,7 +17,7 @@ import (
"github.com/containers/image/types"
"github.com/containers/storage/pkg/ioutils"
"github.com/klauspost/pgzip"
- "github.com/opencontainers/go-digest"
+ digest "github.com/opencontainers/go-digest"
glib "github.com/ostreedev/ostree-go/pkg/glibobject"
"github.com/pkg/errors"
"github.com/vbatts/tar-split/tar/asm"
@@ -313,24 +313,19 @@ func (s *ostreeImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca
if err != nil {
return nil, 0, err
}
- defer mfz.Close()
metaUnpacker := storage.NewJSONUnpacker(mfz)
getter, err := newOSTreePathFileGetter(s.repo, branch)
if err != nil {
+ mfz.Close()
return nil, 0, err
}
ots := asm.NewOutputTarStream(getter, metaUnpacker)
- pipeReader, pipeWriter := io.Pipe()
- go func() {
- io.Copy(pipeWriter, ots)
- pipeWriter.Close()
- }()
-
- rc := ioutils.NewReadCloserWrapper(pipeReader, func() error {
+ rc := ioutils.NewReadCloserWrapper(ots, func() error {
getter.Close()
+ mfz.Close()
return ots.Close()
})
return rc, layerSize, nil
diff --git a/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go b/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
index 9e3e9cfe1..3d0bb0df2 100644
--- a/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
+++ b/vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
@@ -53,20 +53,23 @@ type Registry struct {
Prefix string `toml:"prefix"`
}
-// backwards compatability to sysregistries v1
-type v1TOMLregistries struct {
+// V1TOMLregistries is for backwards compatibility to sysregistries v1
+type V1TOMLregistries struct {
Registries []string `toml:"registries"`
}
+// V1TOMLConfig is for backwards compatibility to sysregistries v1
+type V1TOMLConfig struct {
+ Search V1TOMLregistries `toml:"search"`
+ Insecure V1TOMLregistries `toml:"insecure"`
+ Block V1TOMLregistries `toml:"block"`
+}
+
// tomlConfig is the data type used to unmarshal the toml config.
type tomlConfig struct {
Registries []Registry `toml:"registry"`
// backwards compatability to sysregistries v1
- V1Registries struct {
- Search v1TOMLregistries `toml:"search"`
- Insecure v1TOMLregistries `toml:"insecure"`
- Block v1TOMLregistries `toml:"block"`
- } `toml:"registries"`
+ V1TOMLConfig `toml:"registries"`
}
// InvalidRegistries represents an invalid registry configurations. An example
@@ -129,21 +132,21 @@ func getV1Registries(config *tomlConfig) ([]Registry, error) {
// Note: config.V1Registries.Search needs to be processed first to ensure registryOrder is populated in the right order
// if one of the search registries is also in one of the other lists.
- for _, search := range config.V1Registries.Search.Registries {
+ for _, search := range config.V1TOMLConfig.Search.Registries {
reg, err := getRegistry(search)
if err != nil {
return nil, err
}
reg.Search = true
}
- for _, blocked := range config.V1Registries.Block.Registries {
+ for _, blocked := range config.V1TOMLConfig.Block.Registries {
reg, err := getRegistry(blocked)
if err != nil {
return nil, err
}
reg.Blocked = true
}
- for _, insecure := range config.V1Registries.Insecure.Registries {
+ for _, insecure := range config.V1TOMLConfig.Insecure.Registries {
reg, err := getRegistry(insecure)
if err != nil {
return nil, err
diff --git a/vendor/github.com/containers/image/storage/storage_image.go b/vendor/github.com/containers/image/storage/storage_image.go
index b53fbdf6e..67dc6142b 100644
--- a/vendor/github.com/containers/image/storage/storage_image.go
+++ b/vendor/github.com/containers/image/storage/storage_image.go
@@ -14,6 +14,7 @@ import (
"sync"
"sync/atomic"
+ "github.com/containers/image/docker/reference"
"github.com/containers/image/image"
"github.com/containers/image/internal/tmpdir"
"github.com/containers/image/manifest"
@@ -70,6 +71,13 @@ type storageImageCloser struct {
size int64
}
+// manifestBigDataKey returns a key suitable for recording a manifest with the specified digest using storage.Store.ImageBigData and related functions.
+// If a specific manifest digest is explicitly requested by the user, the key retruned function should be used preferably;
+// for compatibility, if a manifest is not available under this key, check also storage.ImageDigestBigDataKey
+func manifestBigDataKey(digest digest.Digest) string {
+ return storage.ImageDigestManifestBigDataNamePrefix + "-" + digest.String()
+}
+
// newImageSource sets up an image for reading.
func newImageSource(imageRef storageReference) (*storageImageSource, error) {
// First, locate the image.
@@ -177,12 +185,29 @@ func (s *storageImageSource) GetManifest(ctx context.Context, instanceDigest *di
return nil, "", ErrNoManifestLists
}
if len(s.cachedManifest) == 0 {
- // We stored the manifest as an item named after storage.ImageDigestBigDataKey.
- cachedBlob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, storage.ImageDigestBigDataKey)
- if err != nil {
- return nil, "", err
+ // The manifest is stored as a big data item.
+ // Prefer the manifest corresponding to the user-specified digest, if available.
+ if s.imageRef.named != nil {
+ if digested, ok := s.imageRef.named.(reference.Digested); ok {
+ key := manifestBigDataKey(digested.Digest())
+ blob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, key)
+ if err != nil && !os.IsNotExist(err) { // os.IsNotExist is true if the image exists but there is no data corresponding to key
+ return nil, "", err
+ }
+ if err == nil {
+ s.cachedManifest = blob
+ }
+ }
+ }
+ // If the user did not specify a digest, or this is an old image stored before manifestBigDataKey was introduced, use the default manifest.
+ // Note that the manifest may not match the expected digest, and that is likely to fail eventually, e.g. in c/image/image/UnparsedImage.Manifest().
+ if len(s.cachedManifest) == 0 {
+ cachedBlob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, storage.ImageDigestBigDataKey)
+ if err != nil {
+ return nil, "", err
+ }
+ s.cachedManifest = cachedBlob
}
- s.cachedManifest = cachedBlob
}
return s.cachedManifest, manifest.GuessMIMEType(s.cachedManifest), err
}
@@ -660,6 +685,7 @@ func (s *storageImageDestination) Commit(ctx context.Context) error {
}
lastLayer = layer.ID
}
+
// If one of those blobs was a configuration blob, then we can try to dig out the date when the image
// was originally created, in case we're just copying it. If not, no harm done.
options := &storage.ImageOptions{}
@@ -667,9 +693,6 @@ func (s *storageImageDestination) Commit(ctx context.Context) error {
logrus.Debugf("setting image creation date to %s", inspect.Created)
options.CreationDate = *inspect.Created
}
- if manifestDigest, err := manifest.Digest(s.manifest); err == nil {
- options.Digest = manifestDigest
- }
// Create the image record, pointing to the most-recently added layer.
intendedID := s.imageRef.id
if intendedID == "" {
@@ -735,8 +758,20 @@ func (s *storageImageDestination) Commit(ctx context.Context) error {
}
logrus.Debugf("set names of image %q to %v", img.ID, names)
}
- // Save the manifest. Use storage.ImageDigestBigDataKey as the item's
- // name, so that its digest can be used to locate the image in the Store.
+ // Save the manifest. Allow looking it up by digest by using the key convention defined by the Store.
+ // Record the manifest twice: using a digest-specific key to allow references to that specific digest instance,
+ // and using storage.ImageDigestBigDataKey for future users that don’t specify any digest and for compatibility with older readers.
+ manifestDigest, err := manifest.Digest(s.manifest)
+ if err != nil {
+ return errors.Wrapf(err, "error computing manifest digest")
+ }
+ if err := s.imageRef.transport.store.SetImageBigData(img.ID, manifestBigDataKey(manifestDigest), s.manifest); err != nil {
+ if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil {
+ logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2)
+ }
+ logrus.Debugf("error saving manifest for image %q: %v", img.ID, err)
+ return err
+ }
if err := s.imageRef.transport.store.SetImageBigData(img.ID, storage.ImageDigestBigDataKey, s.manifest); err != nil {
if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil {
logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2)
@@ -788,9 +823,21 @@ func (s *storageImageDestination) SupportedManifestMIMETypes() []string {
}
// PutManifest writes the manifest to the destination.
-func (s *storageImageDestination) PutManifest(ctx context.Context, manifest []byte) error {
- s.manifest = make([]byte, len(manifest))
- copy(s.manifest, manifest)
+func (s *storageImageDestination) PutManifest(ctx context.Context, manifestBlob []byte) error {
+ if s.imageRef.named != nil {
+ if digested, ok := s.imageRef.named.(reference.Digested); ok {
+ matches, err := manifest.MatchesDigest(manifestBlob, digested.Digest())
+ if err != nil {
+ return err
+ }
+ if !matches {
+ return fmt.Errorf("Manifest does not match expected digest %s", digested.Digest())
+ }
+ }
+ }
+
+ s.manifest = make([]byte, len(manifestBlob))
+ copy(s.manifest, manifestBlob)
return nil
}
diff --git a/vendor/github.com/containers/image/storage/storage_reference.go b/vendor/github.com/containers/image/storage/storage_reference.go
index 73306b972..c046d9f22 100644
--- a/vendor/github.com/containers/image/storage/storage_reference.go
+++ b/vendor/github.com/containers/image/storage/storage_reference.go
@@ -55,7 +55,7 @@ func imageMatchesRepo(image *storage.Image, ref reference.Named) bool {
// one present with the same name or ID, and return the image.
func (s *storageReference) resolveImage() (*storage.Image, error) {
var loadedImage *storage.Image
- if s.id == "" {
+ if s.id == "" && s.named != nil {
// Look for an image that has the expanded reference name as an explicit Name value.
image, err := s.transport.store.Image(s.named.String())
if image != nil && err == nil {
@@ -69,7 +69,7 @@ func (s *storageReference) resolveImage() (*storage.Image, error) {
// though possibly with a different tag or digest, as a Name value, so
// that the canonical reference can be implicitly resolved to the image.
images, err := s.transport.store.ImagesByDigest(digested.Digest())
- if images != nil && err == nil {
+ if err == nil && len(images) > 0 {
for _, image := range images {
if imageMatchesRepo(image, s.named) {
loadedImage = image
@@ -97,6 +97,24 @@ func (s *storageReference) resolveImage() (*storage.Image, error) {
return nil, ErrNoSuchImage
}
}
+ // Default to having the image digest that we hand back match the most recently
+ // added manifest...
+ if digest, ok := loadedImage.BigDataDigests[storage.ImageDigestBigDataKey]; ok {
+ loadedImage.Digest = digest
+ }
+ // ... unless the named reference says otherwise, and it matches one of the digests
+ // in the image. For those cases, set the Digest field to that value, for the
+ // sake of older consumers that don't know there's a whole list in there now.
+ if s.named != nil {
+ if digested, ok := s.named.(reference.Digested); ok {
+ for _, digest := range loadedImage.Digests {
+ if digest == digested.Digest() {
+ loadedImage.Digest = digest
+ break
+ }
+ }
+ }
+ }
return loadedImage, nil
}
diff --git a/vendor/github.com/containers/image/storage/storage_transport.go b/vendor/github.com/containers/image/storage/storage_transport.go
index b53c389bd..02d2f5c08 100644
--- a/vendor/github.com/containers/image/storage/storage_transport.go
+++ b/vendor/github.com/containers/image/storage/storage_transport.go
@@ -284,11 +284,6 @@ func (s storageTransport) GetStoreImage(store storage.Store, ref types.ImageRefe
}
}
if sref, ok := ref.(*storageReference); ok {
- if sref.id != "" {
- if img, err := store.Image(sref.id); err == nil {
- return img, nil
- }
- }
tmpRef := *sref
if img, err := tmpRef.resolveImage(); err == nil {
return img, nil
diff --git a/vendor/github.com/containers/image/version/version.go b/vendor/github.com/containers/image/version/version.go
index 6644bcff3..10075992d 100644
--- a/vendor/github.com/containers/image/version/version.go
+++ b/vendor/github.com/containers/image/version/version.go
@@ -8,7 +8,7 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 1
// VersionPatch is for backwards-compatible bug fixes
- VersionPatch = 0
+ VersionPatch = 5
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = "-dev"
diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go
index d99842534..fa4a7c43b 100644
--- a/vendor/github.com/containers/storage/images.go
+++ b/vendor/github.com/containers/storage/images.go
@@ -5,8 +5,10 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"time"
+ "github.com/containers/image/manifest"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/truncindex"
@@ -15,9 +17,13 @@ import (
)
const (
- // ImageDigestBigDataKey is the name of the big data item whose
- // contents we consider useful for computing a "digest" of the
- // image, by which we can locate the image later.
+ // ImageDigestManifestBigDataNamePrefix is a prefix of big data item
+ // names which we consider to be manifests, used for computing a
+ // "digest" value for the image as a whole, by which we can locate the
+ // image later.
+ ImageDigestManifestBigDataNamePrefix = "manifest"
+ // ImageDigestBigDataKey is provided for compatibility with older
+ // versions of the image library. It will be removed in the future.
ImageDigestBigDataKey = "manifest"
)
@@ -27,12 +33,19 @@ type Image struct {
// value which was generated by the library.
ID string `json:"id"`
- // Digest is a digest value that we can use to locate the image.
+ // Digest is a digest value that we can use to locate the image, if one
+ // was specified at creation-time.
Digest digest.Digest `json:"digest,omitempty"`
+ // Digests is a list of digest values of the image's manifests, and
+ // possibly a manually-specified value, that we can use to locate the
+ // image. If Digest is set, its value is also in this list.
+ Digests []digest.Digest `json:"-"`
+
// Names is an optional set of user-defined convenience values. The
// image can be referred to by its ID or any of its names. Names are
- // unique among images.
+ // unique among images, and are often the text representation of tagged
+ // or canonical references.
Names []string `json:"names,omitempty"`
// TopLayer is the ID of the topmost layer of the image itself, if the
@@ -92,8 +105,10 @@ type ROImageStore interface {
// Images returns a slice enumerating the known images.
Images() ([]Image, error)
- // Images returns a slice enumerating the images which have a big data
- // item with the name ImageDigestBigDataKey and the specified digest.
+ // ByDigest returns a slice enumerating the images which have either an
+ // explicitly-set digest, or a big data item with a name that starts
+ // with ImageDigestManifestBigDataNamePrefix, which matches the
+ // specified digest.
ByDigest(d digest.Digest) ([]*Image, error)
}
@@ -111,7 +126,8 @@ type ImageStore interface {
Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error)
// SetNames replaces the list of names associated with an image with the
- // supplied values.
+ // supplied values. The values are expected to be valid normalized
+ // named image references.
SetNames(id string, names []string) error
// Delete removes the record of the image.
@@ -135,6 +151,7 @@ func copyImage(i *Image) *Image {
return &Image{
ID: i.ID,
Digest: i.Digest,
+ Digests: copyDigestSlice(i.Digests),
Names: copyStringSlice(i.Names),
TopLayer: i.TopLayer,
MappedTopLayers: copyStringSlice(i.MappedTopLayers),
@@ -147,6 +164,17 @@ func copyImage(i *Image) *Image {
}
}
+func copyImageSlice(slice []*Image) []*Image {
+ if len(slice) > 0 {
+ cp := make([]*Image, len(slice))
+ for i := range slice {
+ cp[i] = copyImage(slice[i])
+ }
+ return cp
+ }
+ return nil
+}
+
func (r *imageStore) Images() ([]Image, error) {
images := make([]Image, len(r.images))
for i := range r.images {
@@ -167,6 +195,46 @@ func (r *imageStore) datapath(id, key string) string {
return filepath.Join(r.datadir(id), makeBigDataBaseName(key))
}
+// bigDataNameIsManifest determines if a big data item with the specified name
+// is considered to be representative of the image, in that its digest can be
+// said to also be the image's digest. Currently, if its name is, or begins
+// with, "manifest", we say that it is.
+func bigDataNameIsManifest(name string) bool {
+ return strings.HasPrefix(name, ImageDigestManifestBigDataNamePrefix)
+}
+
+// recomputeDigests takes a fixed digest and a name-to-digest map and builds a
+// list of the unique values that would identify the image.
+func (image *Image) recomputeDigests() error {
+ validDigests := make([]digest.Digest, 0, len(image.BigDataDigests)+1)
+ digests := make(map[digest.Digest]struct{})
+ if image.Digest != "" {
+ if err := image.Digest.Validate(); err != nil {
+ return errors.Wrapf(err, "error validating image digest %q", string(image.Digest))
+ }
+ digests[image.Digest] = struct{}{}
+ validDigests = append(validDigests, image.Digest)
+ }
+ for name, digest := range image.BigDataDigests {
+ if !bigDataNameIsManifest(name) {
+ continue
+ }
+ if digest.Validate() != nil {
+ return errors.Wrapf(digest.Validate(), "error validating digest %q for big data item %q", string(digest), name)
+ }
+ // Deduplicate the digest values.
+ if _, known := digests[digest]; !known {
+ digests[digest] = struct{}{}
+ validDigests = append(validDigests, digest)
+ }
+ }
+ if image.Digest == "" && len(validDigests) > 0 {
+ image.Digest = validDigests[0]
+ }
+ image.Digests = validDigests
+ return nil
+}
+
func (r *imageStore) Load() error {
shouldSave := false
rpath := r.imagespath()
@@ -189,17 +257,18 @@ func (r *imageStore) Load() error {
r.removeName(conflict, name)
shouldSave = true
}
- names[name] = images[n]
}
- // Implicit digest
- if digest, ok := image.BigDataDigests[ImageDigestBigDataKey]; ok {
- digests[digest] = append(digests[digest], images[n])
+ // Compute the digest list.
+ err = image.recomputeDigests()
+ if err != nil {
+ return errors.Wrapf(err, "error computing digests for image with ID %q (%v)", image.ID, image.Names)
}
- // Explicit digest
- if image.Digest == "" {
- image.Digest = image.BigDataDigests[ImageDigestBigDataKey]
- } else if image.Digest != image.BigDataDigests[ImageDigestBigDataKey] {
- digests[image.Digest] = append(digests[image.Digest], images[n])
+ for _, name := range image.Names {
+ names[name] = image
+ }
+ for _, digest := range image.Digests {
+ list := digests[digest]
+ digests[digest] = append(list, image)
}
}
}
@@ -333,12 +402,12 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
}
}
if _, idInUse := r.byid[id]; idInUse {
- return nil, ErrDuplicateID
+ return nil, errors.Wrapf(ErrDuplicateID, "an image with ID %q already exists", id)
}
names = dedupeNames(names)
for _, name := range names {
- if _, nameInUse := r.byname[name]; nameInUse {
- return nil, ErrDuplicateName
+ if image, nameInUse := r.byname[name]; nameInUse {
+ return nil, errors.Wrapf(ErrDuplicateName, "image name %q is already associated with image %q", name, image.ID)
}
}
if created.IsZero() {
@@ -348,6 +417,7 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
image = &Image{
ID: id,
Digest: searchableDigest,
+ Digests: nil,
Names: names,
TopLayer: layer,
Metadata: metadata,
@@ -357,16 +427,20 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
Created: created,
Flags: make(map[string]interface{}),
}
+ err := image.recomputeDigests()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error validating digests for new image")
+ }
r.images = append(r.images, image)
r.idindex.Add(id)
r.byid[id] = image
- if searchableDigest != "" {
- list := r.bydigest[searchableDigest]
- r.bydigest[searchableDigest] = append(list, image)
- }
for _, name := range names {
r.byname[name] = image
}
+ for _, digest := range image.Digests {
+ list := r.bydigest[digest]
+ r.bydigest[digest] = append(list, image)
+ }
err = r.Save()
image = copyImage(image)
}
@@ -444,6 +518,14 @@ func (r *imageStore) Delete(id string) error {
for _, name := range image.Names {
delete(r.byname, name)
}
+ for _, digest := range image.Digests {
+ prunedList := imageSliceWithoutValue(r.bydigest[digest], image)
+ if len(prunedList) == 0 {
+ delete(r.bydigest, digest)
+ } else {
+ r.bydigest[digest] = prunedList
+ }
+ }
if toDeleteIndex != -1 {
// delete the image at toDeleteIndex
if toDeleteIndex == len(r.images)-1 {
@@ -452,28 +534,6 @@ func (r *imageStore) Delete(id string) error {
r.images = append(r.images[:toDeleteIndex], r.images[toDeleteIndex+1:]...)
}
}
- if digest, ok := image.BigDataDigests[ImageDigestBigDataKey]; ok {
- // remove the image from the digest-based index
- if list, ok := r.bydigest[digest]; ok {
- prunedList := imageSliceWithoutValue(list, image)
- if len(prunedList) == 0 {
- delete(r.bydigest, digest)
- } else {
- r.bydigest[digest] = prunedList
- }
- }
- }
- if image.Digest != "" {
- // remove the image's hard-coded digest from the digest-based index
- if list, ok := r.bydigest[image.Digest]; ok {
- prunedList := imageSliceWithoutValue(list, image)
- if len(prunedList) == 0 {
- delete(r.bydigest, image.Digest)
- } else {
- r.bydigest[image.Digest] = prunedList
- }
- }
- }
if err := r.Save(); err != nil {
return err
}
@@ -504,7 +564,7 @@ func (r *imageStore) Exists(id string) bool {
func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) {
if images, ok := r.bydigest[d]; ok {
- return images, nil
+ return copyImageSlice(images), nil
}
return nil, ErrImageUnknown
}
@@ -606,10 +666,19 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error {
if !ok {
return ErrImageUnknown
}
- if err := os.MkdirAll(r.datadir(image.ID), 0700); err != nil {
+ err := os.MkdirAll(r.datadir(image.ID), 0700)
+ if err != nil {
return err
}
- err := ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600)
+ var newDigest digest.Digest
+ if bigDataNameIsManifest(key) {
+ if newDigest, err = manifest.Digest(data); err != nil {
+ return errors.Wrapf(err, "error digesting manifest")
+ }
+ } else {
+ newDigest = digest.Canonical.FromBytes(data)
+ }
+ err = ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600)
if err == nil {
save := false
if image.BigDataSizes == nil {
@@ -621,7 +690,6 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error {
image.BigDataDigests = make(map[string]digest.Digest)
}
oldDigest, digestOk := image.BigDataDigests[key]
- newDigest := digest.Canonical.FromBytes(data)
image.BigDataDigests[key] = newDigest
if !sizeOk || oldSize != image.BigDataSizes[key] || !digestOk || oldDigest != newDigest {
save = true
@@ -637,20 +705,21 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error {
image.BigDataNames = append(image.BigDataNames, key)
save = true
}
- if key == ImageDigestBigDataKey {
- if oldDigest != "" && oldDigest != newDigest && oldDigest != image.Digest {
- // remove the image from the list of images in the digest-based
- // index which corresponds to the old digest for this item, unless
- // it's also the hard-coded digest
- if list, ok := r.bydigest[oldDigest]; ok {
- prunedList := imageSliceWithoutValue(list, image)
- if len(prunedList) == 0 {
- delete(r.bydigest, oldDigest)
- } else {
- r.bydigest[oldDigest] = prunedList
- }
+ for _, oldDigest := range image.Digests {
+ // remove the image from the list of images in the digest-based index
+ if list, ok := r.bydigest[oldDigest]; ok {
+ prunedList := imageSliceWithoutValue(list, image)
+ if len(prunedList) == 0 {
+ delete(r.bydigest, oldDigest)
+ } else {
+ r.bydigest[oldDigest] = prunedList
}
}
+ }
+ if err = image.recomputeDigests(); err != nil {
+ return errors.Wrapf(err, "error loading recomputing image digest information for %s", image.ID)
+ }
+ for _, newDigest := range image.Digests {
// add the image to the list of images in the digest-based index which
// corresponds to the new digest for this item, unless it's already there
list := r.bydigest[newDigest]
diff --git a/vendor/github.com/containers/storage/images_ffjson.go b/vendor/github.com/containers/storage/images_ffjson.go
index 539acfe93..6b40ebd59 100644
--- a/vendor/github.com/containers/storage/images_ffjson.go
+++ b/vendor/github.com/containers/storage/images_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: ./images.go
+// source: images.go
package storage
diff --git a/vendor/github.com/containers/storage/pkg/config/config.go b/vendor/github.com/containers/storage/pkg/config/config.go
new file mode 100644
index 000000000..bdb5fbcb8
--- /dev/null
+++ b/vendor/github.com/containers/storage/pkg/config/config.go
@@ -0,0 +1,96 @@
+package config
+
+// ThinpoolOptionsConfig represents the "storage.options.thinpool"
+// TOML config table.
+type ThinpoolOptionsConfig struct {
+ // AutoExtendPercent determines the amount by which pool needs to be
+ // grown. This is specified in terms of % of pool size. So a value of
+ // 20 means that when threshold is hit, pool will be grown by 20% of
+ // existing pool size.
+ AutoExtendPercent string `toml:"autoextend_percent"`
+
+ // AutoExtendThreshold determines the pool extension threshold in terms
+ // of percentage of pool size. For example, if threshold is 60, that
+ // means when pool is 60% full, threshold has been hit.
+ AutoExtendThreshold string `toml:"autoextend_threshold"`
+
+ // BaseSize specifies the size to use when creating the base device,
+ // which limits the size of images and containers.
+ BaseSize string `toml:"basesize"`
+
+ // BlockSize specifies a custom blocksize to use for the thin pool.
+ BlockSize string `toml:"blocksize"`
+
+ // DirectLvmDevice specifies a custom block storage device to use for
+ // the thin pool.
+ DirectLvmDevice string `toml:"directlvm_device"`
+
+ // DirectLvmDeviceForcewipes device even if device already has a
+ // filesystem
+ DirectLvmDeviceForce string `toml:"directlvm_device_force"`
+
+ // Fs specifies the filesystem type to use for the base device.
+ Fs string `toml:"fs"`
+
+ // log_level sets the log level of devicemapper.
+ LogLevel string `toml:"log_level"`
+
+ // MinFreeSpace specifies the min free space percent in a thin pool
+ // require for new device creation to
+ MinFreeSpace string `toml:"min_free_space"`
+
+ // MkfsArg specifies extra mkfs arguments to be used when creating the
+ // basedevice.
+ MkfsArg string `toml:"mkfsarg"`
+
+ // MountOpt specifies extra mount options used when mounting the thin
+ // devices.
+ MountOpt string `toml:"mountopt"`
+
+ // UseDeferredDeletion marks device for deferred deletion
+ UseDeferredDeletion string `toml:"use_deferred_deletion"`
+
+ // UseDeferredRemoval marks device for deferred removal
+ UseDeferredRemoval string `toml:"use_deferred_removal"`
+
+ // XfsNoSpaceMaxRetriesFreeSpace specifies the maximum number of
+ // retries XFS should attempt to complete IO when ENOSPC (no space)
+ // error is returned by underlying storage device.
+ XfsNoSpaceMaxRetries string `toml:"xfs_nospace_max_retries"`
+}
+
+// OptionsConfig represents the "storage.options" TOML config table.
+type OptionsConfig struct {
+ // AdditionalImagesStores is the location of additional read/only
+ // Image stores. Usually used to access Networked File System
+ // for shared image content
+ AdditionalImageStores []string `toml:"additionalimagestores"`
+
+ // Size
+ Size string `toml:"size"`
+
+ // RemapUIDs is a list of default UID mappings to use for layers.
+ RemapUIDs string `toml:"remap-uids"`
+ // RemapGIDs is a list of default GID mappings to use for layers.
+ RemapGIDs string `toml:"remap-gids"`
+
+ // RemapUser is the name of one or more entries in /etc/subuid which
+ // should be used to set up default UID mappings.
+ RemapUser string `toml:"remap-user"`
+ // RemapGroup is the name of one or more entries in /etc/subgid which
+ // should be used to set up default GID mappings.
+ RemapGroup string `toml:"remap-group"`
+ // Thinpool container options to be handed to thinpool drivers
+ Thinpool struct{ ThinpoolOptionsConfig } `toml:"thinpool"`
+ // OSTree repository
+ OstreeRepo string `toml:"ostree_repo"`
+
+ // Do not create a bind mount on the storage home
+ SkipMountHome string `toml:"skip_mount_home"`
+
+ // Alternative program to use for the mount of the file system
+ MountProgram string `toml:"mount_program"`
+
+ // MountOpt specifies extra mount options used when mounting
+ MountOpt string `toml:"mountopt"`
+}
diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go
index 3fe305cc1..856c73e51 100644
--- a/vendor/github.com/containers/storage/store.go
+++ b/vendor/github.com/containers/storage/store.go
@@ -18,6 +18,7 @@ import (
"github.com/BurntSushi/toml"
drivers "github.com/containers/storage/drivers"
"github.com/containers/storage/pkg/archive"
+ "github.com/containers/storage/pkg/config"
"github.com/containers/storage/pkg/directory"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils"
@@ -842,12 +843,16 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, -1, err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, -1, err
+ }
}
if id == "" {
id = stringid.GenerateRandomID()
@@ -870,7 +875,9 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
lstore.Lock()
defer lstore.Unlock()
if modified, err := lstore.Modified(); modified || err != nil {
- lstore.Load()
+ if err = lstore.Load(); err != nil {
+ return nil, -1, err
+ }
}
}
if l, err := lstore.Get(parent); err == nil && l != nil {
@@ -946,7 +953,9 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
ilayer, err = store.Get(layer)
if err == nil {
@@ -966,7 +975,9 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return nil, err
+ }
}
creationDate := time.Now().UTC()
@@ -1004,7 +1015,9 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, crea
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
}
// Walk the top layer list.
@@ -1125,14 +1138,18 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, err
+ }
}
var cimage *Image
for _, store := range append([]ROImageStore{istore}, istores...) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
cimage, err = store.Get(image)
if err == nil {
@@ -1162,7 +1179,9 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, err
+ }
}
if !options.HostUIDMapping && len(options.UIDMap) == 0 {
uidMap = s.uidMap
@@ -1222,7 +1241,9 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
options.IDMappingOptions = IDMappingOptions{
HostUIDMapping: len(options.UIDMap) == 0,
@@ -1254,17 +1275,23 @@ func (s *store) SetMetadata(id, metadata string) error {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return err
+ }
}
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err := ristore.Load(); err != nil {
+ return err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
if rlstore.Exists(id) {
@@ -1292,7 +1319,9 @@ func (s *store) Metadata(id string) (string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return "", err
+ }
}
if store.Exists(id) {
return store.Metadata(id)
@@ -1311,7 +1340,9 @@ func (s *store) Metadata(id string) (string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return "", err
+ }
}
if store.Exists(id) {
return store.Metadata(id)
@@ -1325,7 +1356,9 @@ func (s *store) Metadata(id string) (string, error) {
cstore.Lock()
defer cstore.Unlock()
if modified, err := cstore.Modified(); modified || err != nil {
- cstore.Load()
+ if err = cstore.Load(); err != nil {
+ return "", err
+ }
}
if cstore.Exists(id) {
return cstore.Metadata(id)
@@ -1346,7 +1379,9 @@ func (s *store) ListImageBigData(id string) ([]string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
bigDataNames, err := store.BigDataNames(id)
if err == nil {
@@ -1369,7 +1404,9 @@ func (s *store) ImageBigDataSize(id, key string) (int64, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return -1, err
+ }
}
size, err := store.BigDataSize(id, key)
if err == nil {
@@ -1393,7 +1430,9 @@ func (s *store) ImageBigDataDigest(id, key string) (digest.Digest, error) {
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return "", nil
+ }
}
d, err := ristore.BigDataDigest(id, key)
if err == nil && d.Validate() == nil {
@@ -1416,7 +1455,9 @@ func (s *store) ImageBigData(id, key string) ([]byte, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
data, err := store.BigData(id, key)
if err == nil {
@@ -1435,7 +1476,9 @@ func (s *store) SetImageBigData(id, key string, data []byte) error {
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return nil
+ }
}
return ristore.SetBigData(id, key, data)
@@ -1456,7 +1499,9 @@ func (s *store) ImageSize(id string) (int64, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return -1, err
+ }
}
}
@@ -1475,7 +1520,9 @@ func (s *store) ImageSize(id string) (int64, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return -1, err
+ }
}
if image, err = store.Get(id); err == nil {
imageStore = store
@@ -1560,7 +1607,9 @@ func (s *store) ContainerSize(id string) (int64, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return -1, err
+ }
}
}
@@ -1582,7 +1631,9 @@ func (s *store) ContainerSize(id string) (int64, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return -1, err
+ }
}
// Read the container record.
@@ -1644,7 +1695,9 @@ func (s *store) ListContainerBigData(id string) ([]string, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
return rcstore.BigDataNames(id)
@@ -1658,7 +1711,9 @@ func (s *store) ContainerBigDataSize(id, key string) (int64, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return -1, err
+ }
}
return rcstore.BigDataSize(id, key)
}
@@ -1671,7 +1726,9 @@ func (s *store) ContainerBigDataDigest(id, key string) (digest.Digest, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return "", err
+ }
}
return rcstore.BigDataDigest(id, key)
}
@@ -1684,7 +1741,9 @@ func (s *store) ContainerBigData(id, key string) ([]byte, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
return rcstore.BigData(id, key)
}
@@ -1697,7 +1756,9 @@ func (s *store) SetContainerBigData(id, key string, data []byte) error {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
return rcstore.SetBigData(id, key, data)
}
@@ -1715,7 +1776,9 @@ func (s *store) Exists(id string) bool {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return false
+ }
}
if store.Exists(id) {
return true
@@ -1734,7 +1797,9 @@ func (s *store) Exists(id string) bool {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return false
+ }
}
if store.Exists(id) {
return true
@@ -1748,7 +1813,9 @@ func (s *store) Exists(id string) bool {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return false
+ }
}
if rcstore.Exists(id) {
return true
@@ -1779,7 +1846,9 @@ func (s *store) SetNames(id string, names []string) error {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return err
+ }
}
if rlstore.Exists(id) {
return rlstore.SetNames(id, deduped)
@@ -1792,7 +1861,9 @@ func (s *store) SetNames(id string, names []string) error {
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return err
+ }
}
if ristore.Exists(id) {
return ristore.SetNames(id, deduped)
@@ -1805,7 +1876,9 @@ func (s *store) SetNames(id string, names []string) error {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
if rcstore.Exists(id) {
return rcstore.SetNames(id, deduped)
@@ -1826,7 +1899,9 @@ func (s *store) Names(id string) ([]string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
if l, err := store.Get(id); l != nil && err == nil {
return l.Names, nil
@@ -1845,7 +1920,9 @@ func (s *store) Names(id string) ([]string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
if i, err := store.Get(id); i != nil && err == nil {
return i.Names, nil
@@ -1859,7 +1936,9 @@ func (s *store) Names(id string) ([]string, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
if c, err := rcstore.Get(id); c != nil && err == nil {
return c.Names, nil
@@ -1880,7 +1959,9 @@ func (s *store) Lookup(name string) (string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return "", err
+ }
}
if l, err := store.Get(name); l != nil && err == nil {
return l.ID, nil
@@ -1899,7 +1980,9 @@ func (s *store) Lookup(name string) (string, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return "", err
+ }
}
if i, err := store.Get(name); i != nil && err == nil {
return i.ID, nil
@@ -1913,7 +1996,9 @@ func (s *store) Lookup(name string) (string, error) {
cstore.Lock()
defer cstore.Unlock()
if modified, err := cstore.Modified(); modified || err != nil {
- cstore.Load()
+ if err = cstore.Load(); err != nil {
+ return "", err
+ }
}
if c, err := cstore.Get(name); c != nil && err == nil {
return c.ID, nil
@@ -1939,17 +2024,23 @@ func (s *store) DeleteLayer(id string) error {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return err
+ }
}
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
if rlstore.Exists(id) {
@@ -2005,17 +2096,23 @@ func (s *store) DeleteImage(id string, commit bool) (layers []string, err error)
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, err
+ }
}
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return nil, err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
layersToRemove := []string{}
if ristore.Exists(id) {
@@ -2137,17 +2234,23 @@ func (s *store) DeleteContainer(id string) error {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return err
+ }
}
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
if rcstore.Exists(id) {
@@ -2192,17 +2295,23 @@ func (s *store) Delete(id string) error {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return err
+ }
}
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err := ristore.Load(); err != nil {
+ return err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
if rcstore.Exists(id) {
@@ -2254,17 +2363,23 @@ func (s *store) Wipe() error {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return err
+ }
}
ristore.Lock()
defer ristore.Unlock()
if modified, err := ristore.Modified(); modified || err != nil {
- ristore.Load()
+ if err = ristore.Load(); err != nil {
+ return err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return err
+ }
}
if err = rcstore.Wipe(); err != nil {
@@ -2306,7 +2421,9 @@ func (s *store) Mount(id, mountLabel string) (string, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return "", err
+ }
}
if rlstore.Exists(id) {
options := drivers.MountOpts{
@@ -2331,7 +2448,9 @@ func (s *store) Mounted(id string) (int, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return 0, err
+ }
}
return rlstore.Mounted(id)
@@ -2348,7 +2467,9 @@ func (s *store) Unmount(id string, force bool) (bool, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return false, err
+ }
}
if rlstore.Exists(id) {
return rlstore.Unmount(id, force)
@@ -2369,7 +2490,9 @@ func (s *store) Changes(from, to string) ([]archive.Change, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
if store.Exists(to) {
return store.Changes(from, to)
@@ -2391,7 +2514,9 @@ func (s *store) DiffSize(from, to string) (int64, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return -1, err
+ }
}
if store.Exists(to) {
return store.DiffSize(from, to)
@@ -2412,7 +2537,9 @@ func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, erro
for _, store := range append([]ROLayerStore{lstore}, lstores...) {
store.Lock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
if store.Exists(to) {
rc, err := store.Diff(from, to, options)
@@ -2440,7 +2567,9 @@ func (s *store) ApplyDiff(to string, diff io.Reader) (int64, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return -1, err
+ }
}
if rlstore.Exists(to) {
return rlstore.ApplyDiff(to, diff)
@@ -2463,7 +2592,9 @@ func (s *store) layersByMappedDigest(m func(ROLayerStore, digest.Digest) ([]Laye
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
storeLayers, err := m(store, d)
if err != nil {
@@ -2507,7 +2638,9 @@ func (s *store) LayerSize(id string) (int64, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return -1, err
+ }
}
if store.Exists(id) {
return store.Size(id)
@@ -2524,7 +2657,9 @@ func (s *store) LayerParentOwners(id string) ([]int, []int, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, nil, err
+ }
}
if rlstore.Exists(id) {
return rlstore.ParentOwners(id)
@@ -2544,12 +2679,16 @@ func (s *store) ContainerParentOwners(id string) ([]int, []int, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, nil, err
+ }
}
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, nil, err
+ }
}
container, err := rcstore.Get(id)
if err != nil {
@@ -2577,7 +2716,9 @@ func (s *store) Layers() ([]Layer, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
storeLayers, err := store.Layers()
if err != nil {
@@ -2603,7 +2744,9 @@ func (s *store) Images() ([]Image, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
storeImages, err := store.Images()
if err != nil {
@@ -2623,7 +2766,9 @@ func (s *store) Containers() ([]Container, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
return rcstore.Containers()
@@ -2642,7 +2787,9 @@ func (s *store) Layer(id string) (*Layer, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
layer, err := store.Get(id)
if err == nil {
@@ -2665,7 +2812,9 @@ func (s *store) Image(id string) (*Image, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
image, err := store.Get(id)
if err == nil {
@@ -2695,7 +2844,9 @@ func (s *store) ImagesByTopLayer(id string) ([]*Image, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
imageList, err := store.Images()
if err != nil {
@@ -2726,7 +2877,9 @@ func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) {
store.Lock()
defer store.Unlock()
if modified, err := store.Modified(); modified || err != nil {
- store.Load()
+ if err = store.Load(); err != nil {
+ return nil, err
+ }
}
imageList, err := store.ByDigest(d)
if err != nil && err != ErrImageUnknown {
@@ -2745,7 +2898,9 @@ func (s *store) Container(id string) (*Container, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
return rcstore.Get(id)
@@ -2759,7 +2914,9 @@ func (s *store) ContainerLayerID(id string) (string, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return "", err
+ }
}
container, err := rcstore.Get(id)
if err != nil {
@@ -2780,7 +2937,9 @@ func (s *store) ContainerByLayer(id string) (*Container, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return nil, err
+ }
}
containerList, err := rcstore.Containers()
if err != nil {
@@ -2803,7 +2962,9 @@ func (s *store) ContainerDirectory(id string) (string, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return "", err
+ }
}
id, err = rcstore.Lookup(id)
@@ -2828,7 +2989,9 @@ func (s *store) ContainerRunDirectory(id string) (string, error) {
rcstore.Lock()
defer rcstore.Unlock()
if modified, err := rcstore.Modified(); modified || err != nil {
- rcstore.Load()
+ if err = rcstore.Load(); err != nil {
+ return "", err
+ }
}
id, err = rcstore.Lookup(id)
@@ -2899,7 +3062,9 @@ func (s *store) Shutdown(force bool) ([]string, error) {
rlstore.Lock()
defer rlstore.Unlock()
if modified, err := rlstore.Modified(); modified || err != nil {
- rlstore.Load()
+ if err = rlstore.Load(); err != nil {
+ return nil, err
+ }
}
layers, err := rlstore.Layers()
@@ -2992,6 +3157,15 @@ func copyStringDigestMap(m map[string]digest.Digest) map[string]digest.Digest {
return ret
}
+func copyDigestSlice(slice []digest.Digest) []digest.Digest {
+ if len(slice) == 0 {
+ return nil
+ }
+ ret := make([]digest.Digest, len(slice))
+ copy(ret, slice)
+ return ret
+}
+
// copyStringInterfaceMap still forces us to assume that the interface{} is
// a non-pointer scalar value
func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} {
@@ -3005,108 +3179,13 @@ func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} {
// DefaultConfigFile path to the system wide storage.conf file
const DefaultConfigFile = "/etc/containers/storage.conf"
-// ThinpoolOptionsConfig represents the "storage.options.thinpool"
-// TOML config table.
-type ThinpoolOptionsConfig struct {
- // AutoExtendPercent determines the amount by which pool needs to be
- // grown. This is specified in terms of % of pool size. So a value of
- // 20 means that when threshold is hit, pool will be grown by 20% of
- // existing pool size.
- AutoExtendPercent string `toml:"autoextend_percent"`
-
- // AutoExtendThreshold determines the pool extension threshold in terms
- // of percentage of pool size. For example, if threshold is 60, that
- // means when pool is 60% full, threshold has been hit.
- AutoExtendThreshold string `toml:"autoextend_threshold"`
-
- // BaseSize specifies the size to use when creating the base device,
- // which limits the size of images and containers.
- BaseSize string `toml:"basesize"`
-
- // BlockSize specifies a custom blocksize to use for the thin pool.
- BlockSize string `toml:"blocksize"`
-
- // DirectLvmDevice specifies a custom block storage device to use for
- // the thin pool.
- DirectLvmDevice string `toml:"directlvm_device"`
-
- // DirectLvmDeviceForcewipes device even if device already has a
- // filesystem
- DirectLvmDeviceForce string `toml:"directlvm_device_force"`
-
- // Fs specifies the filesystem type to use for the base device.
- Fs string `toml:"fs"`
-
- // log_level sets the log level of devicemapper.
- LogLevel string `toml:"log_level"`
-
- // MinFreeSpace specifies the min free space percent in a thin pool
- // require for new device creation to
- MinFreeSpace string `toml:"min_free_space"`
-
- // MkfsArg specifies extra mkfs arguments to be used when creating the
- // basedevice.
- MkfsArg string `toml:"mkfsarg"`
-
- // MountOpt specifies extra mount options used when mounting the thin
- // devices.
- MountOpt string `toml:"mountopt"`
-
- // UseDeferredDeletion marks device for deferred deletion
- UseDeferredDeletion string `toml:"use_deferred_deletion"`
-
- // UseDeferredRemoval marks device for deferred removal
- UseDeferredRemoval string `toml:"use_deferred_removal"`
-
- // XfsNoSpaceMaxRetriesFreeSpace specifies the maximum number of
- // retries XFS should attempt to complete IO when ENOSPC (no space)
- // error is returned by underlying storage device.
- XfsNoSpaceMaxRetries string `toml:"xfs_nospace_max_retries"`
-}
-
-// OptionsConfig represents the "storage.options" TOML config table.
-type OptionsConfig struct {
- // AdditionalImagesStores is the location of additional read/only
- // Image stores. Usually used to access Networked File System
- // for shared image content
- AdditionalImageStores []string `toml:"additionalimagestores"`
-
- // Size
- Size string `toml:"size"`
-
- // RemapUIDs is a list of default UID mappings to use for layers.
- RemapUIDs string `toml:"remap-uids"`
- // RemapGIDs is a list of default GID mappings to use for layers.
- RemapGIDs string `toml:"remap-gids"`
-
- // RemapUser is the name of one or more entries in /etc/subuid which
- // should be used to set up default UID mappings.
- RemapUser string `toml:"remap-user"`
- // RemapGroup is the name of one or more entries in /etc/subgid which
- // should be used to set up default GID mappings.
- RemapGroup string `toml:"remap-group"`
- // Thinpool container options to be handed to thinpool drivers
- Thinpool struct{ ThinpoolOptionsConfig } `toml:"thinpool"`
- // OSTree repository
- OstreeRepo string `toml:"ostree_repo"`
-
- // Do not create a bind mount on the storage home
- SkipMountHome string `toml:"skip_mount_home"`
-
- // Alternative program to use for the mount of the file system
- MountProgram string `toml:"mount_program"`
-
- // MountOpt specifies extra mount options used when mounting
- MountOpt string `toml:"mountopt"`
-}
-
// TOML-friendly explicit tables used for conversions.
type tomlConfig struct {
Storage struct {
- Driver string `toml:"driver"`
- RunRoot string `toml:"runroot"`
- GraphRoot string `toml:"graphroot"`
- Options struct{ OptionsConfig } `toml:"options"`
+ Driver string `toml:"driver"`
+ RunRoot string `toml:"runroot"`
+ GraphRoot string `toml:"graphroot"`
+ Options struct{ config.OptionsConfig } `toml:"options"`
} `toml:"storage"`
}
diff --git a/vendor/github.com/containers/storage/vendor.conf b/vendor/github.com/containers/storage/vendor.conf
index 04af9010b..c143b049d 100644
--- a/vendor/github.com/containers/storage/vendor.conf
+++ b/vendor/github.com/containers/storage/vendor.conf
@@ -1,12 +1,18 @@
github.com/BurntSushi/toml master
github.com/Microsoft/go-winio 307e919c663683a9000576fdc855acaf9534c165
github.com/Microsoft/hcsshim a8d9cc56cbce765a7eebdf4792e6ceceeff3edb8
+github.com/containers/image master
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
+github.com/docker/libtrust master
+github.com/klauspost/compress v1.4.1
+github.com/klauspost/cpuid v1.2.0
+github.com/klauspost/pgzip v1.2.1
github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6
github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062
github.com/opencontainers/go-digest master
+github.com/opencontainers/image-spec master
github.com/opencontainers/runc 6c22e77604689db8725fa866f0f2ec0b3e8c3a07
github.com/opencontainers/selinux v1.1
github.com/ostreedev/ostree-go master
@@ -23,6 +29,3 @@ golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
gotest.tools master
github.com/google/go-cmp master
-github.com/klauspost/pgzip v1.2.1
-github.com/klauspost/compress v1.4.1
-github.com/klauspost/cpuid v1.2.0
diff --git a/vendor/github.com/urfave/cli/LICENSE b/vendor/github.com/urfave/cli/LICENSE
deleted file mode 100644
index 42a597e29..000000000
--- a/vendor/github.com/urfave/cli/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2016 Jeremy Saenz & Contributors
-
-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/urfave/cli/README.md b/vendor/github.com/urfave/cli/README.md
deleted file mode 100644
index f2baef4f0..000000000
--- a/vendor/github.com/urfave/cli/README.md
+++ /dev/null
@@ -1,1526 +0,0 @@
-cli
-===
-
-[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
-[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)
-[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
-[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli)
-[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli)
-[![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) /
-[![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc)
-
-This is the library formerly known as `github.com/codegangsta/cli` -- Github
-will automatically redirect requests to this repository, but we recommend
-updating your references for clarity.
-
-cli is a simple, fast, and fun package for building command line apps in Go. The
-goal is to enable developers to write fast and distributable command line
-applications in an expressive way.
-
-<!-- toc -->
-
-- [Overview](#overview)
-- [Installation](#installation)
- * [Supported platforms](#supported-platforms)
- * [Using the `v2` branch](#using-the-v2-branch)
- * [Pinning to the `v1` releases](#pinning-to-the-v1-releases)
-- [Getting Started](#getting-started)
-- [Examples](#examples)
- * [Arguments](#arguments)
- * [Flags](#flags)
- + [Placeholder Values](#placeholder-values)
- + [Alternate Names](#alternate-names)
- + [Ordering](#ordering)
- + [Values from the Environment](#values-from-the-environment)
- + [Values from files](#values-from-files)
- + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
- + [Precedence](#precedence)
- * [Subcommands](#subcommands)
- * [Subcommands categories](#subcommands-categories)
- * [Exit code](#exit-code)
- * [Bash Completion](#bash-completion)
- + [Enabling](#enabling)
- + [Distribution](#distribution)
- + [Customization](#customization)
- * [Generated Help Text](#generated-help-text)
- + [Customization](#customization-1)
- * [Version Flag](#version-flag)
- + [Customization](#customization-2)
- + [Full API Example](#full-api-example)
- * [Combining short Bool options](#combining-short-bool-options)
-- [Contribution Guidelines](#contribution-guidelines)
-
-<!-- tocstop -->
-
-## Overview
-
-Command line apps are usually so tiny that there is absolutely no reason why
-your code should *not* be self-documenting. Things like generating help text and
-parsing command flags/options should not hinder productivity when writing a
-command line app.
-
-**This is where cli comes into play.** cli makes command line programming fun,
-organized, and expressive!
-
-## Installation
-
-Make sure you have a working Go environment. Go version 1.2+ is supported. [See
-the install instructions for Go](http://golang.org/doc/install.html).
-
-To install cli, simply run:
-```
-$ go get github.com/urfave/cli
-```
-
-Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can
-be easily used:
-```
-export PATH=$PATH:$GOPATH/bin
-```
-
-### Supported platforms
-
-cli is tested against multiple versions of Go on Linux, and against the latest
-released version of Go on OS X and Windows. For full details, see
-[`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml).
-
-### Using the `v2` branch
-
-**Warning**: The `v2` branch is currently unreleased and considered unstable.
-
-There is currently a long-lived branch named `v2` that is intended to land as
-the new `master` branch once development there has settled down. The current
-`master` branch (mirrored as `v1`) is being manually merged into `v2` on
-an irregular human-based schedule, but generally if one wants to "upgrade" to
-`v2` *now* and accept the volatility (read: "awesomeness") that comes along with
-that, please use whatever version pinning of your preference, such as via
-`gopkg.in`:
-
-```
-$ go get gopkg.in/urfave/cli.v2
-```
-
-``` go
-...
-import (
- "gopkg.in/urfave/cli.v2" // imports as package "cli"
-)
-...
-```
-
-### Pinning to the `v1` releases
-
-Similarly to the section above describing use of the `v2` branch, if one wants
-to avoid any unexpected compatibility pains once `v2` becomes `master`, then
-pinning to `v1` is an acceptable option, e.g.:
-
-```
-$ go get gopkg.in/urfave/cli.v1
-```
-
-``` go
-...
-import (
- "gopkg.in/urfave/cli.v1" // imports as package "cli"
-)
-...
-```
-
-This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing).
-
-## Getting Started
-
-One of the philosophies behind cli is that an API should be playful and full of
-discovery. So a cli app can be as little as one line of code in `main()`.
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "A new cli application"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- err := cli.NewApp().Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-This app will run and show help text, but is not very useful. Let's give an
-action to execute and some help documentation:
-
-<!-- {
- "output": "boom! I say!"
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
- app.Name = "boom"
- app.Usage = "make an explosive entrance"
- app.Action = func(c *cli.Context) error {
- fmt.Println("boom! I say!")
- return nil
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Running this already gives you a ton of functionality, plus support for things
-like subcommands and flags, which are covered below.
-
-## Examples
-
-Being a programmer can be a lonely job. Thankfully by the power of automation
-that is not the case! Let's create a greeter app to fend off our demons of
-loneliness!
-
-Start by creating a directory named `greet`, and within it, add a file,
-`greet.go` with the following code in it:
-
-<!-- {
- "output": "Hello friend!"
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
- app.Name = "greet"
- app.Usage = "fight the loneliness!"
- app.Action = func(c *cli.Context) error {
- fmt.Println("Hello friend!")
- return nil
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Install our command to the `$GOPATH/bin` directory:
-
-```
-$ go install
-```
-
-Finally run our new command:
-
-```
-$ greet
-Hello friend!
-```
-
-cli also generates neat help text:
-
-```
-$ greet help
-NAME:
- greet - fight the loneliness!
-
-USAGE:
- greet [global options] command [command options] [arguments...]
-
-VERSION:
- 0.0.0
-
-COMMANDS:
- help, h Shows a list of commands or help for one command
-
-GLOBAL OPTIONS
- --version Shows version information
-```
-
-### Arguments
-
-You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
-
-<!-- {
- "output": "Hello \""
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Action = func(c *cli.Context) error {
- fmt.Printf("Hello %q", c.Args().Get(0))
- return nil
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Flags
-
-Setting and querying flags is simple.
-
-<!-- {
- "output": "Hello Nefertiti"
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang",
- Value: "english",
- Usage: "language for the greeting",
- },
- }
-
- app.Action = func(c *cli.Context) error {
- name := "Nefertiti"
- if c.NArg() > 0 {
- name = c.Args().Get(0)
- }
- if c.String("lang") == "spanish" {
- fmt.Println("Hola", name)
- } else {
- fmt.Println("Hello", name)
- }
- return nil
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-You can also set a destination variable for a flag, to which the content will be
-scanned.
-
-<!-- {
- "output": "Hello someone"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
- "fmt"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- var language string
-
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang",
- Value: "english",
- Usage: "language for the greeting",
- Destination: &language,
- },
- }
-
- app.Action = func(c *cli.Context) error {
- name := "someone"
- if c.NArg() > 0 {
- name = c.Args()[0]
- }
- if language == "spanish" {
- fmt.Println("Hola", name)
- } else {
- fmt.Println("Hello", name)
- }
- return nil
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-See full list of flags at http://godoc.org/github.com/urfave/cli
-
-#### Placeholder Values
-
-Sometimes it's useful to specify a flag's value within the usage string itself.
-Such placeholders are indicated with back quotes.
-
-For example this:
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "&#45;&#45;config FILE, &#45;c FILE"
-} -->
-```go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag{
- cli.StringFlag{
- Name: "config, c",
- Usage: "Load configuration from `FILE`",
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Will result in help output like:
-
-```
---config FILE, -c FILE Load configuration from FILE
-```
-
-Note that only the first placeholder is used. Subsequent back-quoted words will
-be left as-is.
-
-#### Alternate Names
-
-You can set alternate (or short) names for flags by providing a comma-delimited
-list for the `Name`. e.g.
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "language for the greeting",
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-That flag can then be set with `--lang spanish` or `-l spanish`. Note that
-giving two different forms of the same flag in the same command invocation is an
-error.
-
-#### Ordering
-
-Flags for the application and commands are shown in the order they are defined.
-However, it's possible to sort them from outside this library by using `FlagsByName`
-or `CommandsByName` with `sort`.
-
-For example this:
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
- "sort"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "Language for the greeting",
- },
- cli.StringFlag{
- Name: "config, c",
- Usage: "Load configuration from `FILE`",
- },
- }
-
- app.Commands = []cli.Command{
- {
- Name: "complete",
- Aliases: []string{"c"},
- Usage: "complete a task on the list",
- Action: func(c *cli.Context) error {
- return nil
- },
- },
- {
- Name: "add",
- Aliases: []string{"a"},
- Usage: "add a task to the list",
- Action: func(c *cli.Context) error {
- return nil
- },
- },
- }
-
- sort.Sort(cli.FlagsByName(app.Flags))
- sort.Sort(cli.CommandsByName(app.Commands))
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Will result in help output like:
-
-```
---config FILE, -c FILE Load configuration from FILE
---lang value, -l value Language for the greeting (default: "english")
-```
-
-#### Values from the Environment
-
-You can also have the default value set from the environment via `EnvVar`. e.g.
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "language for the greeting.*APP_LANG"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "language for the greeting",
- EnvVar: "APP_LANG",
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-The `EnvVar` may also be given as a comma-delimited "cascade", where the first
-environment variable that resolves is used as the default.
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "lang, l",
- Value: "english",
- Usage: "language for the greeting",
- EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-#### Values from files
-
-You can also have the default value set from file via `FilePath`. e.g.
-
-<!-- {
- "args": ["&#45;&#45;help"],
- "output": "password for the mysql database"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Flags = []cli.Flag {
- cli.StringFlag{
- Name: "password, p",
- Usage: "password for the mysql database",
- FilePath: "/etc/mysql/password",
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Note that default values set from file (e.g. `FilePath`) take precedence over
-default values set from the enviornment (e.g. `EnvVar`).
-
-#### Values from alternate input sources (YAML, TOML, and others)
-
-There is a separate package altsrc that adds support for getting flag values
-from other file input sources.
-
-Currently supported input source formats:
-* YAML
-* TOML
-
-In order to get values for a flag from an alternate input source the following
-code would be added to wrap an existing cli.Flag like below:
-
-``` go
- altsrc.NewIntFlag(cli.IntFlag{Name: "test"})
-```
-
-Initialization must also occur for these flags. Below is an example initializing
-getting data from a yaml file below.
-
-``` go
- command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
-```
-
-The code above will use the "load" string as a flag name to get the file name of
-a yaml file from the cli.Context. It will then use that file name to initialize
-the yaml input source for any flags that are defined on that command. As a note
-the "load" flag used would also have to be defined on the command flags in order
-for this code snipped to work.
-
-Currently only YAML and JSON files are supported but developers can add support
-for other input sources by implementing the altsrc.InputSourceContext for their
-given sources.
-
-Here is a more complete sample of a command using YAML support:
-
-<!-- {
- "args": ["test-cmd", "&#45;&#45;help"],
- "output": "&#45&#45;test value.*default: 0"
-} -->
-``` go
-package notmain
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
- "github.com/urfave/cli/altsrc"
-)
-
-func main() {
- app := cli.NewApp()
-
- flags := []cli.Flag{
- altsrc.NewIntFlag(cli.IntFlag{Name: "test"}),
- cli.StringFlag{Name: "load"},
- }
-
- app.Action = func(c *cli.Context) error {
- fmt.Println("yaml ist rad")
- return nil
- }
-
- app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
- app.Flags = flags
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-#### Precedence
-
-The precedence for flag value sources is as follows (highest to lowest):
-
-0. Command line flag value from user
-0. Environment variable (if specified)
-0. Configuration file (if specified)
-0. Default defined on the flag
-
-### Subcommands
-
-Subcommands can be defined for a more git-like command line app.
-
-<!-- {
- "args": ["template", "add"],
- "output": "new task template: .+"
-} -->
-```go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Commands = []cli.Command{
- {
- Name: "add",
- Aliases: []string{"a"},
- Usage: "add a task to the list",
- Action: func(c *cli.Context) error {
- fmt.Println("added task: ", c.Args().First())
- return nil
- },
- },
- {
- Name: "complete",
- Aliases: []string{"c"},
- Usage: "complete a task on the list",
- Action: func(c *cli.Context) error {
- fmt.Println("completed task: ", c.Args().First())
- return nil
- },
- },
- {
- Name: "template",
- Aliases: []string{"t"},
- Usage: "options for task templates",
- Subcommands: []cli.Command{
- {
- Name: "add",
- Usage: "add a new template",
- Action: func(c *cli.Context) error {
- fmt.Println("new task template: ", c.Args().First())
- return nil
- },
- },
- {
- Name: "remove",
- Usage: "remove an existing template",
- Action: func(c *cli.Context) error {
- fmt.Println("removed task template: ", c.Args().First())
- return nil
- },
- },
- },
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Subcommands categories
-
-For additional organization in apps that have many subcommands, you can
-associate a category for each command to group them together in the help
-output.
-
-E.g.
-
-```go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
-
- app.Commands = []cli.Command{
- {
- Name: "noop",
- },
- {
- Name: "add",
- Category: "Template actions",
- },
- {
- Name: "remove",
- Category: "Template actions",
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Will include:
-
-```
-COMMANDS:
- noop
-
- Template actions:
- add
- remove
-```
-
-### Exit code
-
-Calling `App.Run` will not automatically call `os.Exit`, which means that by
-default the exit code will "fall through" to being `0`. An explicit exit code
-may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
-`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
-
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- app := cli.NewApp()
- app.Flags = []cli.Flag{
- cli.BoolTFlag{
- Name: "ginger-crouton",
- Usage: "is it in the soup?",
- },
- }
- app.Action = func(ctx *cli.Context) error {
- if !ctx.Bool("ginger-crouton") {
- return cli.NewExitError("it is not in the soup", 86)
- }
- return nil
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Bash Completion
-
-You can enable completion commands by setting the `EnableBashCompletion`
-flag on the `App` object. By default, this setting will only auto-complete to
-show an app's subcommands, but you can write your own completion methods for
-the App or its subcommands.
-
-<!-- {
- "args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
- "output": "laundry"
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
-
- app := cli.NewApp()
- app.EnableBashCompletion = true
- app.Commands = []cli.Command{
- {
- Name: "complete",
- Aliases: []string{"c"},
- Usage: "complete a task on the list",
- Action: func(c *cli.Context) error {
- fmt.Println("completed task: ", c.Args().First())
- return nil
- },
- BashComplete: func(c *cli.Context) {
- // This will complete if no args are passed
- if c.NArg() > 0 {
- return
- }
- for _, t := range tasks {
- fmt.Println(t)
- }
- },
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-#### Enabling
-
-Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
-setting the `PROG` variable to the name of your program:
-
-`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
-
-#### Distribution
-
-Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
-it to the name of the program you wish to add autocomplete support for (or
-automatically install it there if you are distributing a package). Don't forget
-to source the file to make it active in the current shell.
-
-```
-sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
-source /etc/bash_completion.d/<myprogram>
-```
-
-Alternatively, you can just document that users should source the generic
-`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
-to the name of their program (as above).
-
-#### Customization
-
-The default bash completion flag (`--generate-bash-completion`) is defined as
-`cli.BashCompletionFlag`, and may be redefined if desired, e.g.:
-
-<!-- {
- "args": ["&#45;&#45;compgen"],
- "output": "wat\nhelp\nh"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- cli.BashCompletionFlag = cli.BoolFlag{
- Name: "compgen",
- Hidden: true,
- }
-
- app := cli.NewApp()
- app.EnableBashCompletion = true
- app.Commands = []cli.Command{
- {
- Name: "wat",
- },
- }
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Generated Help Text
-
-The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
-by the cli internals in order to print generated help text for the app, command,
-or subcommand, and break execution.
-
-#### Customization
-
-All of the help text generation may be customized, and at multiple levels. The
-templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
-`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
-is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
-e.g.:
-
-<!-- {
- "output": "Ha HA. I pwnd the help!!1"
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "io"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- // EXAMPLE: Append to an existing template
- cli.AppHelpTemplate = fmt.Sprintf(`%s
-
-WEBSITE: http://awesometown.example.com
-
-SUPPORT: support@awesometown.example.com
-
-`, cli.AppHelpTemplate)
-
- // EXAMPLE: Override a template
- cli.AppHelpTemplate = `NAME:
- {{.Name}} - {{.Usage}}
-USAGE:
- {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
- {{if len .Authors}}
-AUTHOR:
- {{range .Authors}}{{ . }}{{end}}
- {{end}}{{if .Commands}}
-COMMANDS:
-{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
-GLOBAL OPTIONS:
- {{range .VisibleFlags}}{{.}}
- {{end}}{{end}}{{if .Copyright }}
-COPYRIGHT:
- {{.Copyright}}
- {{end}}{{if .Version}}
-VERSION:
- {{.Version}}
- {{end}}
-`
-
- // EXAMPLE: Replace the `HelpPrinter` func
- cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
- fmt.Println("Ha HA. I pwnd the help!!1")
- }
-
- err := cli.NewApp().Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-The default flag may be customized to something other than `-h/--help` by
-setting `cli.HelpFlag`, e.g.:
-
-<!-- {
- "args": ["&#45;&#45halp"],
- "output": "haaaaalp.*HALP"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- cli.HelpFlag = cli.BoolFlag{
- Name: "halp, haaaaalp",
- Usage: "HALP",
- EnvVar: "SHOW_HALP,HALPPLZ",
- }
-
- err := cli.NewApp().Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Version Flag
-
-The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
-is checked by the cli internals in order to print the `App.Version` via
-`cli.VersionPrinter` and break execution.
-
-#### Customization
-
-The default flag may be customized to something other than `-v/--version` by
-setting `cli.VersionFlag`, e.g.:
-
-<!-- {
- "args": ["&#45;&#45print-version"],
- "output": "partay version 19\\.99\\.0"
-} -->
-``` go
-package main
-
-import (
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-func main() {
- cli.VersionFlag = cli.BoolFlag{
- Name: "print-version, V",
- Usage: "print only the version",
- }
-
- app := cli.NewApp()
- app.Name = "partay"
- app.Version = "19.99.0"
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
-
-<!-- {
- "args": ["&#45;&#45version"],
- "output": "version=19\\.99\\.0 revision=fafafaf"
-} -->
-``` go
-package main
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/urfave/cli"
-)
-
-var (
- Revision = "fafafaf"
-)
-
-func main() {
- cli.VersionPrinter = func(c *cli.Context) {
- fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
- }
-
- app := cli.NewApp()
- app.Name = "partay"
- app.Version = "19.99.0"
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
-```
-
-#### Full API Example
-
-**Notice**: This is a contrived (functioning) example meant strictly for API
-demonstration purposes. Use of one's imagination is encouraged.
-
-<!-- {
- "output": "made it!\nPhew!"
-} -->
-``` go
-package main
-
-import (
- "errors"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "time"
-
- "github.com/urfave/cli"
-)
-
-func init() {
- cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
- cli.CommandHelpTemplate += "\nYMMV\n"
- cli.SubcommandHelpTemplate += "\nor something\n"
-
- cli.HelpFlag = cli.BoolFlag{Name: "halp"}
- cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true}
- cli.VersionFlag = cli.BoolFlag{Name: "print-version, V"}
-
- cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
- fmt.Fprintf(w, "best of luck to you\n")
- }
- cli.VersionPrinter = func(c *cli.Context) {
- fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version)
- }
- cli.OsExiter = func(c int) {
- fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c)
- }
- cli.ErrWriter = ioutil.Discard
- cli.FlagStringer = func(fl cli.Flag) string {
- return fmt.Sprintf("\t\t%s", fl.GetName())
- }
-}
-
-type hexWriter struct{}
-
-func (w *hexWriter) Write(p []byte) (int, error) {
- for _, b := range p {
- fmt.Printf("%x", b)
- }
- fmt.Printf("\n")
-
- return len(p), nil
-}
-
-type genericType struct{
- s string
-}
-
-func (g *genericType) Set(value string) error {
- g.s = value
- return nil
-}
-
-func (g *genericType) String() string {
- return g.s
-}
-
-func main() {
- app := cli.NewApp()
- app.Name = "kənˈtrīv"
- app.Version = "19.99.0"
- app.Compiled = time.Now()
- app.Authors = []cli.Author{
- cli.Author{
- Name: "Example Human",
- Email: "human@example.com",
- },
- }
- app.Copyright = "(c) 1999 Serious Enterprise"
- app.HelpName = "contrive"
- app.Usage = "demonstrate available API"
- app.UsageText = "contrive - demonstrating the available API"
- app.ArgsUsage = "[args and such]"
- app.Commands = []cli.Command{
- cli.Command{
- Name: "doo",
- Aliases: []string{"do"},
- Category: "motion",
- Usage: "do the doo",
- UsageText: "doo - does the dooing",
- Description: "no really, there is a lot of dooing to be done",
- ArgsUsage: "[arrgh]",
- Flags: []cli.Flag{
- cli.BoolFlag{Name: "forever, forevvarr"},
- },
- Subcommands: cli.Commands{
- cli.Command{
- Name: "wop",
- Action: wopAction,
- },
- },
- SkipFlagParsing: false,
- HideHelp: false,
- Hidden: false,
- HelpName: "doo!",
- BashComplete: func(c *cli.Context) {
- fmt.Fprintf(c.App.Writer, "--better\n")
- },
- Before: func(c *cli.Context) error {
- fmt.Fprintf(c.App.Writer, "brace for impact\n")
- return nil
- },
- After: func(c *cli.Context) error {
- fmt.Fprintf(c.App.Writer, "did we lose anyone?\n")
- return nil
- },
- Action: func(c *cli.Context) error {
- c.Command.FullName()
- c.Command.HasName("wop")
- c.Command.Names()
- c.Command.VisibleFlags()
- fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n")
- if c.Bool("forever") {
- c.Command.Run(c)
- }
- return nil
- },
- OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
- fmt.Fprintf(c.App.Writer, "for shame\n")
- return err
- },
- },
- }
- app.Flags = []cli.Flag{
- cli.BoolFlag{Name: "fancy"},
- cli.BoolTFlag{Name: "fancier"},
- cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3},
- cli.Float64Flag{Name: "howmuch"},
- cli.GenericFlag{Name: "wat", Value: &genericType{}},
- cli.Int64Flag{Name: "longdistance"},
- cli.Int64SliceFlag{Name: "intervals"},
- cli.IntFlag{Name: "distance"},
- cli.IntSliceFlag{Name: "times"},
- cli.StringFlag{Name: "dance-move, d"},
- cli.StringSliceFlag{Name: "names, N"},
- cli.UintFlag{Name: "age"},
- cli.Uint64Flag{Name: "bigage"},
- }
- app.EnableBashCompletion = true
- app.HideHelp = false
- app.HideVersion = false
- app.BashComplete = func(c *cli.Context) {
- fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
- }
- app.Before = func(c *cli.Context) error {
- fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
- return nil
- }
- app.After = func(c *cli.Context) error {
- fmt.Fprintf(c.App.Writer, "Phew!\n")
- return nil
- }
- app.CommandNotFound = func(c *cli.Context, command string) {
- fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
- }
- app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error {
- if isSubcommand {
- return err
- }
-
- fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
- return nil
- }
- app.Action = func(c *cli.Context) error {
- cli.DefaultAppComplete(c)
- cli.HandleExitCoder(errors.New("not an exit coder, though"))
- cli.ShowAppHelp(c)
- cli.ShowCommandCompletions(c, "nope")
- cli.ShowCommandHelp(c, "also-nope")
- cli.ShowCompletions(c)
- cli.ShowSubcommandHelp(c)
- cli.ShowVersion(c)
-
- categories := c.App.Categories()
- categories.AddCommand("sounds", cli.Command{
- Name: "bloop",
- })
-
- for _, category := range c.App.Categories() {
- fmt.Fprintf(c.App.Writer, "%s\n", category.Name)
- fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands)
- fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
- }
-
- fmt.Printf("%#v\n", c.App.Command("doo"))
- if c.Bool("infinite") {
- c.App.Run([]string{"app", "doo", "wop"})
- }
-
- if c.Bool("forevar") {
- c.App.RunAsSubcommand(c)
- }
- c.App.Setup()
- fmt.Printf("%#v\n", c.App.VisibleCategories())
- fmt.Printf("%#v\n", c.App.VisibleCommands())
- fmt.Printf("%#v\n", c.App.VisibleFlags())
-
- fmt.Printf("%#v\n", c.Args().First())
- if len(c.Args()) > 0 {
- fmt.Printf("%#v\n", c.Args()[1])
- }
- fmt.Printf("%#v\n", c.Args().Present())
- fmt.Printf("%#v\n", c.Args().Tail())
-
- set := flag.NewFlagSet("contrive", 0)
- nc := cli.NewContext(c.App, set, c)
-
- fmt.Printf("%#v\n", nc.Args())
- fmt.Printf("%#v\n", nc.Bool("nope"))
- fmt.Printf("%#v\n", nc.BoolT("nerp"))
- fmt.Printf("%#v\n", nc.Duration("howlong"))
- fmt.Printf("%#v\n", nc.Float64("hay"))
- fmt.Printf("%#v\n", nc.Generic("bloop"))
- fmt.Printf("%#v\n", nc.Int64("bonk"))
- fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
- fmt.Printf("%#v\n", nc.Int("bips"))
- fmt.Printf("%#v\n", nc.IntSlice("blups"))
- fmt.Printf("%#v\n", nc.String("snurt"))
- fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
- fmt.Printf("%#v\n", nc.Uint("flub"))
- fmt.Printf("%#v\n", nc.Uint64("florb"))
- fmt.Printf("%#v\n", nc.GlobalBool("global-nope"))
- fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp"))
- fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong"))
- fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay"))
- fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop"))
- fmt.Printf("%#v\n", nc.GlobalInt("global-bips"))
- fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups"))
- fmt.Printf("%#v\n", nc.GlobalString("global-snurt"))
- fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles"))
-
- fmt.Printf("%#v\n", nc.FlagNames())
- fmt.Printf("%#v\n", nc.GlobalFlagNames())
- fmt.Printf("%#v\n", nc.GlobalIsSet("wat"))
- fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope"))
- fmt.Printf("%#v\n", nc.NArg())
- fmt.Printf("%#v\n", nc.NumFlags())
- fmt.Printf("%#v\n", nc.Parent())
-
- nc.Set("wat", "also-nope")
-
- ec := cli.NewExitError("ohwell", 86)
- fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
- fmt.Printf("made it!\n")
- return nil
- }
-
- if os.Getenv("HEXY") != "" {
- app.Writer = &hexWriter{}
- app.ErrWriter = &hexWriter{}
- }
-
- app.Metadata = map[string]interface{}{
- "layers": "many",
- "explicable": false,
- "whatever-values": 19.99,
- }
-
-
- // ignore error so we don't exit non-zero and break gfmrun README example tests
- _ = app.Run(os.Args)
-}
-
-func wopAction(c *cli.Context) error {
- fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
- return nil
-}
-```
-
-### Combining short Bool options
-
-Traditional use of boolean options using their shortnames look like this:
-```
-# cmd foobar -s -o
-```
-
-Suppose you want users to be able to combine your bool options with their shortname. This
-can be done using the **UseShortOptionHandling** bool in your commands. Suppose your program
-has a two bool flags such as *serve* and *option* with the short options of *-o* and
-*-s* respectively. With **UseShortOptionHandling** set to *true*, a user can use a syntax
-like:
-```
-# cmd foobar -so
-```
-
-If you enable the **UseShortOptionHandling*, then you must not use any flags that have a single
-leading *-* or this will result in failures. For example, **-option** can no longer be used. Flags
-with two leading dashes (such as **--options**) are still valid.
-
-## Contribution Guidelines
-
-See [./CONTRIBUTING.md](./CONTRIBUTING.md)
diff --git a/vendor/github.com/urfave/cli/app.go b/vendor/github.com/urfave/cli/app.go
deleted file mode 100644
index 9add067b0..000000000
--- a/vendor/github.com/urfave/cli/app.go
+++ /dev/null
@@ -1,508 +0,0 @@
-package cli
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "sort"
- "time"
-)
-
-var (
- changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
- appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
- runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
-
- contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
-
- errInvalidActionType = NewExitError("ERROR invalid Action type. "+
- fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
- fmt.Sprintf("See %s", appActionDeprecationURL), 2)
-)
-
-// App is the main structure of a cli application. It is recommended that
-// an app be created with the cli.NewApp() function
-type App struct {
- // The name of the program. Defaults to path.Base(os.Args[0])
- Name string
- // Full name of command for help, defaults to Name
- HelpName string
- // Description of the program.
- Usage string
- // Text to override the USAGE section of help
- UsageText string
- // Description of the program argument format.
- ArgsUsage string
- // Version of the program
- Version string
- // Description of the program
- Description string
- // List of commands to execute
- Commands []Command
- // List of flags to parse
- Flags []Flag
- // Boolean to enable bash completion commands
- EnableBashCompletion bool
- // Boolean to hide built-in help command
- HideHelp bool
- // Boolean to hide built-in version flag and the VERSION section of help
- HideVersion bool
- // Populate on app startup, only gettable through method Categories()
- categories CommandCategories
- // An action to execute when the bash-completion flag is set
- BashComplete BashCompleteFunc
- // An action to execute before any subcommands are run, but after the context is ready
- // If a non-nil error is returned, no subcommands are run
- Before BeforeFunc
- // An action to execute after any subcommands are run, but after the subcommand has finished
- // It is run even if Action() panics
- After AfterFunc
-
- // The action to execute when no subcommands are specified
- // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
- // *Note*: support for the deprecated `Action` signature will be removed in a future version
- Action interface{}
-
- // Execute this function if the proper command cannot be found
- CommandNotFound CommandNotFoundFunc
- // Execute this function if an usage error occurs
- OnUsageError OnUsageErrorFunc
- // Compilation date
- Compiled time.Time
- // List of all authors who contributed
- Authors []Author
- // Copyright of the binary if any
- Copyright string
- // Name of Author (Note: Use App.Authors, this is deprecated)
- Author string
- // Email of Author (Note: Use App.Authors, this is deprecated)
- Email string
- // Writer writer to write output to
- Writer io.Writer
- // ErrWriter writes error output
- ErrWriter io.Writer
- // Execute this function to handle ExitErrors. If not provided, HandleExitCoder is provided to
- // function as a default, so this is optional.
- ExitErrHandler ExitErrHandlerFunc
- // Other custom info
- Metadata map[string]interface{}
- // Carries a function which returns app specific info.
- ExtraInfo func() map[string]string
- // CustomAppHelpTemplate the text template for app help topic.
- // cli.go uses text/template to render templates. You can
- // render custom help text by setting this variable.
- CustomAppHelpTemplate string
-
- didSetup bool
-}
-
-// Tries to find out when this binary was compiled.
-// Returns the current time if it fails to find it.
-func compileTime() time.Time {
- info, err := os.Stat(os.Args[0])
- if err != nil {
- return time.Now()
- }
- return info.ModTime()
-}
-
-// NewApp creates a new cli Application with some reasonable defaults for Name,
-// Usage, Version and Action.
-func NewApp() *App {
- return &App{
- Name: filepath.Base(os.Args[0]),
- HelpName: filepath.Base(os.Args[0]),
- Usage: "A new cli application",
- UsageText: "",
- Version: "0.0.0",
- BashComplete: DefaultAppComplete,
- Action: helpCommand.Action,
- Compiled: compileTime(),
- Writer: os.Stdout,
- }
-}
-
-// Setup runs initialization code to ensure all data structures are ready for
-// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
-// will return early if setup has already happened.
-func (a *App) Setup() {
- if a.didSetup {
- return
- }
-
- a.didSetup = true
-
- if a.Author != "" || a.Email != "" {
- a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
- }
-
- newCmds := []Command{}
- for _, c := range a.Commands {
- if c.HelpName == "" {
- c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
- }
- newCmds = append(newCmds, c)
- }
- a.Commands = newCmds
-
- if a.Command(helpCommand.Name) == nil && !a.HideHelp {
- a.Commands = append(a.Commands, helpCommand)
- if (HelpFlag != BoolFlag{}) {
- a.appendFlag(HelpFlag)
- }
- }
-
- if !a.HideVersion {
- a.appendFlag(VersionFlag)
- }
-
- a.categories = CommandCategories{}
- for _, command := range a.Commands {
- a.categories = a.categories.AddCommand(command.Category, command)
- }
- sort.Sort(a.categories)
-
- if a.Metadata == nil {
- a.Metadata = make(map[string]interface{})
- }
-
- if a.Writer == nil {
- a.Writer = os.Stdout
- }
-}
-
-// Run is the entry point to the cli app. Parses the arguments slice and routes
-// to the proper flag/args combination
-func (a *App) Run(arguments []string) (err error) {
- a.Setup()
-
- // handle the completion flag separately from the flagset since
- // completion could be attempted after a flag, but before its value was put
- // on the command line. this causes the flagset to interpret the completion
- // flag name as the value of the flag before it which is undesirable
- // note that we can only do this because the shell autocomplete function
- // always appends the completion flag at the end of the command
- shellComplete, arguments := checkShellCompleteFlag(a, arguments)
-
- // parse flags
- set, err := flagSet(a.Name, a.Flags)
- if err != nil {
- return err
- }
-
- set.SetOutput(ioutil.Discard)
- err = set.Parse(arguments[1:])
- nerr := normalizeFlags(a.Flags, set)
- context := NewContext(a, set, nil)
- if nerr != nil {
- fmt.Fprintln(a.Writer, nerr)
- ShowAppHelp(context)
- return nerr
- }
- context.shellComplete = shellComplete
-
- if checkCompletions(context) {
- return nil
- }
-
- if err != nil {
- if a.OnUsageError != nil {
- err := a.OnUsageError(context, err, false)
- a.handleExitCoder(context, err)
- return err
- }
- fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
- ShowAppHelp(context)
- return err
- }
-
- if !a.HideHelp && checkHelp(context) {
- ShowAppHelp(context)
- return nil
- }
-
- if !a.HideVersion && checkVersion(context) {
- ShowVersion(context)
- return nil
- }
-
- if a.After != nil {
- defer func() {
- if afterErr := a.After(context); afterErr != nil {
- if err != nil {
- err = NewMultiError(err, afterErr)
- } else {
- err = afterErr
- }
- }
- }()
- }
-
- if a.Before != nil {
- beforeErr := a.Before(context)
- if beforeErr != nil {
- fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
- ShowAppHelp(context)
- a.handleExitCoder(context, beforeErr)
- err = beforeErr
- return err
- }
- }
-
- args := context.Args()
- if args.Present() {
- name := args.First()
- c := a.Command(name)
- if c != nil {
- return c.Run(context)
- }
- }
-
- if a.Action == nil {
- a.Action = helpCommand.Action
- }
-
- // Run default Action
- err = HandleAction(a.Action, context)
-
- a.handleExitCoder(context, err)
- return err
-}
-
-// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
-//
-// Deprecated: instead you should return an error that fulfills cli.ExitCoder
-// to cli.App.Run. This will cause the application to exit with the given eror
-// code in the cli.ExitCoder
-func (a *App) RunAndExitOnError() {
- if err := a.Run(os.Args); err != nil {
- fmt.Fprintln(a.errWriter(), err)
- OsExiter(1)
- }
-}
-
-// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
-// generate command-specific flags
-func (a *App) RunAsSubcommand(ctx *Context) (err error) {
- // append help to commands
- if len(a.Commands) > 0 {
- if a.Command(helpCommand.Name) == nil && !a.HideHelp {
- a.Commands = append(a.Commands, helpCommand)
- if (HelpFlag != BoolFlag{}) {
- a.appendFlag(HelpFlag)
- }
- }
- }
-
- newCmds := []Command{}
- for _, c := range a.Commands {
- if c.HelpName == "" {
- c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
- }
- newCmds = append(newCmds, c)
- }
- a.Commands = newCmds
-
- // parse flags
- set, err := flagSet(a.Name, a.Flags)
- if err != nil {
- return err
- }
-
- set.SetOutput(ioutil.Discard)
- err = set.Parse(ctx.Args().Tail())
- nerr := normalizeFlags(a.Flags, set)
- context := NewContext(a, set, ctx)
-
- if nerr != nil {
- fmt.Fprintln(a.Writer, nerr)
- fmt.Fprintln(a.Writer)
- if len(a.Commands) > 0 {
- ShowSubcommandHelp(context)
- } else {
- ShowCommandHelp(ctx, context.Args().First())
- }
- return nerr
- }
-
- if checkCompletions(context) {
- return nil
- }
-
- if err != nil {
- if a.OnUsageError != nil {
- err = a.OnUsageError(context, err, true)
- a.handleExitCoder(context, err)
- return err
- }
- fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
- ShowSubcommandHelp(context)
- return err
- }
-
- if len(a.Commands) > 0 {
- if checkSubcommandHelp(context) {
- return nil
- }
- } else {
- if checkCommandHelp(ctx, context.Args().First()) {
- return nil
- }
- }
-
- if a.After != nil {
- defer func() {
- afterErr := a.After(context)
- if afterErr != nil {
- a.handleExitCoder(context, err)
- if err != nil {
- err = NewMultiError(err, afterErr)
- } else {
- err = afterErr
- }
- }
- }()
- }
-
- if a.Before != nil {
- beforeErr := a.Before(context)
- if beforeErr != nil {
- a.handleExitCoder(context, beforeErr)
- err = beforeErr
- return err
- }
- }
-
- args := context.Args()
- if args.Present() {
- name := args.First()
- c := a.Command(name)
- if c != nil {
- return c.Run(context)
- }
- }
-
- // Run default Action
- err = HandleAction(a.Action, context)
-
- a.handleExitCoder(context, err)
- return err
-}
-
-// Command returns the named command on App. Returns nil if the command does not exist
-func (a *App) Command(name string) *Command {
- for _, c := range a.Commands {
- if c.HasName(name) {
- return &c
- }
- }
-
- return nil
-}
-
-// Categories returns a slice containing all the categories with the commands they contain
-func (a *App) Categories() CommandCategories {
- return a.categories
-}
-
-// VisibleCategories returns a slice of categories and commands that are
-// Hidden=false
-func (a *App) VisibleCategories() []*CommandCategory {
- ret := []*CommandCategory{}
- for _, category := range a.categories {
- if visible := func() *CommandCategory {
- for _, command := range category.Commands {
- if !command.Hidden {
- return category
- }
- }
- return nil
- }(); visible != nil {
- ret = append(ret, visible)
- }
- }
- return ret
-}
-
-// VisibleCommands returns a slice of the Commands with Hidden=false
-func (a *App) VisibleCommands() []Command {
- ret := []Command{}
- for _, command := range a.Commands {
- if !command.Hidden {
- ret = append(ret, command)
- }
- }
- return ret
-}
-
-// VisibleFlags returns a slice of the Flags with Hidden=false
-func (a *App) VisibleFlags() []Flag {
- return visibleFlags(a.Flags)
-}
-
-func (a *App) hasFlag(flag Flag) bool {
- for _, f := range a.Flags {
- if flag == f {
- return true
- }
- }
-
- return false
-}
-
-func (a *App) errWriter() io.Writer {
- // When the app ErrWriter is nil use the package level one.
- if a.ErrWriter == nil {
- return ErrWriter
- }
-
- return a.ErrWriter
-}
-
-func (a *App) appendFlag(flag Flag) {
- if !a.hasFlag(flag) {
- a.Flags = append(a.Flags, flag)
- }
-}
-
-func (a *App) handleExitCoder(context *Context, err error) {
- if a.ExitErrHandler != nil {
- a.ExitErrHandler(context, err)
- } else {
- HandleExitCoder(err)
- }
-}
-
-// Author represents someone who has contributed to a cli project.
-type Author struct {
- Name string // The Authors name
- Email string // The Authors email
-}
-
-// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
-func (a Author) String() string {
- e := ""
- if a.Email != "" {
- e = " <" + a.Email + ">"
- }
-
- return fmt.Sprintf("%v%v", a.Name, e)
-}
-
-// HandleAction attempts to figure out which Action signature was used. If
-// it's an ActionFunc or a func with the legacy signature for Action, the func
-// is run!
-func HandleAction(action interface{}, context *Context) (err error) {
- if a, ok := action.(ActionFunc); ok {
- return a(context)
- } else if a, ok := action.(func(*Context) error); ok {
- return a(context)
- } else if a, ok := action.(func(*Context)); ok { // deprecated function signature
- a(context)
- return nil
- }
-
- return errInvalidActionType
-}
diff --git a/vendor/github.com/urfave/cli/category.go b/vendor/github.com/urfave/cli/category.go
deleted file mode 100644
index bf3c73c55..000000000
--- a/vendor/github.com/urfave/cli/category.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package cli
-
-// CommandCategories is a slice of *CommandCategory.
-type CommandCategories []*CommandCategory
-
-// CommandCategory is a category containing commands.
-type CommandCategory struct {
- Name string
- Commands Commands
-}
-
-func (c CommandCategories) Less(i, j int) bool {
- return lexicographicLess(c[i].Name, c[j].Name)
-}
-
-func (c CommandCategories) Len() int {
- return len(c)
-}
-
-func (c CommandCategories) Swap(i, j int) {
- c[i], c[j] = c[j], c[i]
-}
-
-// AddCommand adds a command to a category.
-func (c CommandCategories) AddCommand(category string, command Command) CommandCategories {
- for _, commandCategory := range c {
- if commandCategory.Name == category {
- commandCategory.Commands = append(commandCategory.Commands, command)
- return c
- }
- }
- return append(c, &CommandCategory{Name: category, Commands: []Command{command}})
-}
-
-// VisibleCommands returns a slice of the Commands with Hidden=false
-func (c *CommandCategory) VisibleCommands() []Command {
- ret := []Command{}
- for _, command := range c.Commands {
- if !command.Hidden {
- ret = append(ret, command)
- }
- }
- return ret
-}
diff --git a/vendor/github.com/urfave/cli/cli.go b/vendor/github.com/urfave/cli/cli.go
deleted file mode 100644
index 90c07eb8e..000000000
--- a/vendor/github.com/urfave/cli/cli.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Package cli provides a minimal framework for creating and organizing command line
-// Go applications. cli is designed to be easy to understand and write, the most simple
-// cli application can be written as follows:
-// func main() {
-// cli.NewApp().Run(os.Args)
-// }
-//
-// Of course this application does not do much, so let's make this an actual application:
-// func main() {
-// app := cli.NewApp()
-// app.Name = "greet"
-// app.Usage = "say a greeting"
-// app.Action = func(c *cli.Context) error {
-// println("Greetings")
-// return nil
-// }
-//
-// app.Run(os.Args)
-// }
-package cli
-
-//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
diff --git a/vendor/github.com/urfave/cli/command.go b/vendor/github.com/urfave/cli/command.go
deleted file mode 100644
index 3d44404f1..000000000
--- a/vendor/github.com/urfave/cli/command.go
+++ /dev/null
@@ -1,383 +0,0 @@
-package cli
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "sort"
- "strings"
-)
-
-// Command is a subcommand for a cli.App.
-type Command struct {
- // The name of the command
- Name string
- // short name of the command. Typically one character (deprecated, use `Aliases`)
- ShortName string
- // A list of aliases for the command
- Aliases []string
- // A short description of the usage of this command
- Usage string
- // Custom text to show on USAGE section of help
- UsageText string
- // A longer explanation of how the command works
- Description string
- // A short description of the arguments of this command
- ArgsUsage string
- // The category the command is part of
- Category string
- // The function to call when checking for bash command completions
- BashComplete BashCompleteFunc
- // An action to execute before any sub-subcommands are run, but after the context is ready
- // If a non-nil error is returned, no sub-subcommands are run
- Before BeforeFunc
- // An action to execute after any subcommands are run, but after the subcommand has finished
- // It is run even if Action() panics
- After AfterFunc
- // The function to call when this command is invoked
- Action interface{}
- // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
- // of deprecation period has passed, maybe?
-
- // Execute this function if a usage error occurs.
- OnUsageError OnUsageErrorFunc
- // List of child commands
- Subcommands Commands
- // List of flags to parse
- Flags []Flag
- // Treat all flags as normal arguments if true
- SkipFlagParsing bool
- // Skip argument reordering which attempts to move flags before arguments,
- // but only works if all flags appear after all arguments. This behavior was
- // removed n version 2 since it only works under specific conditions so we
- // backport here by exposing it as an option for compatibility.
- SkipArgReorder bool
- // Boolean to hide built-in help command
- HideHelp bool
- // Boolean to hide this command from help or completion
- Hidden bool
- // Boolean to enable short-option handling so user can combine several
- // single-character bool arguments into one
- // i.e. foobar -o -v -> foobar -ov
- UseShortOptionHandling bool
-
- // Full name of command for help, defaults to full command name, including parent commands.
- HelpName string
- commandNamePath []string
-
- // CustomHelpTemplate the text template for the command help topic.
- // cli.go uses text/template to render templates. You can
- // render custom help text by setting this variable.
- CustomHelpTemplate string
-}
-
-type CommandsByName []Command
-
-func (c CommandsByName) Len() int {
- return len(c)
-}
-
-func (c CommandsByName) Less(i, j int) bool {
- return lexicographicLess(c[i].Name, c[j].Name)
-}
-
-func (c CommandsByName) Swap(i, j int) {
- c[i], c[j] = c[j], c[i]
-}
-
-// FullName returns the full name of the command.
-// For subcommands this ensures that parent commands are part of the command path
-func (c Command) FullName() string {
- if c.commandNamePath == nil {
- return c.Name
- }
- return strings.Join(c.commandNamePath, " ")
-}
-
-// Commands is a slice of Command
-type Commands []Command
-
-// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
-func (c Command) Run(ctx *Context) (err error) {
- if len(c.Subcommands) > 0 {
- return c.startApp(ctx)
- }
-
- if !c.HideHelp && (HelpFlag != BoolFlag{}) {
- // append help to flags
- c.Flags = append(
- c.Flags,
- HelpFlag,
- )
- }
-
- set, err := c.parseFlags(ctx.Args().Tail())
-
- context := NewContext(ctx.App, set, ctx)
- context.Command = c
- if checkCommandCompletions(context, c.Name) {
- return nil
- }
-
- if err != nil {
- if c.OnUsageError != nil {
- err := c.OnUsageError(context, err, false)
- context.App.handleExitCoder(context, err)
- return err
- }
- fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
- fmt.Fprintln(context.App.Writer)
- ShowCommandHelp(context, c.Name)
- return err
- }
-
- if checkCommandHelp(context, c.Name) {
- return nil
- }
-
- if c.After != nil {
- defer func() {
- afterErr := c.After(context)
- if afterErr != nil {
- context.App.handleExitCoder(context, err)
- if err != nil {
- err = NewMultiError(err, afterErr)
- } else {
- err = afterErr
- }
- }
- }()
- }
-
- if c.Before != nil {
- err = c.Before(context)
- if err != nil {
- ShowCommandHelp(context, c.Name)
- context.App.handleExitCoder(context, err)
- return err
- }
- }
-
- if c.Action == nil {
- c.Action = helpSubcommand.Action
- }
-
- err = HandleAction(c.Action, context)
-
- if err != nil {
- context.App.handleExitCoder(context, err)
- }
- return err
-}
-
-func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
- set, err := flagSet(c.Name, c.Flags)
- if err != nil {
- return nil, err
- }
- set.SetOutput(ioutil.Discard)
-
- if c.SkipFlagParsing {
- return set, set.Parse(append([]string{"--"}, args...))
- }
-
- if !c.SkipArgReorder {
- args = reorderArgs(args)
- }
-
-PARSE:
- err = set.Parse(args)
- if err != nil {
- if c.UseShortOptionHandling {
- // To enable short-option handling (e.g., "-it" vs "-i -t")
- // we have to iteratively catch parsing errors. This way
- // we achieve LR parsing without transforming any arguments.
- // Otherwise, there is no way we can discriminate combined
- // short options from common arguments that should be left
- // untouched.
- errStr := err.Error()
- trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
- if errStr == trimmed {
- return nil, err
- }
- // regenerate the initial args with the split short opts
- newArgs := Args{}
- for i, arg := range args {
- if arg != trimmed {
- newArgs = append(newArgs, arg)
- continue
- }
- shortOpts := translateShortOptions(set, Args{trimmed})
- if len(shortOpts) == 1 {
- return nil, err
- }
- // add each short option and all remaining arguments
- newArgs = append(newArgs, shortOpts...)
- newArgs = append(newArgs, args[i+1:]...)
- args = newArgs
- // now reset the flagset parse again
- set, err = flagSet(c.Name, c.Flags)
- if err != nil {
- return nil, err
- }
- set.SetOutput(ioutil.Discard)
- goto PARSE
- }
- }
- return nil, err
- }
-
- err = normalizeFlags(c.Flags, set)
- if err != nil {
- return nil, err
- }
-
- return set, nil
-}
-
-// reorderArgs moves all flags before arguments as this is what flag expects
-func reorderArgs(args []string) []string {
- var nonflags, flags []string
-
- readFlagValue := false
- for i, arg := range args {
- if arg == "--" {
- nonflags = append(nonflags, args[i:]...)
- break
- }
-
- if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") {
- readFlagValue = false
- flags = append(flags, arg)
- continue
- }
- readFlagValue = false
-
- if arg != "-" && strings.HasPrefix(arg, "-") {
- flags = append(flags, arg)
-
- readFlagValue = !strings.Contains(arg, "=")
- } else {
- nonflags = append(nonflags, arg)
- }
- }
-
- return append(flags, nonflags...)
-}
-
-func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string {
- allCharsFlags := func (s string) bool {
- for i := range s {
- f := set.Lookup(string(s[i]))
- if f == nil {
- return false
- }
- }
- return true
- }
-
- // separate combined flags
- var flagArgsSeparated []string
- for _, flagArg := range flagArgs {
- if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 {
- if !allCharsFlags(flagArg[1:]) {
- flagArgsSeparated = append(flagArgsSeparated, flagArg)
- continue
- }
- for _, flagChar := range flagArg[1:] {
- flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar))
- }
- } else {
- flagArgsSeparated = append(flagArgsSeparated, flagArg)
- }
- }
- return flagArgsSeparated
-}
-
-// Names returns the names including short names and aliases.
-func (c Command) Names() []string {
- names := []string{c.Name}
-
- if c.ShortName != "" {
- names = append(names, c.ShortName)
- }
-
- return append(names, c.Aliases...)
-}
-
-// HasName returns true if Command.Name or Command.ShortName matches given name
-func (c Command) HasName(name string) bool {
- for _, n := range c.Names() {
- if n == name {
- return true
- }
- }
- return false
-}
-
-func (c Command) startApp(ctx *Context) error {
- app := NewApp()
- app.Metadata = ctx.App.Metadata
- // set the name and usage
- app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
- if c.HelpName == "" {
- app.HelpName = c.HelpName
- } else {
- app.HelpName = app.Name
- }
-
- app.Usage = c.Usage
- app.Description = c.Description
- app.ArgsUsage = c.ArgsUsage
-
- // set CommandNotFound
- app.CommandNotFound = ctx.App.CommandNotFound
- app.CustomAppHelpTemplate = c.CustomHelpTemplate
-
- // set the flags and commands
- app.Commands = c.Subcommands
- app.Flags = c.Flags
- app.HideHelp = c.HideHelp
-
- app.Version = ctx.App.Version
- app.HideVersion = ctx.App.HideVersion
- app.Compiled = ctx.App.Compiled
- app.Author = ctx.App.Author
- app.Email = ctx.App.Email
- app.Writer = ctx.App.Writer
- app.ErrWriter = ctx.App.ErrWriter
-
- app.categories = CommandCategories{}
- for _, command := range c.Subcommands {
- app.categories = app.categories.AddCommand(command.Category, command)
- }
-
- sort.Sort(app.categories)
-
- // bash completion
- app.EnableBashCompletion = ctx.App.EnableBashCompletion
- if c.BashComplete != nil {
- app.BashComplete = c.BashComplete
- }
-
- // set the actions
- app.Before = c.Before
- app.After = c.After
- if c.Action != nil {
- app.Action = c.Action
- } else {
- app.Action = helpSubcommand.Action
- }
- app.OnUsageError = c.OnUsageError
-
- for index, cc := range app.Commands {
- app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
- }
-
- return app.RunAsSubcommand(ctx)
-}
-
-// VisibleFlags returns a slice of the Flags with Hidden=false
-func (c Command) VisibleFlags() []Flag {
- return visibleFlags(c.Flags)
-}
diff --git a/vendor/github.com/urfave/cli/context.go b/vendor/github.com/urfave/cli/context.go
deleted file mode 100644
index 552ee7405..000000000
--- a/vendor/github.com/urfave/cli/context.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package cli
-
-import (
- "errors"
- "flag"
- "os"
- "reflect"
- "strings"
- "syscall"
-)
-
-// Context is a type that is passed through to
-// each Handler action in a cli application. Context
-// can be used to retrieve context-specific Args and
-// parsed command-line options.
-type Context struct {
- App *App
- Command Command
- shellComplete bool
- flagSet *flag.FlagSet
- setFlags map[string]bool
- parentContext *Context
-}
-
-// NewContext creates a new context. For use in when invoking an App or Command action.
-func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
- c := &Context{App: app, flagSet: set, parentContext: parentCtx}
-
- if parentCtx != nil {
- c.shellComplete = parentCtx.shellComplete
- }
-
- return c
-}
-
-// NumFlags returns the number of flags set
-func (c *Context) NumFlags() int {
- return c.flagSet.NFlag()
-}
-
-// Set sets a context flag to a value.
-func (c *Context) Set(name, value string) error {
- c.setFlags = nil
- return c.flagSet.Set(name, value)
-}
-
-// GlobalSet sets a context flag to a value on the global flagset
-func (c *Context) GlobalSet(name, value string) error {
- globalContext(c).setFlags = nil
- return globalContext(c).flagSet.Set(name, value)
-}
-
-// IsSet determines if the flag was actually set
-func (c *Context) IsSet(name string) bool {
- if c.setFlags == nil {
- c.setFlags = make(map[string]bool)
-
- c.flagSet.Visit(func(f *flag.Flag) {
- c.setFlags[f.Name] = true
- })
-
- c.flagSet.VisitAll(func(f *flag.Flag) {
- if _, ok := c.setFlags[f.Name]; ok {
- return
- }
- c.setFlags[f.Name] = false
- })
-
- // XXX hack to support IsSet for flags with EnvVar
- //
- // There isn't an easy way to do this with the current implementation since
- // whether a flag was set via an environment variable is very difficult to
- // determine here. Instead, we intend to introduce a backwards incompatible
- // change in version 2 to add `IsSet` to the Flag interface to push the
- // responsibility closer to where the information required to determine
- // whether a flag is set by non-standard means such as environment
- // variables is available.
- //
- // See https://github.com/urfave/cli/issues/294 for additional discussion
- flags := c.Command.Flags
- if c.Command.Name == "" { // cannot == Command{} since it contains slice types
- if c.App != nil {
- flags = c.App.Flags
- }
- }
- for _, f := range flags {
- eachName(f.GetName(), func(name string) {
- if isSet, ok := c.setFlags[name]; isSet || !ok {
- return
- }
-
- val := reflect.ValueOf(f)
- if val.Kind() == reflect.Ptr {
- val = val.Elem()
- }
-
- filePathValue := val.FieldByName("FilePath")
- if filePathValue.IsValid() {
- eachName(filePathValue.String(), func(filePath string) {
- if _, err := os.Stat(filePath); err == nil {
- c.setFlags[name] = true
- return
- }
- })
- }
-
- envVarValue := val.FieldByName("EnvVar")
- if envVarValue.IsValid() {
- eachName(envVarValue.String(), func(envVar string) {
- envVar = strings.TrimSpace(envVar)
- if _, ok := syscall.Getenv(envVar); ok {
- c.setFlags[name] = true
- return
- }
- })
- }
- })
- }
- }
-
- return c.setFlags[name]
-}
-
-// GlobalIsSet determines if the global flag was actually set
-func (c *Context) GlobalIsSet(name string) bool {
- ctx := c
- if ctx.parentContext != nil {
- ctx = ctx.parentContext
- }
-
- for ; ctx != nil; ctx = ctx.parentContext {
- if ctx.IsSet(name) {
- return true
- }
- }
- return false
-}
-
-// FlagNames returns a slice of flag names used in this context.
-func (c *Context) FlagNames() (names []string) {
- for _, flag := range c.Command.Flags {
- name := strings.Split(flag.GetName(), ",")[0]
- if name == "help" {
- continue
- }
- names = append(names, name)
- }
- return
-}
-
-// GlobalFlagNames returns a slice of global flag names used by the app.
-func (c *Context) GlobalFlagNames() (names []string) {
- for _, flag := range c.App.Flags {
- name := strings.Split(flag.GetName(), ",")[0]
- if name == "help" || name == "version" {
- continue
- }
- names = append(names, name)
- }
- return
-}
-
-// Parent returns the parent context, if any
-func (c *Context) Parent() *Context {
- return c.parentContext
-}
-
-// value returns the value of the flag coressponding to `name`
-func (c *Context) value(name string) interface{} {
- return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
-}
-
-// Args contains apps console arguments
-type Args []string
-
-// Args returns the command line arguments associated with the context.
-func (c *Context) Args() Args {
- args := Args(c.flagSet.Args())
- return args
-}
-
-// NArg returns the number of the command line arguments.
-func (c *Context) NArg() int {
- return len(c.Args())
-}
-
-// Get returns the nth argument, or else a blank string
-func (a Args) Get(n int) string {
- if len(a) > n {
- return a[n]
- }
- return ""
-}
-
-// First returns the first argument, or else a blank string
-func (a Args) First() string {
- return a.Get(0)
-}
-
-// Tail returns the rest of the arguments (not the first one)
-// or else an empty string slice
-func (a Args) Tail() []string {
- if len(a) >= 2 {
- return []string(a)[1:]
- }
- return []string{}
-}
-
-// Present checks if there are any arguments present
-func (a Args) Present() bool {
- return len(a) != 0
-}
-
-// Swap swaps arguments at the given indexes
-func (a Args) Swap(from, to int) error {
- if from >= len(a) || to >= len(a) {
- return errors.New("index out of range")
- }
- a[from], a[to] = a[to], a[from]
- return nil
-}
-
-func globalContext(ctx *Context) *Context {
- if ctx == nil {
- return nil
- }
-
- for {
- if ctx.parentContext == nil {
- return ctx
- }
- ctx = ctx.parentContext
- }
-}
-
-func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
- if ctx.parentContext != nil {
- ctx = ctx.parentContext
- }
- for ; ctx != nil; ctx = ctx.parentContext {
- if f := ctx.flagSet.Lookup(name); f != nil {
- return ctx.flagSet
- }
- }
- return nil
-}
-
-func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
- switch ff.Value.(type) {
- case *StringSlice:
- default:
- set.Set(name, ff.Value.String())
- }
-}
-
-func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
- visited := make(map[string]bool)
- set.Visit(func(f *flag.Flag) {
- visited[f.Name] = true
- })
- for _, f := range flags {
- parts := strings.Split(f.GetName(), ",")
- if len(parts) == 1 {
- continue
- }
- var ff *flag.Flag
- for _, name := range parts {
- name = strings.Trim(name, " ")
- if visited[name] {
- if ff != nil {
- return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
- }
- ff = set.Lookup(name)
- }
- }
- if ff == nil {
- continue
- }
- for _, name := range parts {
- name = strings.Trim(name, " ")
- if !visited[name] {
- copyFlag(name, ff, set)
- }
- }
- }
- return nil
-}
diff --git a/vendor/github.com/urfave/cli/errors.go b/vendor/github.com/urfave/cli/errors.go
deleted file mode 100644
index 562b2953c..000000000
--- a/vendor/github.com/urfave/cli/errors.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package cli
-
-import (
- "fmt"
- "io"
- "os"
- "strings"
-)
-
-// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
-var OsExiter = os.Exit
-
-// ErrWriter is used to write errors to the user. This can be anything
-// implementing the io.Writer interface and defaults to os.Stderr.
-var ErrWriter io.Writer = os.Stderr
-
-// MultiError is an error that wraps multiple errors.
-type MultiError struct {
- Errors []error
-}
-
-// NewMultiError creates a new MultiError. Pass in one or more errors.
-func NewMultiError(err ...error) MultiError {
- return MultiError{Errors: err}
-}
-
-// Error implements the error interface.
-func (m MultiError) Error() string {
- errs := make([]string, len(m.Errors))
- for i, err := range m.Errors {
- errs[i] = err.Error()
- }
-
- return strings.Join(errs, "\n")
-}
-
-type ErrorFormatter interface {
- Format(s fmt.State, verb rune)
-}
-
-// ExitCoder is the interface checked by `App` and `Command` for a custom exit
-// code
-type ExitCoder interface {
- error
- ExitCode() int
-}
-
-// ExitError fulfills both the builtin `error` interface and `ExitCoder`
-type ExitError struct {
- exitCode int
- message interface{}
-}
-
-// NewExitError makes a new *ExitError
-func NewExitError(message interface{}, exitCode int) *ExitError {
- return &ExitError{
- exitCode: exitCode,
- message: message,
- }
-}
-
-// Error returns the string message, fulfilling the interface required by
-// `error`
-func (ee *ExitError) Error() string {
- return fmt.Sprintf("%v", ee.message)
-}
-
-// ExitCode returns the exit code, fulfilling the interface required by
-// `ExitCoder`
-func (ee *ExitError) ExitCode() int {
- return ee.exitCode
-}
-
-// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
-// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
-// given exit code. If the given error is a MultiError, then this func is
-// called on all members of the Errors slice and calls OsExiter with the last exit code.
-func HandleExitCoder(err error) {
- if err == nil {
- return
- }
-
- if exitErr, ok := err.(ExitCoder); ok {
- if err.Error() != "" {
- if _, ok := exitErr.(ErrorFormatter); ok {
- fmt.Fprintf(ErrWriter, "%+v\n", err)
- } else {
- fmt.Fprintln(ErrWriter, err)
- }
- }
- OsExiter(exitErr.ExitCode())
- return
- }
-
- if multiErr, ok := err.(MultiError); ok {
- code := handleMultiError(multiErr)
- OsExiter(code)
- return
- }
-}
-
-func handleMultiError(multiErr MultiError) int {
- code := 1
- for _, merr := range multiErr.Errors {
- if multiErr2, ok := merr.(MultiError); ok {
- code = handleMultiError(multiErr2)
- } else {
- fmt.Fprintln(ErrWriter, merr)
- if exitErr, ok := merr.(ExitCoder); ok {
- code = exitErr.ExitCode()
- }
- }
- }
- return code
-}
diff --git a/vendor/github.com/urfave/cli/flag.go b/vendor/github.com/urfave/cli/flag.go
deleted file mode 100644
index b0cffc006..000000000
--- a/vendor/github.com/urfave/cli/flag.go
+++ /dev/null
@@ -1,786 +0,0 @@
-package cli
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "reflect"
- "runtime"
- "strconv"
- "strings"
- "syscall"
- "time"
-)
-
-const defaultPlaceholder = "value"
-
-// BashCompletionFlag enables bash-completion for all commands and subcommands
-var BashCompletionFlag Flag = BoolFlag{
- Name: "generate-bash-completion",
- Hidden: true,
-}
-
-// VersionFlag prints the version for the application
-var VersionFlag Flag = BoolFlag{
- Name: "version, v",
- Usage: "print the version",
-}
-
-// HelpFlag prints the help for all commands and subcommands
-// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
-// unless HideHelp is set to true)
-var HelpFlag Flag = BoolFlag{
- Name: "help, h",
- Usage: "show help",
-}
-
-// FlagStringer converts a flag definition to a string. This is used by help
-// to display a flag.
-var FlagStringer FlagStringFunc = stringifyFlag
-
-// FlagNamePrefixer converts a full flag name and its placeholder into the help
-// message flag prefix. This is used by the default FlagStringer.
-var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames
-
-// FlagEnvHinter annotates flag help message with the environment variable
-// details. This is used by the default FlagStringer.
-var FlagEnvHinter FlagEnvHintFunc = withEnvHint
-
-// FlagFileHinter annotates flag help message with the environment variable
-// details. This is used by the default FlagStringer.
-var FlagFileHinter FlagFileHintFunc = withFileHint
-
-// FlagsByName is a slice of Flag.
-type FlagsByName []Flag
-
-func (f FlagsByName) Len() int {
- return len(f)
-}
-
-func (f FlagsByName) Less(i, j int) bool {
- return lexicographicLess(f[i].GetName(), f[j].GetName())
-}
-
-func (f FlagsByName) Swap(i, j int) {
- f[i], f[j] = f[j], f[i]
-}
-
-// Flag is a common interface related to parsing flags in cli.
-// For more advanced flag parsing techniques, it is recommended that
-// this interface be implemented.
-type Flag interface {
- fmt.Stringer
- // Apply Flag settings to the given flag set
- Apply(*flag.FlagSet)
- GetName() string
-}
-
-// errorableFlag is an interface that allows us to return errors during apply
-// it allows flags defined in this library to return errors in a fashion backwards compatible
-// TODO remove in v2 and modify the existing Flag interface to return errors
-type errorableFlag interface {
- Flag
-
- ApplyWithError(*flag.FlagSet) error
-}
-
-func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
- set := flag.NewFlagSet(name, flag.ContinueOnError)
-
- for _, f := range flags {
- //TODO remove in v2 when errorableFlag is removed
- if ef, ok := f.(errorableFlag); ok {
- if err := ef.ApplyWithError(set); err != nil {
- return nil, err
- }
- } else {
- f.Apply(set)
- }
- }
- return set, nil
-}
-
-func eachName(longName string, fn func(string)) {
- parts := strings.Split(longName, ",")
- for _, name := range parts {
- name = strings.Trim(name, " ")
- fn(name)
- }
-}
-
-// Generic is a generic parseable type identified by a specific flag
-type Generic interface {
- Set(value string) error
- String() string
-}
-
-// Apply takes the flagset and calls Set on the generic flag with the value
-// provided by the user for parsing by the flag
-// Ignores parsing errors
-func (f GenericFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError takes the flagset and calls Set on the generic flag with the value
-// provided by the user for parsing by the flag
-func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
- val := f.Value
- if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- if err := val.Set(fileEnvVal); err != nil {
- return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err)
- }
- }
-
- eachName(f.Name, func(name string) {
- set.Var(f.Value, name, f.Usage)
- })
-
- return nil
-}
-
-// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
-type StringSlice []string
-
-// Set appends the string value to the list of values
-func (f *StringSlice) Set(value string) error {
- *f = append(*f, value)
- return nil
-}
-
-// String returns a readable representation of this value (for usage defaults)
-func (f *StringSlice) String() string {
- return fmt.Sprintf("%s", *f)
-}
-
-// Value returns the slice of strings set by this flag
-func (f *StringSlice) Value() []string {
- return *f
-}
-
-// Get returns the slice of strings set by this flag
-func (f *StringSlice) Get() interface{} {
- return *f
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f StringSliceFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- newVal := &StringSlice{}
- for _, s := range strings.Split(envVal, ",") {
- s = strings.TrimSpace(s)
- if err := newVal.Set(s); err != nil {
- return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
- }
- }
- if f.Value == nil {
- f.Value = newVal
- } else {
- *f.Value = *newVal
- }
- }
-
- eachName(f.Name, func(name string) {
- if f.Value == nil {
- f.Value = &StringSlice{}
- }
- set.Var(f.Value, name, f.Usage)
- })
-
- return nil
-}
-
-// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
-type IntSlice []int
-
-// Set parses the value into an integer and appends it to the list of values
-func (f *IntSlice) Set(value string) error {
- tmp, err := strconv.Atoi(value)
- if err != nil {
- return err
- }
- *f = append(*f, tmp)
- return nil
-}
-
-// String returns a readable representation of this value (for usage defaults)
-func (f *IntSlice) String() string {
- return fmt.Sprintf("%#v", *f)
-}
-
-// Value returns the slice of ints set by this flag
-func (f *IntSlice) Value() []int {
- return *f
-}
-
-// Get returns the slice of ints set by this flag
-func (f *IntSlice) Get() interface{} {
- return *f
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f IntSliceFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- newVal := &IntSlice{}
- for _, s := range strings.Split(envVal, ",") {
- s = strings.TrimSpace(s)
- if err := newVal.Set(s); err != nil {
- return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err)
- }
- }
- if f.Value == nil {
- f.Value = newVal
- } else {
- *f.Value = *newVal
- }
- }
-
- eachName(f.Name, func(name string) {
- if f.Value == nil {
- f.Value = &IntSlice{}
- }
- set.Var(f.Value, name, f.Usage)
- })
-
- return nil
-}
-
-// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
-type Int64Slice []int64
-
-// Set parses the value into an integer and appends it to the list of values
-func (f *Int64Slice) Set(value string) error {
- tmp, err := strconv.ParseInt(value, 10, 64)
- if err != nil {
- return err
- }
- *f = append(*f, tmp)
- return nil
-}
-
-// String returns a readable representation of this value (for usage defaults)
-func (f *Int64Slice) String() string {
- return fmt.Sprintf("%#v", *f)
-}
-
-// Value returns the slice of ints set by this flag
-func (f *Int64Slice) Value() []int64 {
- return *f
-}
-
-// Get returns the slice of ints set by this flag
-func (f *Int64Slice) Get() interface{} {
- return *f
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- newVal := &Int64Slice{}
- for _, s := range strings.Split(envVal, ",") {
- s = strings.TrimSpace(s)
- if err := newVal.Set(s); err != nil {
- return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err)
- }
- }
- if f.Value == nil {
- f.Value = newVal
- } else {
- *f.Value = *newVal
- }
- }
-
- eachName(f.Name, func(name string) {
- if f.Value == nil {
- f.Value = &Int64Slice{}
- }
- set.Var(f.Value, name, f.Usage)
- })
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f BoolFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
- val := false
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- if envVal == "" {
- val = false
- } else {
- envValBool, err := strconv.ParseBool(envVal)
- if err != nil {
- return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
- }
- val = envValBool
- }
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.BoolVar(f.Destination, name, val, f.Usage)
- return
- }
- set.Bool(name, val, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f BoolTFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
- val := true
-
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- if envVal == "" {
- val = false
- } else {
- envValBool, err := strconv.ParseBool(envVal)
- if err != nil {
- return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
- }
- val = envValBool
- }
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.BoolVar(f.Destination, name, val, f.Usage)
- return
- }
- set.Bool(name, val, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f StringFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- f.Value = envVal
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.StringVar(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.String(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f IntFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- envValInt, err := strconv.ParseInt(envVal, 0, 64)
- if err != nil {
- return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
- }
- f.Value = int(envValInt)
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.IntVar(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.Int(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f Int64Flag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- envValInt, err := strconv.ParseInt(envVal, 0, 64)
- if err != nil {
- return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
- }
-
- f.Value = envValInt
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.Int64Var(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.Int64(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f UintFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- envValInt, err := strconv.ParseUint(envVal, 0, 64)
- if err != nil {
- return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err)
- }
-
- f.Value = uint(envValInt)
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.UintVar(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.Uint(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f Uint64Flag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- envValInt, err := strconv.ParseUint(envVal, 0, 64)
- if err != nil {
- return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err)
- }
-
- f.Value = uint64(envValInt)
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.Uint64Var(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.Uint64(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f DurationFlag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- envValDuration, err := time.ParseDuration(envVal)
- if err != nil {
- return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
- }
-
- f.Value = envValDuration
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.DurationVar(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.Duration(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-// Apply populates the flag given the flag set and environment
-// Ignores errors
-func (f Float64Flag) Apply(set *flag.FlagSet) {
- f.ApplyWithError(set)
-}
-
-// ApplyWithError populates the flag given the flag set and environment
-func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
- if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
- envValFloat, err := strconv.ParseFloat(envVal, 10)
- if err != nil {
- return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err)
- }
-
- f.Value = float64(envValFloat)
- }
-
- eachName(f.Name, func(name string) {
- if f.Destination != nil {
- set.Float64Var(f.Destination, name, f.Value, f.Usage)
- return
- }
- set.Float64(name, f.Value, f.Usage)
- })
-
- return nil
-}
-
-func visibleFlags(fl []Flag) []Flag {
- visible := []Flag{}
- for _, flag := range fl {
- field := flagValue(flag).FieldByName("Hidden")
- if !field.IsValid() || !field.Bool() {
- visible = append(visible, flag)
- }
- }
- return visible
-}
-
-func prefixFor(name string) (prefix string) {
- if len(name) == 1 {
- prefix = "-"
- } else {
- prefix = "--"
- }
-
- return
-}
-
-// Returns the placeholder, if any, and the unquoted usage string.
-func unquoteUsage(usage string) (string, string) {
- for i := 0; i < len(usage); i++ {
- if usage[i] == '`' {
- for j := i + 1; j < len(usage); j++ {
- if usage[j] == '`' {
- name := usage[i+1 : j]
- usage = usage[:i] + name + usage[j+1:]
- return name, usage
- }
- }
- break
- }
- }
- return "", usage
-}
-
-func prefixedNames(fullName, placeholder string) string {
- var prefixed string
- parts := strings.Split(fullName, ",")
- for i, name := range parts {
- name = strings.Trim(name, " ")
- prefixed += prefixFor(name) + name
- if placeholder != "" {
- prefixed += " " + placeholder
- }
- if i < len(parts)-1 {
- prefixed += ", "
- }
- }
- return prefixed
-}
-
-func withEnvHint(envVar, str string) string {
- envText := ""
- if envVar != "" {
- prefix := "$"
- suffix := ""
- sep := ", $"
- if runtime.GOOS == "windows" {
- prefix = "%"
- suffix = "%"
- sep = "%, %"
- }
- envText = " [" + prefix + strings.Join(strings.Split(envVar, ","), sep) + suffix + "]"
- }
- return str + envText
-}
-
-func withFileHint(filePath, str string) string {
- fileText := ""
- if filePath != "" {
- fileText = fmt.Sprintf(" [%s]", filePath)
- }
- return str + fileText
-}
-
-func flagValue(f Flag) reflect.Value {
- fv := reflect.ValueOf(f)
- for fv.Kind() == reflect.Ptr {
- fv = reflect.Indirect(fv)
- }
- return fv
-}
-
-func stringifyFlag(f Flag) string {
- fv := flagValue(f)
-
- switch f.(type) {
- case IntSliceFlag:
- return FlagFileHinter(
- fv.FieldByName("FilePath").String(),
- FlagEnvHinter(
- fv.FieldByName("EnvVar").String(),
- stringifyIntSliceFlag(f.(IntSliceFlag)),
- ),
- )
- case Int64SliceFlag:
- return FlagFileHinter(
- fv.FieldByName("FilePath").String(),
- FlagEnvHinter(
- fv.FieldByName("EnvVar").String(),
- stringifyInt64SliceFlag(f.(Int64SliceFlag)),
- ),
- )
- case StringSliceFlag:
- return FlagFileHinter(
- fv.FieldByName("FilePath").String(),
- FlagEnvHinter(
- fv.FieldByName("EnvVar").String(),
- stringifyStringSliceFlag(f.(StringSliceFlag)),
- ),
- )
- }
-
- placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
-
- needsPlaceholder := false
- defaultValueString := ""
-
- if val := fv.FieldByName("Value"); val.IsValid() {
- needsPlaceholder = true
- defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
-
- if val.Kind() == reflect.String && val.String() != "" {
- defaultValueString = fmt.Sprintf(" (default: %q)", val.String())
- }
- }
-
- if defaultValueString == " (default: )" {
- defaultValueString = ""
- }
-
- if needsPlaceholder && placeholder == "" {
- placeholder = defaultPlaceholder
- }
-
- usageWithDefault := strings.TrimSpace(usage + defaultValueString)
-
- return FlagFileHinter(
- fv.FieldByName("FilePath").String(),
- FlagEnvHinter(
- fv.FieldByName("EnvVar").String(),
- FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder)+"\t"+usageWithDefault,
- ),
- )
-}
-
-func stringifyIntSliceFlag(f IntSliceFlag) string {
- defaultVals := []string{}
- if f.Value != nil && len(f.Value.Value()) > 0 {
- for _, i := range f.Value.Value() {
- defaultVals = append(defaultVals, strconv.Itoa(i))
- }
- }
-
- return stringifySliceFlag(f.Usage, f.Name, defaultVals)
-}
-
-func stringifyInt64SliceFlag(f Int64SliceFlag) string {
- defaultVals := []string{}
- if f.Value != nil && len(f.Value.Value()) > 0 {
- for _, i := range f.Value.Value() {
- defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
- }
- }
-
- return stringifySliceFlag(f.Usage, f.Name, defaultVals)
-}
-
-func stringifyStringSliceFlag(f StringSliceFlag) string {
- defaultVals := []string{}
- if f.Value != nil && len(f.Value.Value()) > 0 {
- for _, s := range f.Value.Value() {
- if len(s) > 0 {
- defaultVals = append(defaultVals, strconv.Quote(s))
- }
- }
- }
-
- return stringifySliceFlag(f.Usage, f.Name, defaultVals)
-}
-
-func stringifySliceFlag(usage, name string, defaultVals []string) string {
- placeholder, usage := unquoteUsage(usage)
- if placeholder == "" {
- placeholder = defaultPlaceholder
- }
-
- defaultVal := ""
- if len(defaultVals) > 0 {
- defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", "))
- }
-
- usageWithDefault := strings.TrimSpace(usage + defaultVal)
- return FlagNamePrefixer(name, placeholder) + "\t" + usageWithDefault
-}
-
-func flagFromFileEnv(filePath, envName string) (val string, ok bool) {
- for _, envVar := range strings.Split(envName, ",") {
- envVar = strings.TrimSpace(envVar)
- if envVal, ok := syscall.Getenv(envVar); ok {
- return envVal, true
- }
- }
- for _, fileVar := range strings.Split(filePath, ",") {
- if data, err := ioutil.ReadFile(fileVar); err == nil {
- return string(data), true
- }
- }
- return "", false
-}
diff --git a/vendor/github.com/urfave/cli/flag_generated.go b/vendor/github.com/urfave/cli/flag_generated.go
deleted file mode 100644
index 001576c8b..000000000
--- a/vendor/github.com/urfave/cli/flag_generated.go
+++ /dev/null
@@ -1,640 +0,0 @@
-package cli
-
-import (
- "flag"
- "strconv"
- "time"
-)
-
-// WARNING: This file is generated!
-
-// BoolFlag is a flag with type bool
-type BoolFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Destination *bool
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f BoolFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f BoolFlag) GetName() string {
- return f.Name
-}
-
-// Bool looks up the value of a local BoolFlag, returns
-// false if not found
-func (c *Context) Bool(name string) bool {
- return lookupBool(name, c.flagSet)
-}
-
-// GlobalBool looks up the value of a global BoolFlag, returns
-// false if not found
-func (c *Context) GlobalBool(name string) bool {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupBool(name, fs)
- }
- return false
-}
-
-func lookupBool(name string, set *flag.FlagSet) bool {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseBool(f.Value.String())
- if err != nil {
- return false
- }
- return parsed
- }
- return false
-}
-
-// BoolTFlag is a flag with type bool that is true by default
-type BoolTFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Destination *bool
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f BoolTFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f BoolTFlag) GetName() string {
- return f.Name
-}
-
-// BoolT looks up the value of a local BoolTFlag, returns
-// false if not found
-func (c *Context) BoolT(name string) bool {
- return lookupBoolT(name, c.flagSet)
-}
-
-// GlobalBoolT looks up the value of a global BoolTFlag, returns
-// false if not found
-func (c *Context) GlobalBoolT(name string) bool {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupBoolT(name, fs)
- }
- return false
-}
-
-func lookupBoolT(name string, set *flag.FlagSet) bool {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseBool(f.Value.String())
- if err != nil {
- return false
- }
- return parsed
- }
- return false
-}
-
-// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
-type DurationFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value time.Duration
- Destination *time.Duration
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f DurationFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f DurationFlag) GetName() string {
- return f.Name
-}
-
-// Duration looks up the value of a local DurationFlag, returns
-// 0 if not found
-func (c *Context) Duration(name string) time.Duration {
- return lookupDuration(name, c.flagSet)
-}
-
-// GlobalDuration looks up the value of a global DurationFlag, returns
-// 0 if not found
-func (c *Context) GlobalDuration(name string) time.Duration {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupDuration(name, fs)
- }
- return 0
-}
-
-func lookupDuration(name string, set *flag.FlagSet) time.Duration {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := time.ParseDuration(f.Value.String())
- if err != nil {
- return 0
- }
- return parsed
- }
- return 0
-}
-
-// Float64Flag is a flag with type float64
-type Float64Flag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value float64
- Destination *float64
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f Float64Flag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f Float64Flag) GetName() string {
- return f.Name
-}
-
-// Float64 looks up the value of a local Float64Flag, returns
-// 0 if not found
-func (c *Context) Float64(name string) float64 {
- return lookupFloat64(name, c.flagSet)
-}
-
-// GlobalFloat64 looks up the value of a global Float64Flag, returns
-// 0 if not found
-func (c *Context) GlobalFloat64(name string) float64 {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupFloat64(name, fs)
- }
- return 0
-}
-
-func lookupFloat64(name string, set *flag.FlagSet) float64 {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseFloat(f.Value.String(), 64)
- if err != nil {
- return 0
- }
- return parsed
- }
- return 0
-}
-
-// GenericFlag is a flag with type Generic
-type GenericFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value Generic
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f GenericFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f GenericFlag) GetName() string {
- return f.Name
-}
-
-// Generic looks up the value of a local GenericFlag, returns
-// nil if not found
-func (c *Context) Generic(name string) interface{} {
- return lookupGeneric(name, c.flagSet)
-}
-
-// GlobalGeneric looks up the value of a global GenericFlag, returns
-// nil if not found
-func (c *Context) GlobalGeneric(name string) interface{} {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupGeneric(name, fs)
- }
- return nil
-}
-
-func lookupGeneric(name string, set *flag.FlagSet) interface{} {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := f.Value, error(nil)
- if err != nil {
- return nil
- }
- return parsed
- }
- return nil
-}
-
-// Int64Flag is a flag with type int64
-type Int64Flag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value int64
- Destination *int64
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f Int64Flag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f Int64Flag) GetName() string {
- return f.Name
-}
-
-// Int64 looks up the value of a local Int64Flag, returns
-// 0 if not found
-func (c *Context) Int64(name string) int64 {
- return lookupInt64(name, c.flagSet)
-}
-
-// GlobalInt64 looks up the value of a global Int64Flag, returns
-// 0 if not found
-func (c *Context) GlobalInt64(name string) int64 {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupInt64(name, fs)
- }
- return 0
-}
-
-func lookupInt64(name string, set *flag.FlagSet) int64 {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
- if err != nil {
- return 0
- }
- return parsed
- }
- return 0
-}
-
-// IntFlag is a flag with type int
-type IntFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value int
- Destination *int
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f IntFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f IntFlag) GetName() string {
- return f.Name
-}
-
-// Int looks up the value of a local IntFlag, returns
-// 0 if not found
-func (c *Context) Int(name string) int {
- return lookupInt(name, c.flagSet)
-}
-
-// GlobalInt looks up the value of a global IntFlag, returns
-// 0 if not found
-func (c *Context) GlobalInt(name string) int {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupInt(name, fs)
- }
- return 0
-}
-
-func lookupInt(name string, set *flag.FlagSet) int {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
- if err != nil {
- return 0
- }
- return int(parsed)
- }
- return 0
-}
-
-// IntSliceFlag is a flag with type *IntSlice
-type IntSliceFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value *IntSlice
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f IntSliceFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f IntSliceFlag) GetName() string {
- return f.Name
-}
-
-// IntSlice looks up the value of a local IntSliceFlag, returns
-// nil if not found
-func (c *Context) IntSlice(name string) []int {
- return lookupIntSlice(name, c.flagSet)
-}
-
-// GlobalIntSlice looks up the value of a global IntSliceFlag, returns
-// nil if not found
-func (c *Context) GlobalIntSlice(name string) []int {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupIntSlice(name, fs)
- }
- return nil
-}
-
-func lookupIntSlice(name string, set *flag.FlagSet) []int {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := (f.Value.(*IntSlice)).Value(), error(nil)
- if err != nil {
- return nil
- }
- return parsed
- }
- return nil
-}
-
-// Int64SliceFlag is a flag with type *Int64Slice
-type Int64SliceFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value *Int64Slice
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f Int64SliceFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f Int64SliceFlag) GetName() string {
- return f.Name
-}
-
-// Int64Slice looks up the value of a local Int64SliceFlag, returns
-// nil if not found
-func (c *Context) Int64Slice(name string) []int64 {
- return lookupInt64Slice(name, c.flagSet)
-}
-
-// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
-// nil if not found
-func (c *Context) GlobalInt64Slice(name string) []int64 {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupInt64Slice(name, fs)
- }
- return nil
-}
-
-func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil)
- if err != nil {
- return nil
- }
- return parsed
- }
- return nil
-}
-
-// StringFlag is a flag with type string
-type StringFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value string
- Destination *string
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f StringFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f StringFlag) GetName() string {
- return f.Name
-}
-
-// String looks up the value of a local StringFlag, returns
-// "" if not found
-func (c *Context) String(name string) string {
- return lookupString(name, c.flagSet)
-}
-
-// GlobalString looks up the value of a global StringFlag, returns
-// "" if not found
-func (c *Context) GlobalString(name string) string {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupString(name, fs)
- }
- return ""
-}
-
-func lookupString(name string, set *flag.FlagSet) string {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := f.Value.String(), error(nil)
- if err != nil {
- return ""
- }
- return parsed
- }
- return ""
-}
-
-// StringSliceFlag is a flag with type *StringSlice
-type StringSliceFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value *StringSlice
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f StringSliceFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f StringSliceFlag) GetName() string {
- return f.Name
-}
-
-// StringSlice looks up the value of a local StringSliceFlag, returns
-// nil if not found
-func (c *Context) StringSlice(name string) []string {
- return lookupStringSlice(name, c.flagSet)
-}
-
-// GlobalStringSlice looks up the value of a global StringSliceFlag, returns
-// nil if not found
-func (c *Context) GlobalStringSlice(name string) []string {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupStringSlice(name, fs)
- }
- return nil
-}
-
-func lookupStringSlice(name string, set *flag.FlagSet) []string {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := (f.Value.(*StringSlice)).Value(), error(nil)
- if err != nil {
- return nil
- }
- return parsed
- }
- return nil
-}
-
-// Uint64Flag is a flag with type uint64
-type Uint64Flag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value uint64
- Destination *uint64
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f Uint64Flag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f Uint64Flag) GetName() string {
- return f.Name
-}
-
-// Uint64 looks up the value of a local Uint64Flag, returns
-// 0 if not found
-func (c *Context) Uint64(name string) uint64 {
- return lookupUint64(name, c.flagSet)
-}
-
-// GlobalUint64 looks up the value of a global Uint64Flag, returns
-// 0 if not found
-func (c *Context) GlobalUint64(name string) uint64 {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupUint64(name, fs)
- }
- return 0
-}
-
-func lookupUint64(name string, set *flag.FlagSet) uint64 {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
- if err != nil {
- return 0
- }
- return parsed
- }
- return 0
-}
-
-// UintFlag is a flag with type uint
-type UintFlag struct {
- Name string
- Usage string
- EnvVar string
- FilePath string
- Hidden bool
- Value uint
- Destination *uint
-}
-
-// String returns a readable representation of this value
-// (for usage defaults)
-func (f UintFlag) String() string {
- return FlagStringer(f)
-}
-
-// GetName returns the name of the flag
-func (f UintFlag) GetName() string {
- return f.Name
-}
-
-// Uint looks up the value of a local UintFlag, returns
-// 0 if not found
-func (c *Context) Uint(name string) uint {
- return lookupUint(name, c.flagSet)
-}
-
-// GlobalUint looks up the value of a global UintFlag, returns
-// 0 if not found
-func (c *Context) GlobalUint(name string) uint {
- if fs := lookupGlobalFlagSet(name, c); fs != nil {
- return lookupUint(name, fs)
- }
- return 0
-}
-
-func lookupUint(name string, set *flag.FlagSet) uint {
- f := set.Lookup(name)
- if f != nil {
- parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
- if err != nil {
- return 0
- }
- return uint(parsed)
- }
- return 0
-}
diff --git a/vendor/github.com/urfave/cli/funcs.go b/vendor/github.com/urfave/cli/funcs.go
deleted file mode 100644
index 0036b1130..000000000
--- a/vendor/github.com/urfave/cli/funcs.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package cli
-
-// BashCompleteFunc is an action to execute when the bash-completion flag is set
-type BashCompleteFunc func(*Context)
-
-// BeforeFunc is an action to execute before any subcommands are run, but after
-// the context is ready if a non-nil error is returned, no subcommands are run
-type BeforeFunc func(*Context) error
-
-// AfterFunc is an action to execute after any subcommands are run, but after the
-// subcommand has finished it is run even if Action() panics
-type AfterFunc func(*Context) error
-
-// ActionFunc is the action to execute when no subcommands are specified
-type ActionFunc func(*Context) error
-
-// CommandNotFoundFunc is executed if the proper command cannot be found
-type CommandNotFoundFunc func(*Context, string)
-
-// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
-// customized usage error messages. This function is able to replace the
-// original error messages. If this function is not set, the "Incorrect usage"
-// is displayed and the execution is interrupted.
-type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error
-
-// ExitErrHandlerFunc is executed if provided in order to handle ExitError values
-// returned by Actions and Before/After functions.
-type ExitErrHandlerFunc func(context *Context, err error)
-
-// FlagStringFunc is used by the help generation to display a flag, which is
-// expected to be a single line.
-type FlagStringFunc func(Flag) string
-
-// FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix
-// text for a flag's full name.
-type FlagNamePrefixFunc func(fullName, placeholder string) string
-
-// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
-// with the environment variable details.
-type FlagEnvHintFunc func(envVar, str string) string
-
-// FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help
-// with the file path details.
-type FlagFileHintFunc func(filePath, str string) string
diff --git a/vendor/github.com/urfave/cli/help.go b/vendor/github.com/urfave/cli/help.go
deleted file mode 100644
index 65874fa2f..000000000
--- a/vendor/github.com/urfave/cli/help.go
+++ /dev/null
@@ -1,345 +0,0 @@
-package cli
-
-import (
- "fmt"
- "io"
- "os"
- "strings"
- "text/tabwriter"
- "text/template"
-)
-
-// AppHelpTemplate is the text template for the Default help topic.
-// cli.go uses text/template to render templates. You can
-// render custom help text by setting this variable.
-var AppHelpTemplate = `NAME:
- {{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
-
-USAGE:
- {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
-
-VERSION:
- {{.Version}}{{end}}{{end}}{{if .Description}}
-
-DESCRIPTION:
- {{.Description}}{{end}}{{if len .Authors}}
-
-AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
- {{range $index, $author := .Authors}}{{if $index}}
- {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
-
-COMMANDS:{{range .VisibleCategories}}{{if .Name}}
-
- {{.Name}}:{{end}}{{range .VisibleCommands}}
- {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
-
-GLOBAL OPTIONS:
- {{range $index, $option := .VisibleFlags}}{{if $index}}
- {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
-
-COPYRIGHT:
- {{.Copyright}}{{end}}
-`
-
-// CommandHelpTemplate is the text template for the command help topic.
-// cli.go uses text/template to render templates. You can
-// render custom help text by setting this variable.
-var CommandHelpTemplate = `NAME:
- {{.HelpName}} - {{.Usage}}
-
-USAGE:
- {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
-
-CATEGORY:
- {{.Category}}{{end}}{{if .Description}}
-
-DESCRIPTION:
- {{.Description}}{{end}}{{if .VisibleFlags}}
-
-OPTIONS:
- {{range .VisibleFlags}}{{.}}
- {{end}}{{end}}
-`
-
-// SubcommandHelpTemplate is the text template for the subcommand help topic.
-// cli.go uses text/template to render templates. You can
-// render custom help text by setting this variable.
-var SubcommandHelpTemplate = `NAME:
- {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}}
-
-USAGE:
- {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
-
-COMMANDS:{{range .VisibleCategories}}{{if .Name}}
- {{.Name}}:{{end}}{{range .VisibleCommands}}
- {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
-{{end}}{{if .VisibleFlags}}
-OPTIONS:
- {{range .VisibleFlags}}{{.}}
- {{end}}{{end}}
-`
-
-var helpCommand = Command{
- Name: "help",
- Aliases: []string{"h"},
- Usage: "Shows a list of commands or help for one command",
- ArgsUsage: "[command]",
- Action: func(c *Context) error {
- args := c.Args()
- if args.Present() {
- return ShowCommandHelp(c, args.First())
- }
-
- ShowAppHelp(c)
- return nil
- },
-}
-
-var helpSubcommand = Command{
- Name: "help",
- Aliases: []string{"h"},
- Usage: "Shows a list of commands or help for one command",
- ArgsUsage: "[command]",
- Action: func(c *Context) error {
- args := c.Args()
- if args.Present() {
- return ShowCommandHelp(c, args.First())
- }
-
- return ShowSubcommandHelp(c)
- },
-}
-
-// Prints help for the App or Command
-type helpPrinter func(w io.Writer, templ string, data interface{})
-
-// Prints help for the App or Command with custom template function.
-type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
-
-// HelpPrinter is a function that writes the help output. If not set a default
-// is used. The function signature is:
-// func(w io.Writer, templ string, data interface{})
-var HelpPrinter helpPrinter = printHelp
-
-// HelpPrinterCustom is same as HelpPrinter but
-// takes a custom function for template function map.
-var HelpPrinterCustom helpPrinterCustom = printHelpCustom
-
-// VersionPrinter prints the version for the App
-var VersionPrinter = printVersion
-
-// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
-func ShowAppHelpAndExit(c *Context, exitCode int) {
- ShowAppHelp(c)
- os.Exit(exitCode)
-}
-
-// ShowAppHelp is an action that displays the help.
-func ShowAppHelp(c *Context) (err error) {
- if c.App.CustomAppHelpTemplate == "" {
- HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
- return
- }
- customAppData := func() map[string]interface{} {
- if c.App.ExtraInfo == nil {
- return nil
- }
- return map[string]interface{}{
- "ExtraInfo": c.App.ExtraInfo,
- }
- }
- HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
- return nil
-}
-
-// DefaultAppComplete prints the list of subcommands as the default app completion method
-func DefaultAppComplete(c *Context) {
- for _, command := range c.App.Commands {
- if command.Hidden {
- continue
- }
- if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" {
- for _, name := range command.Names() {
- fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage)
- }
- } else {
- for _, name := range command.Names() {
- fmt.Fprintf(c.App.Writer, "%s\n", name)
- }
- }
- }
-}
-
-// ShowCommandHelpAndExit - exits with code after showing help
-func ShowCommandHelpAndExit(c *Context, command string, code int) {
- ShowCommandHelp(c, command)
- os.Exit(code)
-}
-
-// ShowCommandHelp prints help for the given command
-func ShowCommandHelp(ctx *Context, command string) error {
- // show the subcommand help for a command with subcommands
- if command == "" {
- HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
- return nil
- }
-
- for _, c := range ctx.App.Commands {
- if c.HasName(command) {
- if c.CustomHelpTemplate != "" {
- HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
- } else {
- HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
- }
- return nil
- }
- }
-
- if ctx.App.CommandNotFound == nil {
- return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3)
- }
-
- ctx.App.CommandNotFound(ctx, command)
- return nil
-}
-
-// ShowSubcommandHelp prints help for the given subcommand
-func ShowSubcommandHelp(c *Context) error {
- return ShowCommandHelp(c, c.Command.Name)
-}
-
-// ShowVersion prints the version number of the App
-func ShowVersion(c *Context) {
- VersionPrinter(c)
-}
-
-func printVersion(c *Context) {
- fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
-}
-
-// ShowCompletions prints the lists of commands within a given context
-func ShowCompletions(c *Context) {
- a := c.App
- if a != nil && a.BashComplete != nil {
- a.BashComplete(c)
- }
-}
-
-// ShowCommandCompletions prints the custom completions for a given command
-func ShowCommandCompletions(ctx *Context, command string) {
- c := ctx.App.Command(command)
- if c != nil && c.BashComplete != nil {
- c.BashComplete(ctx)
- }
-}
-
-func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
- funcMap := template.FuncMap{
- "join": strings.Join,
- }
- if customFunc != nil {
- for key, value := range customFunc {
- funcMap[key] = value
- }
- }
-
- w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
- t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
- err := t.Execute(w, data)
- if err != nil {
- // If the writer is closed, t.Execute will fail, and there's nothing
- // we can do to recover.
- if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
- fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
- }
- return
- }
- w.Flush()
-}
-
-func printHelp(out io.Writer, templ string, data interface{}) {
- printHelpCustom(out, templ, data, nil)
-}
-
-func checkVersion(c *Context) bool {
- found := false
- if VersionFlag.GetName() != "" {
- eachName(VersionFlag.GetName(), func(name string) {
- if c.GlobalBool(name) || c.Bool(name) {
- found = true
- }
- })
- }
- return found
-}
-
-func checkHelp(c *Context) bool {
- found := false
- if HelpFlag.GetName() != "" {
- eachName(HelpFlag.GetName(), func(name string) {
- if c.GlobalBool(name) || c.Bool(name) {
- found = true
- }
- })
- }
- return found
-}
-
-func checkCommandHelp(c *Context, name string) bool {
- if c.Bool("h") || c.Bool("help") {
- ShowCommandHelp(c, name)
- return true
- }
-
- return false
-}
-
-func checkSubcommandHelp(c *Context) bool {
- if c.Bool("h") || c.Bool("help") {
- ShowSubcommandHelp(c)
- return true
- }
-
- return false
-}
-
-func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
- if !a.EnableBashCompletion {
- return false, arguments
- }
-
- pos := len(arguments) - 1
- lastArg := arguments[pos]
-
- if lastArg != "--"+BashCompletionFlag.GetName() {
- return false, arguments
- }
-
- return true, arguments[:pos]
-}
-
-func checkCompletions(c *Context) bool {
- if !c.shellComplete {
- return false
- }
-
- if args := c.Args(); args.Present() {
- name := args.First()
- if cmd := c.App.Command(name); cmd != nil {
- // let the command handle the completion
- return false
- }
- }
-
- ShowCompletions(c)
- return true
-}
-
-func checkCommandCompletions(c *Context, name string) bool {
- if !c.shellComplete {
- return false
- }
-
- ShowCommandCompletions(c, name)
- return true
-}
diff --git a/vendor/github.com/urfave/cli/sort.go b/vendor/github.com/urfave/cli/sort.go
deleted file mode 100644
index 23d1c2f77..000000000
--- a/vendor/github.com/urfave/cli/sort.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package cli
-
-import "unicode"
-
-// lexicographicLess compares strings alphabetically considering case.
-func lexicographicLess(i, j string) bool {
- iRunes := []rune(i)
- jRunes := []rune(j)
-
- lenShared := len(iRunes)
- if lenShared > len(jRunes) {
- lenShared = len(jRunes)
- }
-
- for index := 0; index < lenShared; index++ {
- ir := iRunes[index]
- jr := jRunes[index]
-
- if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr {
- return lir < ljr
- }
-
- if ir != jr {
- return ir < jr
- }
- }
-
- return i < j
-}