diff options
166 files changed, 2307 insertions, 18914 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 5f99b0490..0fa51be63 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -270,24 +270,6 @@ swagger_task: always: *binary_artifacts -endpoint_task: - name: "Test Endpoint" - alias: endpoint - only_if: *not_docs - depends_on: - - build - container: *smallcontainer - env: - <<: *stdenvars - TEST_FLAVOR: endpoint - TEST_ENVIRON: container - CTR_FQIN: ${FEDORA_CONTAINER_FQIN} - clone_script: *full_clone # build-cache not available to container tasks - setup_script: *setup - main_script: *main - always: *runner_stats - - # Check that all included go modules from other sources match # what is expected in `vendor/modules.txt` vs `go.mod`. vendor_task: @@ -328,11 +310,7 @@ alt_build_task: - env: ALT_NAME: 'Build Without CGO' - env: - ALT_NAME: 'Build varlink-API' - - env: ALT_NAME: 'Test build RPM' - - env: - ALT_NAME: 'Build varlink-binaries' setup_script: *setup main_script: *main always: *binary_artifacts @@ -633,7 +611,6 @@ success_task: - build - validate - bindings - - endpoint - swagger - vendor - alt_build diff --git a/.gitignore b/.gitignore index 9e444442a..208a71f37 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ /bin/ /brew /build/ -/cmd/podman/varlink/iopodman.go -/cmd/podman/varlink/ioprojectatomicpodman.go /conmon/ contrib/spec/podman.spec *.coverprofile @@ -21,7 +19,6 @@ coverprofile /_output/ /pause/pause.o pkg/api/swagger.yaml -/pkg/varlink/iopodman.go podman-remote*.zip podman*.tar.gz __pycache__ diff --git a/.golangci.yml b/.golangci.yml index b3466d8f9..da22b7602 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,9 +8,6 @@ run: - dependencies - test - pkg/spec - - pkg/varlink - - pkg/varlinkapi - - docs/varlink - vendor skip-files: - iopodman.go diff --git a/API.md b/API.md deleted file mode 100644 index 831367d8e..000000000 --- a/API.md +++ /dev/null @@ -1,2211 +0,0 @@ -# io.podman -Podman Service Interface and API description. The master version of this document can be found -in the [API.md](https://github.com/containers/podman/blob/master/API.md) file in the upstream libpod repository. -## Index - -[func Attach(name: string, detachKeys: string, start: bool) ](#Attach) - -[func AttachControl(name: string) ](#AttachControl) - -[func BuildImage(build: BuildInfo) MoreResponse](#BuildImage) - -[func BuildImageHierarchyMap(name: string) string](#BuildImageHierarchyMap) - -[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) MoreResponse](#Commit) - -[func ContainerArtifacts(name: string, artifactName: string) string](#ContainerArtifacts) - -[func ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) string](#ContainerCheckpoint) - -[func ContainerConfig(name: string) string](#ContainerConfig) - -[func ContainerExists(name: string) int](#ContainerExists) - -[func ContainerInspectData(name: string, size: bool) string](#ContainerInspectData) - -[func ContainerRestore(name: string, keep: bool, tcpEstablished: bool) string](#ContainerRestore) - -[func ContainerRunlabel(runlabel: Runlabel) ](#ContainerRunlabel) - -[func ContainerStateData(name: string) string](#ContainerStateData) - -[func CreateContainer(create: Create) string](#CreateContainer) - -[func CreateFromCC(in: []string) string](#CreateFromCC) - -[func CreatePod(create: PodCreate) string](#CreatePod) - -[func DeleteStoppedContainers() []string](#DeleteStoppedContainers) - -[func DeleteUnusedImages() []string](#DeleteUnusedImages) - -[func Diff(name: string) DiffInfo](#Diff) - -[func EvictContainer(name: string, removeVolumes: bool) string](#EvictContainer) - -[func ExecContainer(opts: ExecOpts) ](#ExecContainer) - -[func ExportContainer(name: string, path: string) string](#ExportContainer) - -[func ExportImage(name: string, destination: string, compress: bool, tags: []string) string](#ExportImage) - -[func GenerateKube(name: string, service: bool) KubePodService](#GenerateKube) - -[func GetAttachSockets(name: string) Sockets](#GetAttachSockets) - -[func GetContainer(id: string) Container](#GetContainer) - -[func GetContainerLogs(name: string) []string](#GetContainerLogs) - -[func GetContainerStats(name: string) ContainerStats](#GetContainerStats) - -[func GetContainerStatsWithHistory(previousStats: ContainerStats) ContainerStats](#GetContainerStatsWithHistory) - -[func GetContainersByContext(all: bool, latest: bool, args: []string) []string](#GetContainersByContext) - -[func GetContainersByStatus(status: []string) Container](#GetContainersByStatus) - -[func GetContainersLogs(names: []string, follow: bool, latest: bool, since: string, tail: int, timestamps: bool) LogLine](#GetContainersLogs) - -[func GetEvents(filter: []string, since: string, until: string) Event](#GetEvents) - -[func GetImage(id: string) Image](#GetImage) - -[func GetInfo() PodmanInfo](#GetInfo) - -[func GetLayersMapWithImageInfo() string](#GetLayersMapWithImageInfo) - -[func GetPod(name: string) ListPodData](#GetPod) - -[func GetPodStats(name: string) string, ContainerStats](#GetPodStats) - -[func GetPodsByContext(all: bool, latest: bool, args: []string) []string](#GetPodsByContext) - -[func GetPodsByStatus(statuses: []string) []string](#GetPodsByStatus) - -[func GetVersion() string, string, string, string, string, int](#GetVersion) - -[func GetVolumes(args: []string, all: bool) Volume](#GetVolumes) - -[func HealthCheckRun(nameOrID: string) string](#HealthCheckRun) - -[func HistoryImage(name: string) ImageHistory](#HistoryImage) - -[func ImageExists(name: string) int](#ImageExists) - -[func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave) - -[func ImageTree(name: string, whatRequires: bool) string](#ImageTree) - -[func ImagesPrune(all: bool, filter: []string) []string](#ImagesPrune) - -[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage) - -[func InitContainer(name: string) string](#InitContainer) - -[func InspectContainer(name: string) string](#InspectContainer) - -[func InspectImage(name: string) string](#InspectImage) - -[func InspectPod(name: string) string](#InspectPod) - -[func InspectVolume(name: string) string](#InspectVolume) - -[func KillContainer(name: string, signal: int) string](#KillContainer) - -[func KillPod(name: string, signal: int) string](#KillPod) - -[func ListContainerChanges(name: string) ContainerChanges](#ListContainerChanges) - -[func ListContainerMounts() map[string]](#ListContainerMounts) - -[func ListContainerProcesses(name: string, opts: []string) []string](#ListContainerProcesses) - -[func ListContainers() Container](#ListContainers) - -[func ListImages() Image](#ListImages) - -[func ListImagesWithFilters(filters: []string) Image](#ListImagesWithFilters) - -[func ListPods() ListPodData](#ListPods) - -[func LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) MoreResponse](#LoadImage) - -[func MountContainer(name: string) string](#MountContainer) - -[func PauseContainer(name: string) string](#PauseContainer) - -[func PausePod(name: string) string](#PausePod) - -[func PodStateData(name: string) string](#PodStateData) - -[func Ps(opts: PsOpts) PsContainer](#Ps) - -[func PullImage(name: string, creds: AuthConfig) MoreResponse](#PullImage) - -[func PushImage(name: string, tag: string, compress: bool, format: string, removeSignatures: bool, signBy: string) MoreResponse](#PushImage) - -[func ReceiveFile(path: string, delete: bool) int](#ReceiveFile) - -[func RemoveContainer(name: string, force: bool, removeVolumes: bool) string](#RemoveContainer) - -[func RemoveImage(name: string, force: bool) string](#RemoveImage) - -[func RemoveImageWithResponse(name: string, force: bool) RemoveImageResponse](#RemoveImageWithResponse) - -[func RemovePod(name: string, force: bool) string](#RemovePod) - -[func Reset() ](#Reset) - -[func RestartContainer(name: string, timeout: int) string](#RestartContainer) - -[func RestartPod(name: string) string](#RestartPod) - -[func SearchImages(query: string, limit: ?int, filter: ImageSearchFilter) ImageSearchResult](#SearchImages) - -[func SendFile(type: string, length: int) string](#SendFile) - -[func Spec(name: string) string](#Spec) - -[func StartContainer(name: string) string](#StartContainer) - -[func StartPod(name: string) string](#StartPod) - -[func StopContainer(name: string, timeout: int) string](#StopContainer) - -[func StopPod(name: string, timeout: int) string](#StopPod) - -[func TagImage(name: string, tagged: string) string](#TagImage) - -[func Top(nameOrID: string, descriptors: []string) []string](#Top) - -[func TopPod(pod: string, latest: bool, descriptors: []string) []string](#TopPod) - -[func UnmountContainer(name: string, force: bool) ](#UnmountContainer) - -[func UnpauseContainer(name: string) string](#UnpauseContainer) - -[func UnpausePod(name: string) string](#UnpausePod) - -[func UntagImage(name: string, tag: string) string](#UntagImage) - -[func VolumeCreate(options: VolumeCreateOpts) string](#VolumeCreate) - -[func VolumeRemove(options: VolumeRemoveOpts) []string, map[string]](#VolumeRemove) - -[func VolumesPrune() []string, []string](#VolumesPrune) - -[func WaitContainer(name: string, interval: int) int](#WaitContainer) - -[type AuthConfig](#AuthConfig) - -[type BuildInfo](#BuildInfo) - -[type BuildOptions](#BuildOptions) - -[type Container](#Container) - -[type ContainerChanges](#ContainerChanges) - -[type ContainerMount](#ContainerMount) - -[type ContainerNameSpace](#ContainerNameSpace) - -[type ContainerPortMappings](#ContainerPortMappings) - -[type ContainerStats](#ContainerStats) - -[type Create](#Create) - -[type DiffInfo](#DiffInfo) - -[type Event](#Event) - -[type ExecOpts](#ExecOpts) - -[type Image](#Image) - -[type ImageHistory](#ImageHistory) - -[type ImageSaveOptions](#ImageSaveOptions) - -[type ImageSearchFilter](#ImageSearchFilter) - -[type ImageSearchResult](#ImageSearchResult) - -[type InfoDistribution](#InfoDistribution) - -[type InfoGraphStatus](#InfoGraphStatus) - -[type InfoHost](#InfoHost) - -[type InfoPodmanBinary](#InfoPodmanBinary) - -[type InfoRegistry](#InfoRegistry) - -[type InfoStore](#InfoStore) - -[type KubePodService](#KubePodService) - -[type ListPodContainerInfo](#ListPodContainerInfo) - -[type ListPodData](#ListPodData) - -[type LogLine](#LogLine) - -[type MoreResponse](#MoreResponse) - -[type NotImplemented](#NotImplemented) - -[type PodContainerErrorData](#PodContainerErrorData) - -[type PodCreate](#PodCreate) - -[type PodmanInfo](#PodmanInfo) - -[type PsContainer](#PsContainer) - -[type PsOpts](#PsOpts) - -[type RemoveImageResponse](#RemoveImageResponse) - -[type Runlabel](#Runlabel) - -[type Sockets](#Sockets) - -[type StringResponse](#StringResponse) - -[type Volume](#Volume) - -[type VolumeCreateOpts](#VolumeCreateOpts) - -[type VolumeRemoveOpts](#VolumeRemoveOpts) - -[error ContainerNotFound](#ContainerNotFound) - -[error ErrCtrStopped](#ErrCtrStopped) - -[error ErrRequiresCgroupsV2ForRootless](#ErrRequiresCgroupsV2ForRootless) - -[error ErrorOccurred](#ErrorOccurred) - -[error ImageNotFound](#ImageNotFound) - -[error InvalidState](#InvalidState) - -[error NoContainerRunning](#NoContainerRunning) - -[error NoContainersInPod](#NoContainersInPod) - -[error PodContainerError](#PodContainerError) - -[error PodNotFound](#PodNotFound) - -[error RuntimeError](#RuntimeError) - -[error VolumeNotFound](#VolumeNotFound) - -[error WantsMoreRequired](#WantsMoreRequired) - -## Methods -### <a name="Attach"></a>func Attach -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Attach(name: [string](https://godoc.org/builtin#string), detachKeys: [string](https://godoc.org/builtin#string), start: [bool](https://godoc.org/builtin#bool)) </div> -Attach takes the name or ID of a container and sets up the ability to remotely attach to its console. The start -bool is whether you wish to start the container in question first. -### <a name="AttachControl"></a>func AttachControl -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method AttachControl(name: [string](https://godoc.org/builtin#string)) </div> - -### <a name="BuildImage"></a>func BuildImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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 -contextDir tarball path, the 'dockerfiles' path, and 'output' option in the BuildInfo structure. The 'output' -options is the name of the of the resulting build. It will return a [MoreResponse](#MoreResponse) structure -that contains the build logs and resulting image ID. -#### Example -~~~ -$ sudo varlink call -m unix:///run/podman/io.podman/io.podman.BuildImage '{"build":{"contextDir":"/tmp/t/context.tar","dockerfiles":["Dockerfile"], "output":"foobar"}}' -{ - "image": { - "id": "", - "logs": [ - "STEP 1: FROM alpine\n" - ] - } -} -{ - "image": { - "id": "", - "logs": [ - "STEP 2: COMMIT foobar\n" - ] - } -} -{ - "image": { - "id": "", - "logs": [ - "b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9\n" - ] - } -} -{ - "image": { - "id": "b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9", - "logs": [] - } -} -~~~ -### <a name="BuildImageHierarchyMap"></a>func BuildImageHierarchyMap -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method BuildImageHierarchyMap(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -BuildImageHierarchyMap is for the development of Podman and should not be used. -### <a name="Commit"></a>func Commit -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Commit(name: [string](https://godoc.org/builtin#string), image_name: [string](https://godoc.org/builtin#string), changes: [[]string](#[]string), author: [string](https://godoc.org/builtin#string), message: [string](https://godoc.org/builtin#string), pause: [bool](https://godoc.org/builtin#bool), manifestType: [string](https://godoc.org/builtin#string)) [MoreResponse](#MoreResponse)</div> -Commit, creates an image from an existing container. It requires the name or -ID of the container as well as the resulting image name. Optionally, you can define an author and message -to be added to the resulting image. You can also define changes to the resulting image for the following -attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOLUME, and WORKDIR_. To pause the -container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot -be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise, -the resulting image's ID will be returned as a string inside a MoreResponse. -### <a name="ContainerArtifacts"></a>func ContainerArtifacts -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerArtifacts(name: [string](https://godoc.org/builtin#string), artifactName: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -ContainerArtifacts returns a container's artifacts in string form. This call is for -development of Podman only and generally should not be used. -### <a name="ContainerCheckpoint"></a>func ContainerCheckpoint -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerCheckpoint(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), leaveRunning: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container -ID. On successful checkpoint, the id of the checkpointed container is returned. -### <a name="ContainerConfig"></a>func ContainerConfig -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerConfig(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -ContainerConfig returns a container's config in string form. This call is for -development of Podman only and generally should not be used. -### <a name="ContainerExists"></a>func ContainerExists -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerExists(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div> -ContainerExists takes a full or partial container ID or name and returns an int as to -whether the container exists in local storage. A result of 0 means the container does -exists; whereas a result of 1 means it could not be found. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ContainerExists '{"name": "flamboyant_payne"}'{ - "exists": 0 -} -~~~ -### <a name="ContainerInspectData"></a>func ContainerInspectData -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerInspectData(name: [string](https://godoc.org/builtin#string), size: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -ContainerInspectData returns a container's inspect data in string form. This call is for -development of Podman only and generally should not be used. -### <a name="ContainerRestore"></a>func ContainerRestore -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerRestore(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -ContainerRestore restores a container that has been checkpointed. The container to be restored can -be identified by its name or full/partial container ID. A successful restore will result in the return -of the container's ID. -### <a name="ContainerRunlabel"></a>func ContainerRunlabel -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerRunlabel(runlabel: [Runlabel](#Runlabel)) </div> -ContainerRunlabel runs executes a command as described by a given container image label. -### <a name="ContainerStateData"></a>func ContainerStateData -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ContainerStateData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -ContainerStateData returns a container's state config in string form. This call is for -development of Podman only and generally should not be used. -### <a name="CreateContainer"></a>func CreateContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method CreateContainer(create: [Create](#Create)) [string](https://godoc.org/builtin#string)</div> -CreateContainer creates a new container from an image. It uses a [Create](#Create) type for input. -### <a name="CreateFromCC"></a>func CreateFromCC -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method CreateFromCC(in: [[]string](#[]string)) [string](https://godoc.org/builtin#string)</div> -This call is for the development of Podman only and should not be used. -### <a name="CreatePod"></a>func CreatePod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method CreatePod(create: [PodCreate](#PodCreate)) [string](https://godoc.org/builtin#string)</div> -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. -#### Example -~~~ -$ varlink call unix:/run/podman/io.podman/io.podman.CreatePod '{"create": {"name": "test"}}' -{ - "pod": "b05dee7bd4ccfee688099fe1588a7a898d6ddd6897de9251d4671c9b0feacb2a" -} -# $ varlink call unix:/run/podman/io.podman/io.podman.CreatePod '{"create": {"infra": true, "share": ["ipc", "net", "uts"]}}' -{ - "pod": "d7697449a8035f613c1a8891286502aca68fff7d5d49a85279b3bda229af3b28" -} -~~~ -### <a name="DeleteStoppedContainers"></a>func DeleteStoppedContainers -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method DeleteStoppedContainers() [[]string](#[]string)</div> -DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted -container IDs. See also [RemoveContainer](RemoveContainer). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteStoppedContainers -{ - "containers": [ - "451410b931d00def8aa9b4f8084e4d4a39e5e04ea61f358cf53a5cf95afcdcee", - "8b60f754a3e01389494a9581ade97d35c2765b6e2f19acd2d3040c82a32d1bc0", - "cf2e99d4d3cad6073df199ed32bbe64b124f3e1aba6d78821aa8460e70d30084", - "db901a329587312366e5ecff583d08f0875b4b79294322df67d90fc6eed08fc1" - ] -} -~~~ -### <a name="DeleteUnusedImages"></a>func DeleteUnusedImages -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method DeleteUnusedImages() [[]string](#[]string)</div> -DeleteUnusedImages deletes any images not associated with a container. The IDs of the deleted images are returned -in a string array. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages -{ - "images": [ - "166ea6588079559c724c15223f52927f514f73dd5c5cf2ae2d143e3b2e6e9b52", - "da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e", - "3ef70f7291f47dfe2b82931a993e16f5a44a0e7a68034c3e0e086d77f5829adc", - "59788edf1f3e78cd0ebe6ce1446e9d10788225db3dedcfd1a59f764bad2b2690" - ] -} -~~~ -### <a name="Diff"></a>func Diff -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Diff(name: [string](https://godoc.org/builtin#string)) [DiffInfo](#DiffInfo)</div> -Diff returns a diff between libpod objects -### <a name="EvictContainer"></a>func EvictContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method EvictContainer(name: [string](https://godoc.org/builtin#string), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -EvictContainer requires the name or ID of a container as well as a boolean that -indicates to remove builtin volumes. Upon successful eviction of the container, -its ID is returned. If the container cannot be found by name or ID, -a [ContainerNotFound](#ContainerNotFound) error will be returned. -See also [RemoveContainer](RemoveContainer). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}' -{ - "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" -} -~~~ -### <a name="ExecContainer"></a>func ExecContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ExecContainer(opts: [ExecOpts](#ExecOpts)) </div> -ExecContainer executes a command in the given container. -### <a name="ExportContainer"></a>func ExportContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ExportContainer(name: [string](https://godoc.org/builtin#string), path: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -ExportContainer creates an image from a container. It takes the name or ID of a container and a -path representing the target tarfile. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) -error will be returned. -The return value is the written tarfile. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ExportContainer '{"name": "flamboyant_payne", "path": "/tmp/payne.tar" }' -{ - "tarfile": "/tmp/payne.tar" -} -~~~ -### <a name="ExportImage"></a>func ExportImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ExportImage(name: [string](https://godoc.org/builtin#string), destination: [string](https://godoc.org/builtin#string), compress: [bool](https://godoc.org/builtin#bool), tags: [[]string](#[]string)) [string](https://godoc.org/builtin#string)</div> -ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also -a boolean option to force compression. It also takes in a string array of tags to be able to save multiple -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(name: [string](https://godoc.org/builtin#string), service: [bool](https://godoc.org/builtin#bool)) [KubePodService](#KubePodService)</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="GetAttachSockets"></a>func GetAttachSockets -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetAttachSockets(name: [string](https://godoc.org/builtin#string)) [Sockets](#Sockets)</div> -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 -second is a "control" socket where things like resizing the TTY events are sent. If the container cannot be -found, a [ContainerNotFound](#ContainerNotFound) error will be returned. -#### Example -~~~ -$ varlink call -m unix:/run/io.podman/io.podman.GetAttachSockets '{"name": "b7624e775431219161"}' -{ - "sockets": { - "container_id": "b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca", - "control_socket": "/var/lib/containers/storage/overlay-containers/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/userdata/ctl", - "io_socket": "/var/run/libpod/socket/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/attach" - } -} -~~~ -### <a name="GetContainer"></a>func GetContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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;"> - -method GetContainerLogs(name: [string](https://godoc.org/builtin#string)) [[]string](#[]string)</div> -GetContainerLogs takes a name or ID of a container and returns the logs of that container. -If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) error will be returned. -The container logs are returned as an array of strings. GetContainerLogs will honor the streaming -capability of varlink if the client invokes it. -### <a name="GetContainerStats"></a>func GetContainerStats -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetContainerStats(name: [string](https://godoc.org/builtin#string)) [ContainerStats](#ContainerStats)</div> -GetContainerStats takes the name or ID of a container and returns a single ContainerStats structure which -contains attributes like memory and cpu usage. If the container cannot be found, a -[ContainerNotFound](#ContainerNotFound) error will be returned. If the container is not running, a [NoContainerRunning](#NoContainerRunning) -error will be returned -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.GetContainerStats '{"name": "c33e4164f384"}' -{ - "container": { - "block_input": 0, - "block_output": 0, - "cpu": 2.571123918839990154678e-08, - "cpu_nano": 49037378, - "id": "c33e4164f384aa9d979072a63319d66b74fd7a128be71fa68ede24f33ec6cfee", - "mem_limit": 33080606720, - "mem_perc": 2.166828456524753747370e-03, - "mem_usage": 716800, - "name": "competent_wozniak", - "net_input": 768, - "net_output": 5910, - "pids": 1, - "system_nano": 10000000 - } -} -~~~ -### <a name="GetContainerStatsWithHistory"></a>func GetContainerStatsWithHistory -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetContainerStatsWithHistory(previousStats: [ContainerStats](#ContainerStats)) [ContainerStats](#ContainerStats)</div> -GetContainerStatsWithHistory takes a previous set of container statistics and uses libpod functions -to calculate the containers statistics based on current and previous measurements. -### <a name="GetContainersByContext"></a>func GetContainersByContext -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetContainersByContext(all: [bool](https://godoc.org/builtin#bool), latest: [bool](https://godoc.org/builtin#bool), args: [[]string](#[]string)) [[]string](#[]string)</div> -GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of -container names. The definition of latest container means the latest by creation date. In a multi- -user environment, results might differ from what you expect. -### <a name="GetContainersByStatus"></a>func GetContainersByStatus -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetContainersByStatus(status: [[]string](#[]string)) [Container](#Container)</div> - -### <a name="GetContainersLogs"></a>func GetContainersLogs -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetContainersLogs(names: [[]string](#[]string), follow: [bool](https://godoc.org/builtin#bool), latest: [bool](https://godoc.org/builtin#bool), since: [string](https://godoc.org/builtin#string), tail: [int](https://godoc.org/builtin#int), timestamps: [bool](https://godoc.org/builtin#bool)) [LogLine](#LogLine)</div> - -### <a name="GetEvents"></a>func GetEvents -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetEvents(filter: [[]string](#[]string), since: [string](https://godoc.org/builtin#string), until: [string](https://godoc.org/builtin#string)) [Event](#Event)</div> -GetEvents returns known libpod events filtered by the options provided. -### <a name="GetImage"></a>func GetImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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;"> - -method GetInfo() [PodmanInfo](#PodmanInfo)</div> -GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats, -build information of Podman, and system-wide registries. -### <a name="GetLayersMapWithImageInfo"></a>func GetLayersMapWithImageInfo -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetLayersMapWithImageInfo() [string](https://godoc.org/builtin#string)</div> -GetLayersMapWithImageInfo is for the development of Podman and should not be used. -### <a name="GetPod"></a>func GetPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetPod(name: [string](https://godoc.org/builtin#string)) [ListPodData](#ListPodData)</div> -GetPod takes a name or ID of a pod and returns single [ListPodData](#ListPodData) -structure. A [PodNotFound](#PodNotFound) error will be returned if the pod cannot be found. -See also [ListPods](ListPods). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.GetPod '{"name": "foobar"}' -{ - "pod": { - "cgroup": "machine.slice", - "containersinfo": [ - { - "id": "00c130a45de0411f109f1a0cfea2e298df71db20fa939de5cab8b2160a36be45", - "name": "1840835294cf-infra", - "status": "running" - }, - { - "id": "49a5cce72093a5ca47c6de86f10ad7bb36391e2d89cef765f807e460865a0ec6", - "name": "upbeat_murdock", - "status": "running" - } - ], - "createdat": "2018-12-07 13:10:15.014139258 -0600 CST", - "id": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f", - "name": "foobar", - "numberofcontainers": "2", - "status": "Running" - } -} -~~~ -### <a name="GetPodStats"></a>func GetPodStats -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetPodStats(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string), [ContainerStats](#ContainerStats)</div> -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) -error will be returned. If the pod has no running containers associated with it, a [NoContainerRunning](#NoContainerRunning) -error will be returned. -#### Example -~~~ -$ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b508b6f12b11d8fe02e"}' -{ - "containers": [ - { - "block_input": 0, - "block_output": 0, - "cpu": 2.833470544016107524276e-08, - "cpu_nano": 54363072, - "id": "a64b51f805121fe2c5a3dc5112eb61d6ed139e3d1c99110360d08b58d48e4a93", - "mem_limit": 12276146176, - "mem_perc": 7.974359265237864966003e-03, - "mem_usage": 978944, - "name": "quirky_heisenberg", - "net_input": 866, - "net_output": 7388, - "pids": 1, - "system_nano": 20000000 - } - ], - "pod": "7f62b508b6f12b11d8fe02e0db4de6b9e43a7d7699b33a4fc0d574f6e82b4ebd" -} -~~~ -### <a name="GetPodsByContext"></a>func GetPodsByContext -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetPodsByContext(all: [bool](https://godoc.org/builtin#bool), latest: [bool](https://godoc.org/builtin#bool), args: [[]string](#[]string)) [[]string](#[]string)</div> -GetPodsByContext allows you to get a list pod ids depending on all, latest, or a list of -pod names. The definition of latest pod means the latest by creation date. In a multi- -user environment, results might differ from what you expect. -### <a name="GetPodsByStatus"></a>func GetPodsByStatus -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method GetPodsByStatus(statuses: [[]string](#[]string)) [[]string](#[]string)</div> -GetPodsByStatus searches for pods whose status is included in statuses -### <a name="GetVersion"></a>func GetVersion -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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="HealthCheckRun"></a>func HealthCheckRun -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method HealthCheckRun(nameOrID: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -HealthCheckRun executes defined container's healthcheck command -and returns the container's health status. -### <a name="HistoryImage"></a>func HistoryImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method HistoryImage(name: [string](https://godoc.org/builtin#string)) [ImageHistory](#ImageHistory)</div> -HistoryImage takes the name or ID of an image and returns information about its history and layers. The returned -history is in the form of an array of ImageHistory structures. If the image cannot be found, an -[ImageNotFound](#ImageNotFound) error is returned. -### <a name="ImageExists"></a>func ImageExists -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ImageExists(name: [string](https://godoc.org/builtin#string)) [int](https://godoc.org/builtin#int)</div> -ImageExists talks a full or partial image ID or name and returns an int as to whether -the image exists in local storage. An int result of 0 means the image does exist in -local storage; whereas 1 indicates the image does not exists in local storage. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "imageddoesntexist"}' -{ - "exists": 1 -} -~~~ -### <a name="ImageSave"></a>func ImageSave -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ImageSave(options: [ImageSaveOptions](#ImageSaveOptions)) [MoreResponse](#MoreResponse)</div> -ImageSave allows you to save an image from the local image storage to a tarball -### <a name="ImageTree"></a>func ImageTree -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ImageTree(name: [string](https://godoc.org/builtin#string), whatRequires: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -ImageTree returns the image tree for the provided image name or ID -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}' -{ - "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n" -} -~~~ -### <a name="ImagesPrune"></a>func ImagesPrune -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ImagesPrune(all: [bool](https://godoc.org/builtin#bool), filter: [[]string](#[]string)) [[]string](#[]string)</div> -ImagesPrune removes all unused images from the local store. Upon successful pruning, -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), 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="InitContainer"></a>func InitContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method InitContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -InitContainer initializes the given container. It accepts a container name or -ID, and will initialize the container matching that ID if possible, and error -if not. Containers can only be initialized when they are in the Created or -Exited states. Initialization prepares a container to be started, but does not -start the container. It is intended to be used to debug a container's state -prior to starting it. -### <a name="InspectContainer"></a>func InspectContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method InspectContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -InspectContainer data takes a name or ID of a container returns the inspection -data in string format. You can then serialize the string into JSON. A [ContainerNotFound](#ContainerNotFound) -error will be returned if the container cannot be found. See also [InspectImage](#InspectImage). -### <a name="InspectImage"></a>func InspectImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method InspectImage(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -InspectImage takes the name or ID of an image and returns a string representation of data associated with the -mage. You must serialize the string into JSON to use it further. An [ImageNotFound](#ImageNotFound) error will -be returned if the image cannot be found. -### <a name="InspectPod"></a>func InspectPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method InspectPod(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -InspectPod takes the name or ID of an image and returns a string representation of data associated with the -pod. You must serialize the string into JSON to use it further. A [PodNotFound](#PodNotFound) error will -be returned if the pod cannot be found. -### <a name="InspectVolume"></a>func InspectVolume -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method InspectVolume(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -InspectVolume inspects a single volume. Returns inspect JSON in the form of a -string. -### <a name="KillContainer"></a>func KillContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method KillContainer(name: [string](https://godoc.org/builtin#string), signal: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div> -KillContainer takes the name or ID of a container as well as a signal to be applied to the container. Once the -container has been killed, the container's ID is returned. If the container cannot be found, a -[ContainerNotFound](#ContainerNotFound) error is returned. See also [StopContainer](StopContainer). -### <a name="KillPod"></a>func KillPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method KillPod(name: [string](https://godoc.org/builtin#string), signal: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div> -KillPod takes the name or ID of a pod as well as a signal to be applied to the pod. If the pod cannot be found, a -[PodNotFound](#PodNotFound) error is returned. -Containers in a pod are killed independently. If there is an error killing one container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was killed with no errors, the pod ID is returned. -See also [StopPod](StopPod). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.KillPod '{"name": "foobar", "signal": 15}' -{ - "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" -} -~~~ -### <a name="ListContainerChanges"></a>func ListContainerChanges -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ListContainerChanges(name: [string](https://godoc.org/builtin#string)) [ContainerChanges](#ContainerChanges)</div> -ListContainerChanges takes a name or ID of a container and returns changes between the container and -its base image. It returns a struct of changed, deleted, and added path names. -### <a name="ListContainerMounts"></a>func ListContainerMounts -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ListContainerMounts() [map[string]](#map[string])</div> -ListContainerMounts gathers all the mounted container mount points and returns them as an array -of strings -#### Example -~~~ -$ varlink call unix:/run/podman/io.podman/io.podman.ListContainerMounts -{ - "mounts": { - "04e4c255269ed2545e7f8bd1395a75f7949c50c223415c00c1d54bfa20f3b3d9": "/var/lib/containers/storage/overlay/a078925828f57e20467ca31cfca8a849210d21ec7e5757332b72b6924f441c17/merged", - "1d58c319f9e881a644a5122ff84419dccf6d138f744469281446ab243ef38924": "/var/lib/containers/storage/overlay/948fcf93f8cb932f0f03fd52e3180a58627d547192ffe3b88e0013b98ddcd0d2/merged" - } -} -~~~ -### <a name="ListContainerProcesses"></a>func ListContainerProcesses -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ListContainerProcesses(name: [string](https://godoc.org/builtin#string), opts: [[]string](#[]string)) [[]string](#[]string)</div> -ListContainerProcesses takes a name or ID of a container and returns the processes -running inside the container as array of strings. It will accept an array of string -arguments that represent ps options. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) -error will be returned. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ListContainerProcesses '{"name": "135d71b9495f", "opts": []}' -{ - "container": [ - " UID PID PPID C STIME TTY TIME CMD", - " 0 21220 21210 0 09:05 pts/0 00:00:00 /bin/sh", - " 0 21232 21220 0 09:05 pts/0 00:00:00 top", - " 0 21284 21220 0 09:05 pts/0 00:00:00 vi /etc/hosts" - ] -} -~~~ -### <a name="ListContainers"></a>func ListContainers -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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() [Image](#Image)</div> -ListImages returns information about the images that are currently in storage. -See also [InspectImage](#InspectImage). -### <a name="ListImagesWithFilters"></a>func ListImagesWithFilters -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ListImagesWithFilters(filters: [[]string](#[]string)) [Image](#Image)</div> -ListImagesWithFilters returns information about the images that are currently in storage -after one or more filters has been applied. -See also [InspectImage](#InspectImage). -### <a name="ListPods"></a>func ListPods -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method ListPods() [ListPodData](#ListPodData)</div> -ListPods returns a list of pods in no particular order. They are -returned as an array of ListPodData structs. See also [GetPod](#GetPod). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ListPods -{ - "pods": [ - { - "cgroup": "machine.slice", - "containersinfo": [ - { - "id": "00c130a45de0411f109f1a0cfea2e298df71db20fa939de5cab8b2160a36be45", - "name": "1840835294cf-infra", - "status": "running" - }, - { - "id": "49a5cce72093a5ca47c6de86f10ad7bb36391e2d89cef765f807e460865a0ec6", - "name": "upbeat_murdock", - "status": "running" - } - ], - "createdat": "2018-12-07 13:10:15.014139258 -0600 CST", - "id": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f", - "name": "foobar", - "numberofcontainers": "2", - "status": "Running" - }, - { - "cgroup": "machine.slice", - "containersinfo": [ - { - "id": "1ca4b7bbba14a75ba00072d4b705c77f3df87db0109afaa44d50cb37c04a477e", - "name": "784306f655c6-infra", - "status": "running" - } - ], - "createdat": "2018-12-07 13:09:57.105112457 -0600 CST", - "id": "784306f655c6200aea321dd430ba685e9b2cc1f7d7528a72f3ff74ffb29485a2", - "name": "nostalgic_pike", - "numberofcontainers": "1", - "status": "Running" - } - ] -} -~~~ -### <a name="LoadImage"></a>func LoadImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method LoadImage(name: [string](https://godoc.org/builtin#string), inputFile: [string](https://godoc.org/builtin#string), quiet: [bool](https://godoc.org/builtin#bool), deleteFile: [bool](https://godoc.org/builtin#bool)) [MoreResponse](#MoreResponse)</div> -LoadImage allows you to load an image into local storage from a tarball. -### <a name="MountContainer"></a>func MountContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method MountContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination -mount is returned as a string. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.MountContainer '{"name": "jolly_shannon"}'{ - "path": "/var/lib/containers/storage/overlay/419eeb04e783ea159149ced67d9fcfc15211084d65e894792a96bedfae0470ca/merged" -} -~~~ -### <a name="PauseContainer"></a>func PauseContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method PauseContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -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. -See also [UnpauseContainer](#UnpauseContainer). -### <a name="PausePod"></a>func PausePod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method PausePod(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -PausePod takes the name or ID of a pod and pauses the running containers associated with it. If the pod cannot be found, -a [PodNotFound](#PodNotFound) error will be returned. -Containers in a pod are paused independently. If there is an error pausing one container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was paused with no errors, the pod ID is returned. -See also [UnpausePod](#UnpausePod). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "foobar"}' -{ - "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" -} -~~~ -### <a name="PodStateData"></a>func PodStateData -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method PodStateData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -PodStateData returns inspectr level information of a given pod in string form. This call is for -development of Podman only and generally should not be used. -### <a name="Ps"></a>func Ps -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Ps(opts: [PsOpts](#PsOpts)) [PsContainer](#PsContainer)</div> - -### <a name="PullImage"></a>func PullImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method PullImage(name: [string](https://godoc.org/builtin#string), creds: [AuthConfig](#AuthConfig)) [MoreResponse](#MoreResponse)</div> -PullImage pulls an image from a repository to local storage. After a successful pull, the image id and logs -are returned as a [MoreResponse](#MoreResponse). This connection also will handle a WantsMores request to send -status as it occurs. -### <a name="PushImage"></a>func PushImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method PushImage(name: [string](https://godoc.org/builtin#string), tag: [string](https://godoc.org/builtin#string), 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 two input arguments: the name or ID of an image, the fully-qualified destination name of the image, -It will return an [ImageNotFound](#ImageNotFound) error if -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), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -RemoveContainer requires the name or ID of a container as well as a boolean that -indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean -indicating whether 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. -See also [EvictContainer](EvictContainer). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}' -{ - "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" -} -~~~ -### <a name="RemoveImage"></a>func RemoveImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method RemoveImage(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -RemoveImage takes the name or ID of an image as well as a boolean that determines if containers using that image -should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The -ID of the removed image is returned when complete. See also [DeleteUnusedImages](DeleteUnusedImages). -#### Example -~~~ -varlink call -m unix:/run/podman/io.podman/io.podman.RemoveImage '{"name": "registry.fedoraproject.org/fedora", "force": true}' -{ - "image": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e" -} -~~~ -### <a name="RemoveImageWithResponse"></a>func RemoveImageWithResponse -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method RemoveImageWithResponse(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [RemoveImageResponse](#RemoveImageResponse)</div> -RemoveImageWithResponse takes the name or ID of an image as well as a boolean that determines if containers using that image -should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The response is -in the form of a RemoveImageResponse . -### <a name="RemovePod"></a>func RemovePod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method RemovePod(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> -RemovePod takes the name or ID of a pod as well a boolean representing whether a running -container in the pod can be stopped and removed. If a pod has containers associated with it, and force is not true, -an error will occur. -If the pod cannot be found by name or ID, a [PodNotFound](#PodNotFound) error will be returned. -Containers in a pod are removed independently. If there is an error removing any container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was removed with no errors, the pod ID is returned. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.RemovePod '{"name": "62f4fd98cb57", "force": "true"}' -{ - "pod": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" -} -~~~ -### <a name="Reset"></a>func Reset -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Reset() </div> -Reset resets Podman back to its initial state. -Removes all Pods, Containers, Images and Volumes -### <a name="RestartContainer"></a>func RestartContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method RestartContainer(name: [string](https://godoc.org/builtin#string), timeout: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div> -RestartContainer will restart a running container given a container name or ID and timeout value. The timeout -value is the time before a forcible stop is used to stop the container. If the container cannot be found by -name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise, the ID of the -container will be returned. -### <a name="RestartPod"></a>func RestartPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method RestartPod(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -RestartPod will restart containers in a pod given a pod name or ID. Containers in -the pod that are running will be stopped, then all stopped containers will be run. -If the pod cannot be found by name or ID, a [PodNotFound](#PodNotFound) error will be returned. -Containers in a pod are restarted independently. If there is an error restarting one container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was restarted with no errors, the pod ID is returned. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.RestartPod '{"name": "135d71b9495f"}' -{ - "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6" -} -~~~ -### <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: [?int](#?int), filter: [ImageSearchFilter](#ImageSearchFilter)) [ImageSearchResult](#ImageSearchResult)</div> -SearchImages searches available registries for images that contain the -contents of "query" in their name. If "limit" is given, limits the amount of -search results per registry. -### <a name="SendFile"></a>func SendFile -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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="Spec"></a>func Spec -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Spec(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -Spec returns the oci spec for a container. This call is for development of Podman only and generally should not be used. -### <a name="StartContainer"></a>func StartContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method StartContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -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) -error will be returned. See also [CreateContainer](#CreateContainer). -### <a name="StartPod"></a>func StartPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method StartPod(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -StartPod starts containers in a pod. It takes the name or ID of pod. If the pod cannot be found, a [PodNotFound](#PodNotFound) -error will be returned. Containers in a pod are started independently. If there is an error starting one container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was started with no errors, the pod ID is returned. -See also [CreatePod](#CreatePod). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.StartPod '{"name": "135d71b9495f"}' -{ - "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6", -} -~~~ -### <a name="StopContainer"></a>func StopContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method StopContainer(name: [string](https://godoc.org/builtin#string), timeout: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div> -StopContainer stops a container given a timeout. It takes the name or ID of a container as well as a -timeout value. The timeout value the time before a forcible stop to the container is applied. It -returns the container ID once stopped. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) -error will be returned instead. See also [KillContainer](KillContainer). -#### Error -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.StopContainer '{"name": "135d71b9495f", "timeout": 5}' -{ - "container": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6" -} -~~~ -### <a name="StopPod"></a>func StopPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method StopPod(name: [string](https://godoc.org/builtin#string), timeout: [int](https://godoc.org/builtin#int)) [string](https://godoc.org/builtin#string)</div> -StopPod stops containers in a pod. It takes the name or ID of a pod and a timeout. -If the pod cannot be found, a [PodNotFound](#PodNotFound) error will be returned instead. -Containers in a pod are stopped independently. If there is an error stopping one container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was stopped with no errors, the pod ID is returned. -See also [KillPod](KillPod). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.StopPod '{"name": "135d71b9495f"}' -{ - "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6" -} -~~~ -### <a name="TagImage"></a>func TagImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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="Top"></a>func Top -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method Top(nameOrID: [string](https://godoc.org/builtin#string), descriptors: [[]string](#[]string)) [[]string](#[]string)</div> - -### <a name="TopPod"></a>func TopPod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method TopPod(pod: [string](https://godoc.org/builtin#string), latest: [bool](https://godoc.org/builtin#bool), descriptors: [[]string](#[]string)) [[]string](#[]string)</div> - -### <a name="UnmountContainer"></a>func UnmountContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method UnmountContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool)) </div> -UnmountContainer umounts a container by its name or full/partial container ID. -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.UnmountContainer '{"name": "jolly_shannon", "force": false}' -{} -~~~ -### <a name="UnpauseContainer"></a>func UnpauseContainer -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method UnpauseContainer(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -UnpauseContainer takes the name or ID of container and unpauses a paused container. If the container cannot be -found, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise the ID of the container is returned. -See also [PauseContainer](#PauseContainer). -### <a name="UnpausePod"></a>func UnpausePod -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method UnpausePod(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -UnpausePod takes the name or ID of a pod and unpauses the paused containers associated with it. If the pod cannot be -found, a [PodNotFound](#PodNotFound) error will be returned. -Containers in a pod are unpaused independently. If there is an error unpausing one container, the ID of those containers -will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -If the pod was unpaused with no errors, the pod ID is returned. -See also [PausePod](#PausePod). -#### Example -~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.UnpausePod '{"name": "foobar"}' -{ - "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" -} -~~~ -### <a name="UntagImage"></a>func UntagImage -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -method UntagImage(name: [string](https://godoc.org/builtin#string), tag: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> -UntagImage takes the name or ID of an image in local storage as well as the -tag name to be removed. 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="VolumeCreate"></a>func VolumeCreate -<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> - -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), [map[string]](#map[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;"> - -method WaitContainer(name: [string](https://godoc.org/builtin#string), interval: [int](https://godoc.org/builtin#int)) [int](https://godoc.org/builtin#int)</div> -WaitContainer takes the name or ID of a container and waits the given interval in milliseconds 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. -## Types -### <a name="AuthConfig"></a>type AuthConfig - - - -username [string](https://godoc.org/builtin#string) - -password [string](https://godoc.org/builtin#string) -### <a name="BuildInfo"></a>type BuildInfo - -BuildInfo is used to describe user input for building images - -architecture [string](https://godoc.org/builtin#string) - -addCapabilities [[]string](#[]string) - -additionalTags [[]string](#[]string) - -annotations [[]string](#[]string) - -buildArgs [map[string]](#map[string]) - -buildOptions [BuildOptions](#BuildOptions) - -cniConfigDir [string](https://godoc.org/builtin#string) - -cniPluginDir [string](https://godoc.org/builtin#string) - -compression [string](https://godoc.org/builtin#string) - -contextDir [string](https://godoc.org/builtin#string) - -defaultsMountFilePath [string](https://godoc.org/builtin#string) - -devices [[]string](#[]string) - -dockerfiles [[]string](#[]string) - -dropCapabilities [[]string](#[]string) - -err [string](https://godoc.org/builtin#string) - -forceRmIntermediateCtrs [bool](https://godoc.org/builtin#bool) - -iidfile [string](https://godoc.org/builtin#string) - -label [[]string](#[]string) - -layers [bool](https://godoc.org/builtin#bool) - -nocache [bool](https://godoc.org/builtin#bool) - -os [string](https://godoc.org/builtin#string) - -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) - -signBy [string](https://godoc.org/builtin#string) - -squash [bool](https://godoc.org/builtin#bool) - -target [string](https://godoc.org/builtin#string) - -transientMounts [[]string](#[]string) -### <a name="BuildOptions"></a>type BuildOptions - -BuildOptions are are used to describe describe physical attributes of the build - -addHosts [[]string](#[]string) - -cgroupParent [string](https://godoc.org/builtin#string) - -cpuPeriod [int](https://godoc.org/builtin#int) - -cpuQuota [int](https://godoc.org/builtin#int) - -cpuShares [int](https://godoc.org/builtin#int) - -cpusetCpus [string](https://godoc.org/builtin#string) - -cpusetMems [string](https://godoc.org/builtin#string) - -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 - - - -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 - -changed [[]string](#[]string) - -added [[]string](#[]string) - -deleted [[]string](#[]string) -### <a name="ContainerMount"></a>type ContainerMount - -ContainerMount describes the struct for mounts in a container - -destination [string](https://godoc.org/builtin#string) - -type [string](https://godoc.org/builtin#string) - -source [string](https://godoc.org/builtin#string) - -options [[]string](#[]string) -### <a name="ContainerNameSpace"></a>type ContainerNameSpace - -ContainerNamespace describes the namespace structure for an existing container - -user [string](https://godoc.org/builtin#string) - -uts [string](https://godoc.org/builtin#string) - -pidns [string](https://godoc.org/builtin#string) - -pid [string](https://godoc.org/builtin#string) - -cgroup [string](https://godoc.org/builtin#string) - -net [string](https://godoc.org/builtin#string) - -mnt [string](https://godoc.org/builtin#string) - -ipc [string](https://godoc.org/builtin#string) -### <a name="ContainerPortMappings"></a>type ContainerPortMappings - -ContainerPortMappings describes the struct for portmappings in an existing container - -host_port [string](https://godoc.org/builtin#string) - -host_ip [string](https://godoc.org/builtin#string) - -protocol [string](https://godoc.org/builtin#string) - -container_port [string](https://godoc.org/builtin#string) -### <a name="ContainerStats"></a>type ContainerStats - -ContainerStats is the return struct for the stats of a container - -id [string](https://godoc.org/builtin#string) - -name [string](https://godoc.org/builtin#string) - -cpu [float](https://golang.org/src/builtin/builtin.go#L58) - -cpu_nano [int](https://godoc.org/builtin#int) - -system_nano [int](https://godoc.org/builtin#int) - -mem_usage [int](https://godoc.org/builtin#int) - -mem_limit [int](https://godoc.org/builtin#int) - -mem_perc [float](https://golang.org/src/builtin/builtin.go#L58) - -net_input [int](https://godoc.org/builtin#int) - -net_output [int](https://godoc.org/builtin#int) - -block_output [int](https://godoc.org/builtin#int) - -block_input [int](https://godoc.org/builtin#int) - -pids [int](https://godoc.org/builtin#int) -### <a name="Create"></a>type Create - -Create is an input structure for creating containers. -args[0] is the image name or id -args[1-] are the new commands if changed - -args [[]string](#[]string) - -addHost [?[]string](#?[]string) - -annotation [?[]string](#?[]string) - -attach [?[]string](#?[]string) - -blkioWeight [?string](#?string) - -blkioWeightDevice [?[]string](#?[]string) - -capAdd [?[]string](#?[]string) - -capDrop [?[]string](#?[]string) - -cgroupParent [?string](#?string) - -cidFile [?string](#?string) - -conmonPidfile [?string](#?string) - -command [?[]string](#?[]string) - -cpuPeriod [?int](#?int) - -cpuQuota [?int](#?int) - -cpuRtPeriod [?int](#?int) - -cpuRtRuntime [?int](#?int) - -cpuShares [?int](#?int) - -cpus [?float](#?float) - -cpuSetCpus [?string](#?string) - -cpuSetMems [?string](#?string) - -detach [?bool](#?bool) - -detachKeys [?string](#?string) - -device [?[]string](#?[]string) - -deviceReadBps [?[]string](#?[]string) - -deviceReadIops [?[]string](#?[]string) - -deviceWriteBps [?[]string](#?[]string) - -deviceWriteIops [?[]string](#?[]string) - -dns [?[]string](#?[]string) - -dnsOpt [?[]string](#?[]string) - -dnsSearch [?[]string](#?[]string) - -dnsServers [?[]string](#?[]string) - -entrypoint [?string](#?string) - -env [?[]string](#?[]string) - -envFile [?[]string](#?[]string) - -expose [?[]string](#?[]string) - -gidmap [?[]string](#?[]string) - -groupadd [?[]string](#?[]string) - -healthcheckCommand [?string](#?string) - -healthcheckInterval [?string](#?string) - -healthcheckRetries [?int](#?int) - -healthcheckStartPeriod [?string](#?string) - -healthcheckTimeout [?string](#?string) - -hostname [?string](#?string) - -imageVolume [?string](#?string) - -init [?bool](#?bool) - -initPath [?string](#?string) - -interactive [?bool](#?bool) - -ip [?string](#?string) - -ipc [?string](#?string) - -kernelMemory [?string](#?string) - -label [?[]string](#?[]string) - -labelFile [?[]string](#?[]string) - -logDriver [?string](#?string) - -logOpt [?[]string](#?[]string) - -macAddress [?string](#?string) - -memory [?string](#?string) - -memoryReservation [?string](#?string) - -memorySwap [?string](#?string) - -memorySwappiness [?int](#?int) - -name [?string](#?string) - -network [?string](#?string) - -noHosts [?bool](#?bool) - -oomKillDisable [?bool](#?bool) - -oomScoreAdj [?int](#?int) - -overrideArch [?string](#?string) - -overrideOS [?string](#?string) - -pid [?string](#?string) - -pidsLimit [?int](#?int) - -pod [?string](#?string) - -privileged [?bool](#?bool) - -publish [?[]string](#?[]string) - -publishAll [?bool](#?bool) - -pull [?string](#?string) - -quiet [?bool](#?bool) - -readonly [?bool](#?bool) - -readonlytmpfs [?bool](#?bool) - -restart [?string](#?string) - -rm [?bool](#?bool) - -rootfs [?bool](#?bool) - -securityOpt [?[]string](#?[]string) - -shmSize [?string](#?string) - -stopSignal [?string](#?string) - -stopTimeout [?int](#?int) - -storageOpt [?[]string](#?[]string) - -subuidname [?string](#?string) - -subgidname [?string](#?string) - -sysctl [?[]string](#?[]string) - -systemd [?string](#?string) - -tmpfs [?[]string](#?[]string) - -tty [?bool](#?bool) - -uidmap [?[]string](#?[]string) - -ulimit [?[]string](#?[]string) - -user [?string](#?string) - -userns [?string](#?string) - -uts [?string](#?string) - -mount [?[]string](#?[]string) - -volume [?[]string](#?[]string) - -volumesFrom [?[]string](#?[]string) - -workDir [?string](#?string) -### <a name="DiffInfo"></a>type DiffInfo - - - -path [string](https://godoc.org/builtin#string) - -changeType [string](https://godoc.org/builtin#string) -### <a name="Event"></a>type Event - -Event describes a libpod struct - -id [string](https://godoc.org/builtin#string) - -image [string](https://godoc.org/builtin#string) - -name [string](https://godoc.org/builtin#string) - -status [string](https://godoc.org/builtin#string) - -time [string](https://godoc.org/builtin#string) - -type [string](https://godoc.org/builtin#string) -### <a name="ExecOpts"></a>type ExecOpts - - - -name [string](https://godoc.org/builtin#string) - -tty [bool](https://godoc.org/builtin#bool) - -privileged [bool](https://godoc.org/builtin#bool) - -cmd [[]string](#[]string) - -user [?string](#?string) - -workdir [?string](#?string) - -env [?[]string](#?[]string) - -detachKeys [?string](#?string) -### <a name="Image"></a>type Image - - - -id [string](https://godoc.org/builtin#string) - -digest [string](https://godoc.org/builtin#string) - -digests [[]string](#[]string) - -parentId [string](https://godoc.org/builtin#string) - -repoTags [[]string](#[]string) - -repoDigests [[]string](#[]string) - -created [string](https://godoc.org/builtin#string) - -size [int](https://godoc.org/builtin#int) - -virtualSize [int](https://godoc.org/builtin#int) - -containers [int](https://godoc.org/builtin#int) - -labels [map[string]](#map[string]) - -isParent [bool](https://godoc.org/builtin#bool) - -topLayer [string](https://godoc.org/builtin#string) - -readOnly [bool](https://godoc.org/builtin#bool) - -history [[]string](#[]string) -### <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) - -tags [[]string](#[]string) - -size [int](https://godoc.org/builtin#int) - -comment [string](https://godoc.org/builtin#string) -### <a name="ImageSaveOptions"></a>type ImageSaveOptions - - - -name [string](https://godoc.org/builtin#string) - -format [string](https://godoc.org/builtin#string) - -output [string](https://godoc.org/builtin#string) - -outputType [string](https://godoc.org/builtin#string) - -moreTags [[]string](#[]string) - -quiet [bool](https://godoc.org/builtin#bool) - -compress [bool](https://godoc.org/builtin#bool) -### <a name="ImageSearchFilter"></a>type ImageSearchFilter - - - -is_official [?bool](#?bool) - -is_automated [?bool](#?bool) - -star_count [int](https://godoc.org/builtin#int) -### <a name="ImageSearchResult"></a>type ImageSearchResult - -Represents a single search result from SearchImages - -description [string](https://godoc.org/builtin#string) - -is_official [bool](https://godoc.org/builtin#bool) - -is_automated [bool](https://godoc.org/builtin#bool) - -registry [string](https://godoc.org/builtin#string) - -name [string](https://godoc.org/builtin#string) - -star_count [int](https://godoc.org/builtin#int) -### <a name="InfoDistribution"></a>type InfoDistribution - -InfoDistribution describes the host's distribution - -distribution [string](https://godoc.org/builtin#string) - -version [string](https://godoc.org/builtin#string) -### <a name="InfoGraphStatus"></a>type InfoGraphStatus - -InfoGraphStatus describes the detailed status of the storage driver - -backing_filesystem [string](https://godoc.org/builtin#string) - -native_overlay_diff [string](https://godoc.org/builtin#string) - -supports_d_type [string](https://godoc.org/builtin#string) -### <a name="InfoHost"></a>type InfoHost - -InfoHost describes the host stats portion of PodmanInfo - -buildah_version [string](https://godoc.org/builtin#string) - -distribution [InfoDistribution](#InfoDistribution) - -mem_free [int](https://godoc.org/builtin#int) - -mem_total [int](https://godoc.org/builtin#int) - -swap_free [int](https://godoc.org/builtin#int) - -swap_total [int](https://godoc.org/builtin#int) - -arch [string](https://godoc.org/builtin#string) - -cpus [int](https://godoc.org/builtin#int) - -hostname [string](https://godoc.org/builtin#string) - -kernel [string](https://godoc.org/builtin#string) - -os [string](https://godoc.org/builtin#string) - -uptime [string](https://godoc.org/builtin#string) - -eventlogger [string](https://godoc.org/builtin#string) -### <a name="InfoPodmanBinary"></a>type InfoPodmanBinary - -InfoPodman provides details on the Podman binary - -compiler [string](https://godoc.org/builtin#string) - -go_version [string](https://godoc.org/builtin#string) - -podman_version [string](https://godoc.org/builtin#string) - -git_commit [string](https://godoc.org/builtin#string) -### <a name="InfoRegistry"></a>type InfoRegistry - -InfoRegistry describes the host's registry information - -search [[]string](#[]string) - -insecure [[]string](#[]string) - -blocked [[]string](#[]string) -### <a name="InfoStore"></a>type InfoStore - -InfoStore describes the host's storage information - -containers [int](https://godoc.org/builtin#int) - -images [int](https://godoc.org/builtin#int) - -graph_driver_name [string](https://godoc.org/builtin#string) - -graph_driver_options [string](https://godoc.org/builtin#string) - -graph_root [string](https://godoc.org/builtin#string) - -graph_status [InfoGraphStatus](#InfoGraphStatus) - -run_root [string](https://godoc.org/builtin#string) -### <a name="KubePodService"></a>type KubePodService - - - -pod [string](https://godoc.org/builtin#string) - -service [string](https://godoc.org/builtin#string) -### <a name="ListPodContainerInfo"></a>type ListPodContainerInfo - -ListPodContainerInfo is a returned struct for describing containers -in a pod. - -name [string](https://godoc.org/builtin#string) - -id [string](https://godoc.org/builtin#string) - -status [string](https://godoc.org/builtin#string) -### <a name="ListPodData"></a>type ListPodData - -ListPodData is the returned struct for an individual pod - -id [string](https://godoc.org/builtin#string) - -name [string](https://godoc.org/builtin#string) - -createdat [string](https://godoc.org/builtin#string) - -cgroup [string](https://godoc.org/builtin#string) - -status [string](https://godoc.org/builtin#string) - -labels [map[string]](#map[string]) - -numberofcontainers [string](https://godoc.org/builtin#string) - -containersinfo [ListPodContainerInfo](#ListPodContainerInfo) -### <a name="LogLine"></a>type LogLine - - - -device [string](https://godoc.org/builtin#string) - -parseLogType [string](https://godoc.org/builtin#string) - -time [string](https://godoc.org/builtin#string) - -msg [string](https://godoc.org/builtin#string) - -cid [string](https://godoc.org/builtin#string) -### <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 - - - -comment [string](https://godoc.org/builtin#string) -### <a name="PodContainerErrorData"></a>type PodContainerErrorData - - - -containerid [string](https://godoc.org/builtin#string) - -reason [string](https://godoc.org/builtin#string) -### <a name="PodCreate"></a>type PodCreate - -PodCreate is an input structure for creating pods. -It emulates options to podman pod create. The infraCommand and -infraImage options are currently NotSupported. - -name [string](https://godoc.org/builtin#string) - -cgroupParent [string](https://godoc.org/builtin#string) - -labels [map[string]](#map[string]) - -share [[]string](#[]string) - -infra [bool](https://godoc.org/builtin#bool) - -infraCommand [string](https://godoc.org/builtin#string) - -infraImage [string](https://godoc.org/builtin#string) - -publish [[]string](#[]string) -### <a name="PodmanInfo"></a>type PodmanInfo - -PodmanInfo describes the Podman host and build - -host [InfoHost](#InfoHost) - -registries [InfoRegistry](#InfoRegistry) - -store [InfoStore](#InfoStore) - -podman [InfoPodmanBinary](#InfoPodmanBinary) -### <a name="PsContainer"></a>type PsContainer - - - -id [string](https://godoc.org/builtin#string) - -image [string](https://godoc.org/builtin#string) - -command [string](https://godoc.org/builtin#string) - -created [string](https://godoc.org/builtin#string) - -ports [string](https://godoc.org/builtin#string) - -names [string](https://godoc.org/builtin#string) - -isInfra [bool](https://godoc.org/builtin#bool) - -status [string](https://godoc.org/builtin#string) - -state [string](https://godoc.org/builtin#string) - -pidNum [int](https://godoc.org/builtin#int) - -rootFsSize [int](https://godoc.org/builtin#int) - -rwSize [int](https://godoc.org/builtin#int) - -pod [string](https://godoc.org/builtin#string) - -createdAt [string](https://godoc.org/builtin#string) - -exitedAt [string](https://godoc.org/builtin#string) - -startedAt [string](https://godoc.org/builtin#string) - -labels [map[string]](#map[string]) - -nsPid [string](https://godoc.org/builtin#string) - -cgroup [string](https://godoc.org/builtin#string) - -ipc [string](https://godoc.org/builtin#string) - -mnt [string](https://godoc.org/builtin#string) - -net [string](https://godoc.org/builtin#string) - -pidNs [string](https://godoc.org/builtin#string) - -user [string](https://godoc.org/builtin#string) - -uts [string](https://godoc.org/builtin#string) - -mounts [string](https://godoc.org/builtin#string) -### <a name="PsOpts"></a>type PsOpts - - - -all [bool](https://godoc.org/builtin#bool) - -filters [?[]string](#?[]string) - -last [?int](#?int) - -latest [?bool](#?bool) - -noTrunc [?bool](#?bool) - -pod [?bool](#?bool) - -quiet [?bool](#?bool) - -size [?bool](#?bool) - -sort [?string](#?string) - -sync [?bool](#?bool) -### <a name="RemoveImageResponse"></a>type RemoveImageResponse - - - -untagged [[]string](#[]string) - -deleted [string](https://godoc.org/builtin#string) -### <a name="Runlabel"></a>type Runlabel - -Runlabel describes the required input for container runlabel - -image [string](https://godoc.org/builtin#string) - -authfile [string](https://godoc.org/builtin#string) - -display [bool](https://godoc.org/builtin#bool) - -name [string](https://godoc.org/builtin#string) - -pull [bool](https://godoc.org/builtin#bool) - -label [string](https://godoc.org/builtin#string) - -extraArgs [[]string](#[]string) - -opts [map[string]](#map[string]) -### <a name="Sockets"></a>type Sockets - -Sockets describes sockets location for a container - -container_id [string](https://godoc.org/builtin#string) - -io_socket [string](https://godoc.org/builtin#string) - -control_socket [string](https://godoc.org/builtin#string) -### <a name="StringResponse"></a>type StringResponse - - - -message [string](https://godoc.org/builtin#string) -### <a name="Volume"></a>type Volume - - - -name [string](https://godoc.org/builtin#string) - -labels [map[string]](#map[string]) - -mountPoint [string](https://godoc.org/builtin#string) - -driver [string](https://godoc.org/builtin#string) - -options [map[string]](#map[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) - -all [bool](https://godoc.org/builtin#bool) - -force [bool](https://godoc.org/builtin#bool) -## Errors -### <a name="ContainerNotFound"></a>type ContainerNotFound - -ContainerNotFound means the container could not be found by the provided name or ID in local storage. -### <a name="ErrCtrStopped"></a>type ErrCtrStopped - -Container is already stopped -### <a name="ErrRequiresCgroupsV2ForRootless"></a>type ErrRequiresCgroupsV2ForRootless - -This function requires CGroupsV2 to run in rootless mode. -### <a name="ErrorOccurred"></a>type ErrorOccurred - -ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message -is includes as part of the error's text. -### <a name="ImageNotFound"></a>type ImageNotFound - -ImageNotFound means the image could not be found by the provided name or ID in local storage. -### <a name="InvalidState"></a>type InvalidState - -InvalidState indicates that a container or pod was in an improper state for the requested operation -### <a name="NoContainerRunning"></a>type NoContainerRunning - -NoContainerRunning means none of the containers requested are running in a command that requires a running container. -### <a name="NoContainersInPod"></a>type NoContainersInPod - -NoContainersInPod means a pod has no containers on which to perform the operation. It contains -the pod ID. -### <a name="PodContainerError"></a>type PodContainerError - -PodContainerError means a container associated with a pod failed to perform an operation. It contains -a container ID of the container that failed. -### <a name="PodNotFound"></a>type PodNotFound - -PodNotFound means the pod could not be found by the provided name or ID in local storage. -### <a name="RuntimeError"></a>type RuntimeError - -RuntimeErrors generally means a runtime could not be found or gotten. -### <a name="VolumeNotFound"></a>type VolumeNotFound - -VolumeNotFound means the volume could not be found by the name or ID in local storage. -### <a name="WantsMoreRequired"></a>type WantsMoreRequired - -The Podman endpoint requires that you use a streaming connection. @@ -40,9 +40,6 @@ SOURCES = $(shell find . -path './.*' -prune -o -name "*.go") BUILDFLAGS := -mod=vendor $(BUILDFLAGS) BUILDTAGS_CROSS ?= containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay -ifneq (,$(findstring varlink,$(BUILDTAGS))) - PODMAN_VARLINK_DEPENDENCIES = pkg/varlink/iopodman.go -endif CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker) OCI_RUNTIME ?= "" @@ -149,10 +146,6 @@ ifeq ("$(wildcard $(GOPKGDIR))","") mkdir -p "$(GOPKGBASEDIR)" ln -sfn "$(CURDIR)" "$(GOPKGDIR)" endif - -ifneq (,$(findstring varlink,$(BUILDTAGS))) - ln -sfn "$(CURDIR)/vendor/github.com/varlink" "$(FIRST_GOPATH)/src/github.com/varlink" -endif touch $@ .PHONY: lint @@ -165,7 +158,7 @@ endif $(PRE_COMMIT) run -a .PHONY: golangci-lint -golangci-lint: .gopathok varlink_generate .install.golangci-lint +golangci-lint: .gopathok .install.golangci-lint hack/golangci-lint.sh run .PHONY: gofmt @@ -189,7 +182,7 @@ test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go) .PHONY: bin/podman -bin/podman: .gopathok $(SOURCES) go.mod go.sum $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman +bin/podman: .gopathok $(SOURCES) go.mod go.sum ## Build with podman # Make sure to warn in case we're building without the systemd buildtag. ifeq (,$(findstring systemd,$(BUILDTAGS))) @echo "Podman is being compiled without the systemd build tag. Install libsystemd on \ @@ -201,7 +194,7 @@ endif podman: bin/podman .PHONY: bin/podman-remote -bin/podman-remote: .gopathok $(SOURCES) go.mod go.sum $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote environment +bin/podman-remote: .gopathok $(SOURCES) go.mod go.sum ## Build with podman on remote environment $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "${REMOTETAGS}" -o $@ ./cmd/podman .PHONY: bin/podman-remote-static @@ -218,7 +211,7 @@ podman.msi: podman-remote podman-remote-windows install-podman-remote-windows-do |wixl-heat --var var.ManSourceDir --component-group ManFiles --directory-ref INSTALLDIR --prefix $(DOCFILE)/ >$(DOCFILE)/pages.wsx wixl -D VERSION=$(RELEASE_NUMBER) -D ManSourceDir=$(DOCFILE) -o podman-v$(RELEASE_NUMBER).msi contrib/msi/podman.wxs $(DOCFILE)/pages.wsx -podman-remote-%: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build podman for a specific GOOS +podman-remote-%: .gopathok ## Build podman for a specific GOOS $(eval BINSFX := $(shell test "$*" != "windows" || echo ".exe")) CGO_ENABLED=0 GOOS=$* $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "${REMOTETAGS}" -o bin/$@$(BINSFX) ./cmd/podman @@ -267,7 +260,6 @@ clean: ## Clean artifacts test/checkseccomp/checkseccomp \ test/goecho/goecho \ test/testdata/redis-image \ - pkg/varlink/iopodman.go \ libpod/container_ffjson.go \ libpod/pod_ffjson.go \ libpod/container_easyjson.go \ @@ -276,13 +268,13 @@ clean: ## Clean artifacts make -C docs clean .PHONY: localunit -localunit: test/goecho/goecho varlink_generate +localunit: test/goecho/goecho hack/check_root.sh make localunit rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH} $(GOBIN)/ginkgo \ -r \ $(TESTFLAGS) \ - --skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings,hack \ + --skipPackage test/e2e,pkg/apparmor,pkg/bindings,hack \ --cover \ --covermode atomic \ --coverprofile coverprofile \ @@ -304,18 +296,11 @@ ginkgo: ginkgo-remote: $(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(REMOTETAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/. -.PHONY: endpoint -ifneq (,$(findstring varlink,$(BUILDTAGS))) -endpoint: - $(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -debug test/endpoint/. -endpoint: -endif - .PHONY: localintegration -localintegration: varlink_generate test-binaries ginkgo +localintegration: test-binaries ginkgo .PHONY: remoteintegration -remoteintegration: varlink_generate test-binaries ginkgo-remote +remoteintegration: test-binaries ginkgo-remote .PHONY: localsystem localsystem: @@ -370,7 +355,7 @@ system.test-binary: .install.ginkgo $(GO) test -c ./test/system .PHONY: binaries -binaries: varlink_generate podman podman-remote ## Build podman +binaries: podman podman-remote ## Build podman .PHONY: install.catatonit install.catatonit: @@ -475,15 +460,6 @@ changelog: ## Generate changelog $(shell cat $(TMPFILE) >> changelog.txt) $(shell rm $(TMPFILE)) -completions: binaries - install ${SELINUXOPT} -d -m 755 completions/{bash,zsh,fish} - ./bin/podman completion bash --no-desc -f completions/bash/podman - ./bin/podman-remote completion bash --no-desc -f completions/bash/podman-remote - ./bin/podman completion zsh -f completions/zsh/_podman - ./bin/podman-remote completion zsh -f completions/zsh/_podman-remote - ./bin/podman completion fish -f completions/fish/podman.fish - ./bin/podman-remote completion fish -f completions/fish/podman-remote.fish - .PHONY: install install: .gopathok install.bin install.remote install.man install.cni install.systemd ## Install binaries to system locations @@ -543,23 +519,8 @@ install.docker: docker-docs install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} ${DESTDIR}${TMPFILESDIR} install ${SELINUXOPT} -m 644 contrib/systemd/system/podman-docker.conf -t ${DESTDIR}${TMPFILESDIR} -.PHONY: install.varlink -ifneq (,$(findstring varlink,$(BUILDTAGS))) -install.varlink: - install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} - install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${SYSTEMDDIR}/io.podman.socket - install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${USERSYSTEMDDIR}/io.podman.socket - install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.service ${DESTDIR}${SYSTEMDDIR}/io.podman.service - # User units are ordered differently, we can't make the *system* multi-user.target depend on a user unit. - # For user units the default.target that's the default is fine. - sed -e 's,^WantedBy=.*,WantedBy=default.target,' < contrib/varlink/io.podman.service > ${DESTDIR}${USERSYSTEMDDIR}/io.podman.service -else -install.varlink: -endif - - .PHONY: install.systemd -install.systemd: install.varlink +install.systemd: install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} # User services install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.service ${DESTDIR}${USERSYSTEMDDIR}/podman-auto-update.service @@ -640,20 +601,6 @@ endef fi # $BUILD_TAGS variable is used in hack/golangci-lint.sh -.PHONY: varlink_generate -ifneq (or $(findstring varlink,$(BUILDTAGS)),$(findstring varlink,$(BUILD_TAGS))) -varlink_generate: .gopathok pkg/varlink/iopodman.go ## Generate varlink -else -varlink_generate: -endif - -.PHONY: varlink_api_generate -ifneq (,$(findstring varlink,$(BUILDTAGS))) -varlink_api_generate: .gopathok API.md -else -varlink_api_generate: -endif - .PHONY: install.libseccomp.sudo install.libseccomp.sudo: rm -rf ../../seccomp/libseccomp @@ -661,26 +608,9 @@ install.libseccomp.sudo: cd ../../seccomp/libseccomp && git checkout --detach $(LIBSECCOMP_COMMIT) && ./autogen.sh && ./configure --prefix=/usr && make all && make install -pkg/varlink/iopodman.go: .gopathok pkg/varlink/io.podman.varlink -ifneq (,$(findstring Linux,$(shell uname -s))) - # Only generate the varlink code on Linux (see issue #4814). - $(GO) generate ./pkg/varlink/... -endif - -API.md: pkg/varlink/io.podman.varlink - $(GO) generate ./docs/... - .PHONY: validate.completions validate.completions: SHELL:=/usr/bin/env bash # Set shell to bash for this target validate.completions: - # Check that nobody has manually edited the completion scripts - # If this check fails run make completions to restore the correct scripts - diff completions/bash/podman <(./bin/podman completion --no-desc bash) - diff completions/zsh/_podman <(./bin/podman completion zsh) - diff completions/fish/podman.fish <(./bin/podman completion fish) - diff completions/bash/podman-remote <(./bin/podman-remote completion --no-desc bash) - diff completions/zsh/_podman-remote <(./bin/podman-remote completion zsh) - diff completions/fish/podman-remote.fish <(./bin/podman-remote completion fish) # Check if the files can be loaded by the shell . completions/bash/podman if [ -x /bin/zsh ]; then /bin/zsh completions/zsh/_podman; fi @@ -5,7 +5,7 @@ Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers. Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes. -* [Latest Version: 2.1.1](https://github.com/containers/podman/releases/latest) +* [Latest Version: 2.2.0](https://github.com/containers/podman/releases/latest) * Latest Remote client for Windows * Latest Remote client for MacOs * Latest Static Remote client for Linux @@ -176,10 +176,10 @@ you to manage and maintain those images and containers in a production environme familiar container cli commands. For more details, see the [Container Tools Guide](https://github.com/containers/buildah/tree/master/docs/containertools). -## Podman Legacy API (Varlink) -Podman offers a [Varlink-based API](https://github.com/containers/podman/blob/master/docs/tutorials/varlink_remote_client.md) -for remote management of containers. However, this API has been deprecated by the REST API. -Varlink support is in maintenance mode, and will be removed in a future release. +## Podman Former API (Varlink) +Podman formerly offered a [Varlink-based API](https://github.com/containers/podman/blob/master/docs/tutorials/varlink_remote_client.md) +for remote management of containers. However, this API was replaced by the REST API. +Varlink support has been removed as of the 3.0 release. For more details, you can see [this blog](https://podman.io/blogs/2020/01/17/podman-new-api.html). ## Static Binary Builds diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f4df37a0e..a1027b465 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,12 +3,14 @@ ## 2.2.0 ### Features - Experimental support for shortname aliasing has been added. This is not enabled by default, but can be turned on by setting the environment variable `CONTAINERS_SHORT_NAME_ALIASING` to `on`. Documentation is [available here](https://github.com/containers/image/blob/master/docs/containers-registries.conf.5.md#short-name-aliasing). +- Initial support has been added for the `podman network connect` and `podman network disconnect` commands, which allow existing containers to modify what networks they are connected to. At present, these commands can only be used on running containers that did not specify `--network=none` when they were created. +- The `podman run` command now supports the `--network-alias` option to set network aliases (additional names the container can be accessed at from other containers via DNS if the `dnsname` CNI plugin is in use). Aliases can also be added and removed using the new `podman network connect` and `podman network disconnect` commands. Please note that this requires a new release (v1.1.0) of the `dnsname` plugin, and will only work on newly-created CNI networks. - The `podman generate kube` command now features support for exporting container's memory and CPU limits ([#7855](https://github.com/containers/podman/issues/7855)). - The `podman play kube` command now features support for setting CPU and Memory limits for containers ([#7742](https://github.com/containers/podman/issues/7742)). +- The `podman play kube` command now supports persistent volumes claims using Podman named volumes. - The `podman play kube` command now supports Kubernetes configmaps via the `--configmap` option ([#7567](https://github.com/containers/podman/issues/7567)). - The `podman play kube` command now supports a `--log-driver` option to set the log driver for created containers. - The `podman play kube` command now supports a `--start` option, enabled by default, to start the pod after creating it. This allows for `podman play kube` to be more easily used in systemd unitfiles. -- The `podman run` command now supports the `--network-alias` option to set network aliases (additional names the container can be accessed at from other containers via DNS if the `dnsname` CNI plugin is in use). Please note that this requires a new release (v1.1.0) of the `dnsname` plugin, and will only work on newly-created CNI networks. - The `podman network create` command now supports the `--ipv6` option to enable dual-stack IPv6 networking for created networks ([#7302](https://github.com/containers/podman/issues/7302)). - The `podman inspect` command can now inspect pods, networks, and volumes, in addition to containers and images ([#6757](https://github.com/containers/podman/issues/6757)). - The `--mount` option for `podman run` and `podman create` now supports a new type, `image`, to mount the contents of an image into the container at a given location. @@ -25,7 +27,8 @@ - The `podman events` command now supports filtering events based on the labels of the container they occurred on using the `--filter label=key=value` option. - The `podman volume ls` command now supports filtering volumes based on their labels using the `--filter label=key=value` option. - The `--volume` and `--mount` options to `podman run` and `podman create` now support two new mount propagation options, `unbindable` and `runbindable`. -- The `name` filter for `podman pod ps` now matches based on a regular expression, instead of requiring an exact match. +- The `name` and `id` filters for `podman pod ps` now match based on a regular expression, instead of requiring an exact match. +- The `podman pod ps` command now supports a new filter `status`, that matches pods in a certain state. ### Changes - The `podman network rm --force` command will now also remove pods that are using the network ([#7791](https://github.com/containers/podman/issues/7791)). @@ -38,6 +41,10 @@ - Podman now delays the SIGTERM and SIGINT signals during container creation to ensure that Podman is not stopped midway through creating a container resulting in potential resource leakage ([#7941](https://github.com/containers/podman/issues/7941)). - The `podman save` command now strips signatures from images it is exporting, as the formats we export to do not support signatures ([#7659](https://github.com/containers/podman/issues/7659)). - A new `Degraded` state has been added to pods. Pods that have some, but not all, of their containers running are now considered to be `Degraded` instead of `Running`. +- Podman will now print a warning when conflicting network options related to port forwarding (e.g. `--publish` and `--net=host`) are specified when creating a container. +- The `--restart on-failure` and `--rm` options for containers no longer conflict. When both are specified, the container will be restarted if it exits with a non-zero error code, and removed if it exits cleanly ([#7906](https://github.com/containers/podman/issues/7906)). +- Remote Podman will no longer use settings from the client's `containers.conf`; defaults will instead be provided by the server's `containers.conf` ([#7657](https://github.com/containers/podman/issues/7657)). +- The `podman network rm` command now has a new alias, `podman network remove` ([#8402](https://github.com/containers/podman/issues/8402)). ### Bugfixes - Fixed a bug where `podman load` on the remote client did not error when attempting to load a directory, which is not yet supported for remote use. @@ -103,6 +110,14 @@ - Fixed a bug where the `podman stats` command did not show memory limits for containers ([#8265](https://github.com/containers/podman/issues/8265)). - Fixed a bug where the `podman pod inspect` command printed the static MAC address of the pod in a non-human-readable format ([#8386](https://github.com/containers/podman/pull/8386)). - Fixed a bug where the `--tls-verify` option of the `podman play kube` command had its logic inverted (`false` would enforce the use of TLS, `true` would disable it). +- Fixed a bug where the `podman network rm` command would error when trying to remove `macvlan` networks and rootless CNI networks ([#8491](https://github.com/containers/podman/issues/8491)). +- Fixed a bug where Podman was not setting sane defaults for missing `XDG_` environment variables. +- Fixed a bug where remote Podman would check if volume paths to be mounted in the container existed on the host, not the server ([#8473](https://github.com/containers/podman/issues/8473)). +- Fixed a bug where the `podman manifest create` and `podman manifest add` commands on local images would drop any images in the manifest not pulled by the host. +- Fixed a bug where networks made by `podman network create` did not include the `tuning` plugin, and as such did not support setting custom MAC addresses ([#8385](https://github.com/containers/podman/issues/8385)). +- Fixed a bug where container healthchecks did not use `$PATH` when searching for the Podman executable to run the healthcheck. +- Fixed a bug where the `--ip-range` option to `podman network create` did not properly handle non-classful subnets when calculating the last usable IP for DHCP assignment ([#8448](https://github.com/containers/podman/issues/8448)). +- Fixed a bug where the `podman container ps` alias for `podman ps` was missing ([#8445](https://github.com/containers/podman/issues/8445)). ### API - The Compat Create endpoint for Container has received a major refactor to share more code with the Libpod Create endpoint, and should be significantly more stable. @@ -112,22 +127,28 @@ - The Compat Create endpoint for images now properly supports specifying images by digest. - The Libpod Build endpoint for images now supports an `httpproxy` query parameter which, if set to true, will forward the server's HTTP proxy settings into the build container for `RUN` instructions. - The Libpod Untag endpoint for images will now remove all tags for the given image if no repository and tag are specified for removal. +- Fixed a bug where the Ping endpoint misspelled a header name (`Libpod-Buildha-Version` instead of `Libpod-Buildah-Version`). +- Fixed a bug where the Ping endpoint sent an extra newline at the end of its response where Docker did not. - Fixed a bug where the Compat Logs endpoint for containers did not send a newline character after each log line. - Fixed a bug where the Compat Logs endpoint for containers would mangle line endings to change newline characters to add a preceding carriage return ([#7942](https://github.com/containers/podman/issues/7942)). - Fixed a bug where the Compat Inspect endpoint for Containers did not properly list the container's stop signal ([#7917](https://github.com/containers/podman/issues/7917)). - Fixed a bug where the Compat Inspect endpoint for Containers formatted the container's create time incorrectly ([#7860](https://github.com/containers/podman/issues/7860)). -- Fixed a bug where the Compat Inspect endpoint for Containers did not include complete network information on the container. +- Fixed a bug where the Compat Inspect endpoint for Containers did not include the container's Path, Args, and Restart Count. +- Fixed a bug where the Compat Inspect endpoint for Containers prefixed added and dropped capabilities with `CAP_` (Docker does not do so). +- Fixed a bug where the Compat Info endpoint for the Engine did not include configured registries. - Fixed a bug where the server could panic if a client closed a connection midway through an image pull ([#7896](https://github.com/containers/podman/issues/7896)). - Fixed a bug where the Compat Create endpoint for volumes returned an error when a volume with the same name already existed, instead of succeeding with a 201 code ([#7740](https://github.com/containers/podman/issues/7740)). - Fixed a bug where a client disconnecting from the Libpod or Compat events endpoints could result in the server using 100% CPU ([#7946](https://github.com/containers/podman/issues/7946)). - Fixed a bug where the "no such image" error message sent by the Compat Inspect endpoint for Images returned a 404 status code with an error that was improperly formatted for Docker compatibility. - Fixed a bug where the Compat Create endpoint for networks did not properly set a default for the `driver` parameter if it was not provided by the client. - Fixed a bug where the Compat Inspect endpoint for images did not populate the `RootFS` field of the response. +- Fixed a bug where the Compat Inspect endpoint for images would omit the `ParentId` field if the image had no parent, and the `Created` field if the image did not have a creation time. +- Fixed a bug where the Compat Remove endpoint for Networks did not support the `Force` query parameter. ### Misc - Updated Buildah to v1.18.0 -- Updated the containers/storage library to v1.24.0 -- Updated the containers/image library to v5.8.0 +- Updated the containers/storage library to v1.24.1 +- Updated the containers/image library to v5.8.1 - Updated the containers/common library to v0.27.0 ## 2.1.1 diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 6dc43dbc6..41611d1aa 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -211,12 +211,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup mounts = append(mounts, mount) } - // volumes - volumes := make([]string, 0, len(cc.Config.Volumes)) - for v := range cc.Config.Volumes { - volumes = append(volumes, v) - } - // dns dns := make([]net.IP, 0, len(cc.HostConfig.DNS)) for _, d := range cc.HostConfig.DNS { @@ -373,7 +367,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup UserNS: string(cc.HostConfig.UsernsMode), UTS: string(cc.HostConfig.UTSMode), Mount: mounts, - Volume: volumes, VolumesFrom: cc.HostConfig.VolumesFrom, Workdir: cc.Config.WorkingDir, Net: &netInfo, @@ -388,6 +381,10 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup } } + // volumes + if volumes := cc.HostConfig.Binds; len(volumes) > 0 { + cliOpts.Volume = volumes + } if len(cc.HostConfig.BlkioWeightDevice) > 0 { devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice)) for _, d := range cc.HostConfig.BlkioWeightDevice { diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index 898d65bd0..cae52ccaa 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -59,8 +59,8 @@ func DefineNetFlags(cmd *cobra.Command) { _ = cmd.RegisterFlagCompletionFunc(macAddressFlagName, completion.AutocompleteNone) networkFlagName := "network" - netFlags.StringArray( - networkFlagName, []string{containerConfig.NetNS()}, + netFlags.String( + networkFlagName, containerConfig.NetNS(), "Connect a container to a network", ) _ = cmd.RegisterFlagCompletionFunc(networkFlagName, AutocompleteNetworks) @@ -194,29 +194,25 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { } if cmd.Flags().Changed("network") { - networks, err := cmd.Flags().GetStringArray("network") + network, err := cmd.Flags().GetString("network") if err != nil { return nil, err } - for i, network := range networks { - parts := strings.SplitN(network, ":", 2) - ns, cniNets, err := specgen.ParseNetworkNamespace(network) - if err != nil { - return nil, err - } - if i > 0 && (len(cniNets) == 0 || len(opts.CNINetworks) == 0) { - return nil, errors.Errorf("network conflict between type %s and %s", opts.Network.NSMode, ns.NSMode) - } + parts := strings.SplitN(network, ":", 2) - if len(parts) > 1 { - opts.NetworkOptions = make(map[string][]string) - opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - cniNets = nil - } - opts.Network = ns - opts.CNINetworks = append(opts.CNINetworks, cniNets...) + ns, cniNets, err := specgen.ParseNetworkNamespace(network) + if err != nil { + return nil, err + } + + if len(parts) > 1 { + opts.NetworkOptions = make(map[string][]string) + opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") + cniNets = nil } + opts.Network = ns + opts.CNINetworks = cniNets } aliases, err := cmd.Flags().GetStringSlice("network-alias") diff --git a/cmd/podman/completion/completion.go b/cmd/podman/completion/completion.go index ead8d1f05..84942a508 100644 --- a/cmd/podman/completion/completion.go +++ b/cmd/podman/completion/completion.go @@ -67,11 +67,7 @@ func completion(cmd *cobra.Command, args []string) error { var err error switch args[0] { case "bash": - if noDesc { - err = cmd.Root().GenBashCompletion(w) - } else { - err = cmd.Root().GenBashCompletionWithDesc(w) - } + err = cmd.Root().GenBashCompletion(w) case "zsh": if noDesc { err = cmd.Root().GenZshCompletionNoDesc(w) diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 6f84cf9b8..5d08e6163 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -144,7 +144,7 @@ func checkFlags(c *cobra.Command) error { func jsonOut(responses []entities.ListContainer) error { r := make([]entities.ListContainer, 0) for _, con := range responses { - con.CreatedAt = units.HumanDuration(time.Since(time.Unix(con.Created, 0))) + " ago" + con.CreatedAt = units.HumanDuration(time.Since(con.Created)) + " ago" con.Status = psReporter{con}.Status() r = append(r, con) } @@ -404,12 +404,12 @@ func (l psReporter) Ports() string { // CreatedAt returns the container creation time in string format. podman // and docker both return a timestamped value for createdat func (l psReporter) CreatedAt() string { - return time.Unix(l.Created, 0).String() + return l.Created.String() } // CreateHuman allows us to output the created time in human readable format func (l psReporter) CreatedHuman() string { - return units.HumanDuration(time.Since(time.Unix(l.Created, 0))) + " ago" + return units.HumanDuration(time.Since(l.Created)) + " ago" } // Cgroup exposes .Namespaces.Cgroup diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 5b0aa2fe4..d997ea344 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -171,7 +171,33 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - + createOptions.Net.Network = specgen.Namespace{} + if cmd.Flag("network").Changed { + netInput, err := cmd.Flags().GetString("network") + if err != nil { + return err + } + parts := strings.SplitN(netInput, ":", 2) + + n := specgen.Namespace{} + switch { + case netInput == "bridge": + n.NSMode = specgen.Bridge + case netInput == "host": + n.NSMode = specgen.Host + case netInput == "slirp4netns", strings.HasPrefix(netInput, "slirp4netns:"): + n.NSMode = specgen.Slirp + if len(parts) > 1 { + createOptions.Net.NetworkOptions = make(map[string][]string) + createOptions.Net.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") + } + default: + // Container and NS mode are presently unsupported + n.NSMode = specgen.Bridge + createOptions.Net.CNINetworks = strings.Split(netInput, ",") + } + createOptions.Net.Network = n + } if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") diff --git a/cmd/podman/registry/registry.go b/cmd/podman/registry/registry.go index 9c0b290e7..481ed810f 100644 --- a/cmd/podman/registry/registry.go +++ b/cmd/podman/registry/registry.go @@ -18,9 +18,6 @@ const DefaultRootAPIPath = "/run/podman/podman.sock" // DefaultRootAPIAddress is the default address of the REST socket with unix: prefix const DefaultRootAPIAddress = "unix:" + DefaultRootAPIPath -// DefaultVarlinkAddress is the default address of the varlink socket -const DefaultVarlinkAddress = "unix:/run/podman/io.podman" - type CliCommand struct { Mode []entities.EngineMode Command *cobra.Command diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 78062d135..42482b5d9 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -38,7 +38,6 @@ Enable a listening service for API access to Podman commands. srvArgs = struct { Timeout int64 - Varlink bool }{} ) @@ -55,9 +54,6 @@ func init() { flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") _ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) - flags.BoolVar(&srvArgs.Varlink, "varlink", false, "Use legacy varlink service instead of REST. Unit of --time changes from seconds to milliseconds.") - - _ = flags.MarkDeprecated("varlink", "valink API is deprecated.") flags.SetNormalizeFunc(aliasTimeoutFlag) } @@ -97,11 +93,6 @@ func service(cmd *cobra.Command, args []string) error { Command: cmd, } - if srvArgs.Varlink { - opts.Timeout = time.Duration(srvArgs.Timeout) * time.Millisecond - return registry.ContainerEngine().VarlinkService(registry.GetContext(), opts) - } - opts.Timeout = time.Duration(srvArgs.Timeout) * time.Second return restService(opts, cmd.Flags(), registry.PodmanConfig()) } @@ -111,8 +102,7 @@ func resolveAPIURI(_url []string) (string, error) { // 1) User input wins always // 2) systemd socket activation // 3) rootless honors XDG_RUNTIME_DIR - // 4) if varlink -- adapter.DefaultVarlinkAddress - // 5) lastly adapter.DefaultAPIAddress + // 4) lastly adapter.DefaultAPIAddress if len(_url) == 0 { if v, found := os.LookupEnv("PODMAN_SOCKET"); found { @@ -134,16 +124,11 @@ func resolveAPIURI(_url []string) (string, error) { } socketName := "podman.sock" - if srvArgs.Varlink { - socketName = "io.podman" - } socketPath := filepath.Join(xdg, "podman", socketName) if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil { return "", err } return "unix:" + socketPath, nil - case srvArgs.Varlink: - return registry.DefaultVarlinkAddress, nil default: if err := os.MkdirAll(filepath.Dir(registry.DefaultRootAPIPath), 0700); err != nil { return "", err diff --git a/cmd/podman/system/varlink.go b/cmd/podman/system/varlink.go deleted file mode 100644 index 363ac9cca..000000000 --- a/cmd/podman/system/varlink.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build linux,!remote - -package system - -import ( - "time" - - "github.com/containers/common/pkg/completion" - "github.com/containers/podman/v2/cmd/podman/registry" - "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/spf13/cobra" -) - -var ( - varlinkDescription = `Run varlink interface. Podman varlink listens on the specified unix domain socket for incoming connects. - - Tools speaking varlink protocol can remotely manage pods, containers and images. -` - varlinkCmd = &cobra.Command{ - Use: "varlink [options] [URI]", - Args: cobra.MinimumNArgs(1), - Short: "Run varlink interface", - Long: varlinkDescription, - RunE: varlinkE, - ValidArgsFunction: completion.AutocompleteDefault, - Deprecated: "Please see 'podman system service' for RESTful APIs", - Hidden: true, - Example: `podman varlink unix:/run/podman/io.podman - podman varlink --time 5000 unix:/run/podman/io.podman`, - } - varlinkArgs = struct { - Timeout int64 - }{} -) - -func init() { - registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: varlinkCmd, - }) - flags := varlinkCmd.Flags() - - timeFlagName := "time" - flags.Int64VarP(&varlinkArgs.Timeout, timeFlagName, "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout") - _ = varlinkCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) - - flags.SetNormalizeFunc(aliasTimeoutFlag) -} - -func varlinkE(cmd *cobra.Command, args []string) error { - uri := registry.DefaultVarlinkAddress - if len(args) > 0 { - uri = args[0] - } - opts := entities.ServiceOptions{ - URI: uri, - Timeout: time.Duration(varlinkArgs.Timeout) * time.Second, - Command: cmd, - } - return registry.ContainerEngine().VarlinkService(registry.GetContext(), opts) -} diff --git a/commands-demo.md b/commands-demo.md index cea7a63a3..967fe5beb 100644 --- a/commands-demo.md +++ b/commands-demo.md @@ -94,7 +94,6 @@ | [podman-umount(1)](https://podman.readthedocs.io/en/latest/markdown/podman-umount.1.html) | Unmount a working container's root filesystem | | [podman-unpause(1)](https://podman.readthedocs.io/en/latest/markdown/podman-unpause.1.html) | Unpause one or more containers | [![...](/docs/source/markdown/play.png)](https://podman.io/asciinema/podman/pause_unpause/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_pause_unpause.sh) | | [podman-unshare(1)](https://podman.readthedocs.io/en/latest/markdown/podman-unshare.1.html) | Run a command inside of a modified user namespace | -| [podman-varlink(1)](https://podman.readthedocs.io/en/latest/markdown/podman-varlink.1.html) | Runs the varlink backend interface | | [podman-version(1)](https://podman.readthedocs.io/en/latest/markdown/podman-version.1.html) | Display the Podman version information | | [podman-volume(1)](https://podman.readthedocs.io/en/latest/volume.html) | Manage Volumes | | [podman-volume-create(1)](https://podman.readthedocs.io/en/latest/markdown/podman-volume-create.1.html) | Create a new volume | diff --git a/contrib/build_rpm.sh b/contrib/build_rpm.sh index 6a82b131f..3039c1bf9 100755 --- a/contrib/build_rpm.sh +++ b/contrib/build_rpm.sh @@ -49,7 +49,6 @@ if [[ $pkg_manager == *dnf ]]; then fi PKGS+=(python3-devel \ - python3-varlink \ ) fi diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index 04e8a3c1c..01e75d9a6 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -71,8 +71,8 @@ SCRIPT_BASE=${SCRIPT_BASE:-./contrib/cirrus} # Downloaded, but not installed packages. PACKAGE_DOWNLOAD_DIR=/var/cache/download -# Log remote-client system test varlink output here -PODMAN_SERVER_LOG=$CIRRUS_WORKING_DIR/varlink.log +# Log remote-client system test server output here +PODMAN_SERVER_LOG=$CIRRUS_WORKING_DIR/server.log # Defaults when not running under CI export CI="${CI:-false}" diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index bf2b1a52b..fa921f3e4 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -176,14 +176,6 @@ function _run_altbuild() { *Without*) make build-no-cgo ;; - *varlink-API) - export SUGGESTION='remove API.md, then "make varlink_api_generate" and commit changes.' - make varlink_api_generate BUILDTAGS="varlink" - ./hack/tree_status.sh - ;; - *varlink-binaries) - make clean BUILDTAGS="varlink" binaries - ;; *RPM*) make -f ./.copr/Makefile rpmbuild --rebuild ./podman-*.src.rpm diff --git a/contrib/dependencies.txt b/contrib/dependencies.txt index f61912fde..2b36c947b 100644 --- a/contrib/dependencies.txt +++ b/contrib/dependencies.txt @@ -24,7 +24,6 @@ python3-pip python3-psutil python3-pytoml python3-pyyaml -python3-varlink rsync slirp4netns unzip diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index ee8ce4d45..3be44a3a3 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -42,7 +42,7 @@ Epoch: 99 %else Epoch: 0 %endif -Version: 2.2.0 +Version: 3.0.0 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 @@ -166,7 +166,6 @@ Provides: bundled(golang(github.com/tchap/go-patricia)) = v2.2.6 Provides: bundled(golang(github.com/ulikunitz/xz)) = v0.5.4 # "-" are not accepted in version strings, so comment out below line #Provides: bundled(golang(github.com/urfave/cli)) = fix-short-opts-parsing -Provides: bundled(golang(github.com/varlink/go)) = master Provides: bundled(golang(github.com/vbatts/tar-split)) = v0.10.2 Provides: bundled(golang(github.com/vishvananda/netlink)) = master Provides: bundled(golang(github.com/vishvananda/netns)) = master diff --git a/contrib/spec/python-podman.spec.in b/contrib/spec/python-podman.spec.in index e21fb141e..1fc817d1f 100644 --- a/contrib/spec/python-podman.spec.in +++ b/contrib/spec/python-podman.spec.in @@ -36,20 +36,13 @@ BuildArch: noarch BuildRequires: git BuildRequires: python3-devel BuildRequires: python3-setuptools -BuildRequires: python3-varlink Requires: python3-humanize Requires: python3-pytoml Requires: python3-setuptools -Requires: python3-varlink Requires: python3-psutil Requires: podman -%if 0%{?fedora} -# 2018-07-20 RHEL8 doesn't have varlink RPM yet -Requires: python3-varlink -%endif - Provides: %{name} = %{version}-%{release} %description diff --git a/contrib/varlink/io.podman.service b/contrib/varlink/io.podman.service deleted file mode 100644 index 5be5329f4..000000000 --- a/contrib/varlink/io.podman.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Podman Remote API Service -Requires=io.podman.socket -After=io.podman.socket -Documentation=man:podman-varlink(1) - -[Service] -Type=simple -ExecStart=/usr/bin/podman varlink unix:%t/podman/io.podman --timeout=60000 -TimeoutStopSec=30 -KillMode=process - -[Install] -WantedBy=multi-user.target -Also=io.podman.socket diff --git a/contrib/varlink/io.podman.socket b/contrib/varlink/io.podman.socket deleted file mode 100644 index f6a3ddc49..000000000 --- a/contrib/varlink/io.podman.socket +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Podman Remote API Socket -Documentation=man:podman-varlink(1) - -[Socket] -ListenStream=%t/podman/io.podman -SocketMode=0600 - -[Install] -WantedBy=sockets.target diff --git a/dependencies/analyses/README.md b/dependencies/analyses/README.md index 7309b1fa8..41d2ea418 100644 --- a/dependencies/analyses/README.md +++ b/dependencies/analyses/README.md @@ -29,7 +29,6 @@ WORK=/tmp/go-build794287815 2.7M github.com/containers/podman/vendor/github.com/gogo/protobuf/proto 2.5M github.com/containers/podman/vendor/k8s.io/apimachinery/pkg/apis/meta/v1 2.3M github.com/containers/podman/vendor/github.com/vishvananda/netlink -2.1M github.com/containers/podman/cmd/podman/varlink ``` The output of the `go-archive-analysis.sh` script is a sorted table with the size in bytes followed by the package. diff --git a/dependencies/dependencies.go b/dependencies/dependencies.go index 404ec2e60..b03ab149b 100644 --- a/dependencies/dependencies.go +++ b/dependencies/dependencies.go @@ -2,5 +2,4 @@ package dependencies import ( _ "github.com/onsi/ginkgo/ginkgo" - _ "github.com/varlink/go/cmd/varlink-go-interface-generator" // Note: this file is used to trick `go mod` into vendoring the command below. ) diff --git a/docs/generate.go b/docs/generate.go deleted file mode 100644 index 2adca8fc1..000000000 --- a/docs/generate.go +++ /dev/null @@ -1,3 +0,0 @@ -package docs - -//go:generate go run varlink/apidoc.go ../pkg/varlink/io.podman.varlink ../API.md diff --git a/docs/podman-derivative-api b/docs/podman-derivative-api index 1b6153df5..2e085c248 100644 --- a/docs/podman-derivative-api +++ b/docs/podman-derivative-api @@ -52,10 +52,6 @@ Potential skew between multiple libpod versions operating on the same storage ca .RE -.SH Varlink -.PP -Some code exists for this; splits the difference. Future uncertain. - .SH Making the choice .PP A good question to ask first is: Do you want users to be able to use \fB\fCpodman\fR to manipulate the containers created by your project? diff --git a/docs/source/Tutorials.rst b/docs/source/Tutorials.rst index 83818e3ae..e3e869d5b 100644 --- a/docs/source/Tutorials.rst +++ b/docs/source/Tutorials.rst @@ -2,7 +2,7 @@ Tutorials ========= -Here are a number of useful tutorials to get you up and running with Podman. If you are familiar with the Docker `Container Engine`_ the command in Podman_ should be quite familiar. If are brand new to containers, take a look at our `Introduction`. +Here are a number of useful tutorials to get you up and running with Podman. If you are familiar with the Docker `Container Engine`_ the command in Podman_ should be quite familiar. If you are brand new to containers, take a look at our `Introduction`. * `Basic Setup and Use of Podman <https://github.com/containers/podman/blob/master/docs/tutorials/podman_tutorial.md>`_: Learn how to setup Podman and perform some basic commands with the utility. * `Basic Setup and Use of Podman in a Rootless environment <https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md>`_: The steps required to setup rootless Podman are enumerated. diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 4570bf3ff..c71f4fae9 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -317,6 +317,10 @@ Pass through HTTP Proxy environment variables. Write the image ID to the file. +#### **--ignorefile** + +Path to an alternative .dockerignore file. + #### **--ipc**=*how* Sets the configuration for IPC namespaces when handling `RUN` instructions. @@ -844,9 +848,10 @@ $ podman build -f dev/Containerfile https://10.10.10.1/podman/context.tar.gz ### `.dockerignore` -If the file .dockerignore exists in the context directory, `podman build` reads -its contents. Podman uses the content to exclude files and directories from -the context directory, when executing COPY and ADD directives in the +If the file .dockerignore exists in the context directory, `buildah copy` reads +its contents. Use the `--ignorefile` flag to override .dockerignore path location. +Podman uses the content to exclude files and directories from the context +directory, when executing COPY and ADD directives in the Containerfile/Dockerfile Users can specify a series of Unix shell globals in a .dockerignore file to diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index eb73609d4..ab390447e 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -22,12 +22,54 @@ Default settings for flags are defined in `containers.conf`. Most settings for remote connections use the server's containers.conf, except when documented in man pages. +## IMAGE + + The image is specified using transport:path format. If no transport is specified, the `docker` (container registry) +transport will be used by default. For remote Podman, `docker` is the only allowed transport. + + **dir:**_path_ + An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This +is a non-standardized format, primarily useful for debugging or noninvasive container inspection. + + $ podman save --format docker-dir fedora -o /tmp/fedora + $ podman create dir:/tmp/fedora echo hello + + **docker://**_docker-reference_ (Default) + An image reference stored in a remote container image registry. Example: "quay.io/podman/stable:latest". +The reference can include a path to a specific registry; if it does not, the +registries listed in registries.conf will be queried to find a matching image. +By default, credentials from `podman login` (stored at +$XDG_RUNTIME_DIR/containers/auth.json by default) will be used to authenticate; +otherwise it falls back to using credentials in $HOME/.docker/config.json. + + $ podman create registry.fedoraproject.org/fedora:latest echo hello + + **docker-archive:**_path_[**:**_docker-reference_] +An image stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a +file, and it must not contain a digest. + + $ podman save --format docker-archive fedora -o /tmp/fedora + $ podman create docker-archive:/tmp/fedora echo hello + + **docker-daemon:**_docker-reference_ + An image in _docker-reference_ format stored in the docker daemon internal storage. The _docker-reference_ can also be an image ID (docker-daemon:algo:digest). + + $ sudo docker pull fedora + $ sudo podman create docker-daemon:docker.io/library/fedora echo hello + + **oci-archive:**_path_**:**_tag_ + An image in a directory compliant with the "Open Container Image Layout Specification" at the specified _path_ +and specified with a _tag_. + + $ podman save --format oci-archive fedora -o /tmp/fedora + $ podman create oci-archive:/tmp/fedora echo hello + ## OPTIONS #### **--add-host**=*host* Add a custom host-to-IP mapping (host:ip) -Add a line to /etc/hosts. The format is hostname:ip. The **--add-host** +Add a line to /etc/hosts. The format is hostname:ip. The **--add-host** option can be set multiple times. #### **--annotation**=*key=value* @@ -77,7 +119,7 @@ Set the cgroup namespace mode for the container. **ns:<PATH>**: join the namespace at the specified path. **private**: create a new cgroup namespace. -If the host uses cgroups v1, the default is set to **host**. On cgroups v2 the default is **private**. +If the host uses cgroups v1, the default is set to **host**. On cgroups v2 the default is **private**. #### **--cgroups**=*mode* @@ -87,7 +129,7 @@ Valid values are *enabled*, *disabled*, *no-conmon*, *split*, which the default The *enabled* option will create a new cgroup under the cgroup-parent. The *disabled* option will force the container to not create CGroups, and thus conflicts with CGroup options (**--cgroupns** and **--cgroup-parent**). The *no-conmon* option disables a new CGroup only for the conmon process. -The *split* option splits the current cgroup in two sub-cgroups: one for conmon and one for the container payload. It is not possible to set *--cgroup-parent* with *split*. +The *split* option splits the current cgroup in two sub-cgroups: one for conmon and one for the container payload. It is not possible to set *--cgroup-parent* with *split*. #### **--cgroup-parent**=*path* @@ -95,7 +137,7 @@ Path to cgroups under which the cgroup for the container will be created. If the #### **--cgroup-conf**=*KEY=VALUE* -When running on cgroup v2, specify the cgroup file to write to and its value. For example **--cgroup-conf=memory.high=1073741824** sets the memory.high limit to 1GB. +When running on cgroup v2, specify the cgroup file to write to and its value. For example **--cgroup-conf=memory.high=1073741824** sets the memory.high limit to 1GB. #### **--cidfile**=*id* @@ -248,7 +290,7 @@ Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda: #### **--disable-content-trust** This is a Docker specific option to disable image verification to a Docker -registry and is not supported by Podman. This flag is a NOOP and provided +registry and is not supported by Podman. This flag is a NOOP and provided solely for scripting compatibility. #### **--dns**=*dns* @@ -292,7 +334,7 @@ You need to specify multi option commands in the form of a json string. Set environment variables -This option allows arbitrary environment variables that are available for the process to be launched inside of the container. If an environment variable is specified without a value, Podman will check the host environment for a value and set the variable only if it is set on the host. If an environment variable ending in __*__ is specified, Podman will search the host environment for variables starting with the prefix and will add those variables to the container. If an environment variable with a trailing ***** is specified, then a value must be supplied. +This option allows arbitrary environment variables that are available for the process to be launched inside of the container. If an environment variable is specified without a value, Podman will check the host environment for a value and set the variable only if it is set on the host. If an environment variable ending in __*__ is specified, Podman will search the host environment for variables starting with the prefix and will add those variables to the container. If an environment variable with a trailing ***** is specified, then a value must be supplied. See [**Environment**](#environment) note below for precedence and examples. @@ -311,7 +353,7 @@ on the host system. #### **--gidmap**=*container_gid:host_gid:amount* -GID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subgidname` flags. +GID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subgidname` flags. The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host. `--gidmap=0:30000:2000` @@ -322,8 +364,8 @@ Add additional groups to run as #### **--health-cmd**=*"command"* | *'["command", "arg1", ...]'* Set or alter a healthcheck command for a container. The command is a command to be executed inside your -container that determines your container health. The command is required for other healthcheck options -to be applied. A value of `none` disables existing healthchecks. +container that determines your container health. The command is required for other healthcheck options +to be applied. A value of `none` disables existing healthchecks. Multiple options can be passed in the form of a JSON array; otherwise, the command will be interpreted as an argument to `/bin/sh -c`. @@ -334,17 +376,17 @@ Set an interval for the healthchecks (a value of `disable` results in no automat #### **--health-retries**=*retries* -The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`. +The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`. #### **--health-start-period**=*period* The initialization time needed for a container to bootstrap. The value can be expressed in time format like -`2m3s`. The default value is `0s` +`2m3s`. The default value is `0s` #### **--health-timeout**=*timeout* -The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the -value can be expressed in a time format such as `1m22s`. The default value is `30s`. +The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the +value can be expressed in a time format such as `1m22s`. The default value is `30s`. #### **--hostname**=*name*, **-h** @@ -359,13 +401,13 @@ Print usage statement #### **--http-proxy**=*true|false* By default proxy environment variables are passed into the container if set -for the Podman process. This can be disabled by setting the `--http-proxy` -option to `false`. The environment variables passed in include `http_proxy`, +for the Podman process. This can be disabled by setting the `--http-proxy` +option to `false`. The environment variables passed in include `http_proxy`, `https_proxy`, `ftp_proxy`, `no_proxy`, and also the upper case versions of -those. This option is only needed when the host system must use a proxy but -the container should not use any proxy. Proxy environment variables specified +those. This option is only needed when the host system must use a proxy but +the container should not use any proxy. Proxy environment variables specified for the container in any other way will override the values that would have -been passed through from the host. (Other ways to specify the proxy for the +been passed through from the host. (Other ways to specify the proxy for the container include passing the values with the `--env` flag, or hard coding the proxy environment at container build time.) (Not available for remote commands) @@ -411,9 +453,9 @@ The address must be within the CNI network's IP address pool (default **10.88.0. #### **--ipc**=*ipc* Default is to create a private IPC namespace (POSIX SysV IPC) for the container - 'container:<name|id>': reuses another container shared memory, semaphores and message queues - 'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. - 'ns:<path>' path to an IPC namespace to join. + 'container:<name|id>': reuses another container shared memory, semaphores and message queues + 'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. + 'ns:<path>' path to an IPC namespace to join. #### **--kernel-memory**=*number[unit]* @@ -439,7 +481,7 @@ Not implemented #### **--log-driver**="*k8s-file*" -Logging driver for the container. Currently available options are *k8s-file*, *journald*, and *none*, with *json-file* aliased to *k8s-file* for scripting compatibility. +Logging driver for the container. Currently available options are *k8s-file*, *journald*, and *none*, with *json-file* aliased to *k8s-file* for scripting compatibility. #### **--log-opt**=*name*=*value* @@ -496,7 +538,7 @@ as memory limit. A limit value equal to memory plus swap. Must be used with the **-m** (**--memory**) flag. The swap `LIMIT` should always be larger than **-m** -(**--memory**) value. By default, the swap `LIMIT` will be set to double +(**--memory**) value. By default, the swap `LIMIT` will be set to double the value of --memory. The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes), @@ -547,7 +589,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · bind-propagation: shared, slave, private, unbindable, rshared, rslave, runbindable, or rprivate(default). See also mount(2). - . bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive. + . bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive. . relabel: shared, private. @@ -559,7 +601,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux. - · tmpcopyup: Enable copyup from the image directory at the same location to the tmpfs. Used by default. + · tmpcopyup: Enable copyup from the image directory at the same location to the tmpfs. Used by default. · notmpcopyup: Disable copying files from the image to the tmpfs. @@ -588,10 +630,10 @@ Valid _mode_ values are: - **none**: no networking; - **container:**_id_: reuse another container's network stack; - **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- **cni-network**: connect to a user-defined network, multiple networks should be comma-separated or they can be specified with multiple uses of the **--network** option; +- _network-id_: connect to a user-defined network, multiple networks should be comma separated; - **ns:**_path_: path to a network namespace to join; - **private**: create a new namespace for the container (default) -- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: +- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`). @@ -632,7 +674,7 @@ Override the architecture, defaults to hosts, of the image to be pulled. For exa Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`. #### **--override-variant**=*VARIANT* -Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7. +Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7. #### **--pid**=*pid* @@ -654,7 +696,7 @@ To make a pod with more granular options, use the `podman pod create` command be #### **--pod-id-file**=*path* -Run container in an existing pod and read the pod's ID from the specified file. If a container is run within a pod, and the pod has an infra-container, the infra-container will be started before the container is. +Run container in an existing pod and read the pod's ID from the specified file. If a container is run within a pod, and the pod has an infra-container, the infra-container will be started before the container is. #### **--privileged**=*true|false* @@ -688,7 +730,7 @@ If it is not, the container port will be randomly assigned a port on the host. Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT` **Note:** if a container will be run within a pod, it is not necessary to publish the port for -the containers in the pod. The port must only be published by the pod itself. Pod network +the containers in the pod. The port must only be published by the pod itself. Pod network stacks act like the network stack on the host - you have a variety of containers in the pod, and programs in the container, all sharing a single interface and IP address, and associated ports. If one container binds to a port, no other container can use that port @@ -725,16 +767,16 @@ Suppress output information when pulling images Mount the container's root filesystem as read only. By default a container will have its root filesystem writable allowing processes -to write files anywhere. By specifying the `--read-only` flag the container will have +to write files anywhere. By specifying the `--read-only` flag the container will have its root filesystem mounted as read only prohibiting any writes. #### **--read-only-tmpfs**=*true|false* -If container is running in --read-only mode, then mount a read-write tmpfs on /run, /tmp, and /var/tmp. The default is *true* +If container is running in --read-only mode, then mount a read-write tmpfs on /run, /tmp, and /var/tmp. The default is *true* #### **--replace**=**true**|**false** -If another container with the same name already exists, replace and remove it. The default is **false**. +If another container with the same name already exists, replace and remove it. The default is **false**. #### **--restart**=*policy* @@ -772,9 +814,9 @@ of the container is assumed to be managed externally. Determines how to use the NOTIFY_SOCKET, as passed with systemd and Type=notify. Default is **container**, which means allow the OCI runtime to proxy the socket into the -container to receive ready notification. Podman will set the MAINPID to conmon's pid. +container to receive ready notification. Podman will set the MAINPID to conmon's pid. The **conmon** option sets MAINPID to conmon's pid, and sends READY when the container -has started. The socket is never passed to the runtime or the container. +has started. The socket is never passed to the runtime or the container. The **ignore** option removes NOTIFY_SOCKET from the environment for itself and child processes, for the case where some other process above Podman uses NOTIFY_SOCKET and Podman should not use it. @@ -803,7 +845,7 @@ Security Options - `seccomp=unconfined` : Turn off seccomp confinement for the container - `seccomp=profile.json` : White listed syscalls seccomp Json file to be used as a seccomp filter -- `proc-opts=OPTIONS` : Comma separated list of options to use for the /proc mount. More details for the +- `proc-opts=OPTIONS` : Comma separated list of options to use for the /proc mount. More details for the possible mount options are specified at **proc(5)** man page. Note: Labeling can be disabled for all containers by setting label=false in the **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file. @@ -825,11 +867,11 @@ Remote connections use local containers.conf for defaults #### **--subgidname**=*name* -Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. +Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. #### **--subuidname**=*name* -Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. +Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. #### **--sysctl**=*SYSCTL* @@ -852,7 +894,7 @@ Note: if you use the --network=host option these sysctls will not be allowed. Run container in systemd mode. The default is *true*. The value *always* enforces the systemd mode is enforced without -looking at the executable name. Otherwise, if set to true and the +looking at the executable name. Otherwise, if set to true and the command you are running inside the container is systemd, /usr/sbin/init, /sbin/init or /usr/local/sbin/init. @@ -866,7 +908,7 @@ It will also set the default stop signal to SIGRTMIN+3. This allow systemd to run in a confined container without any modifications. Note: On `SELinux` systems, systemd attempts to write to the cgroup -file system. Containers writing to the cgroup file system are denied by default. +file system. Containers writing to the cgroup file system are denied by default. The `container_manage_cgroup` boolean must be enabled for this to be allowed on an SELinux separated system. `setsebool -P container_manage_cgroup true` @@ -879,7 +921,7 @@ Mount a temporary filesystem (`tmpfs`) mount into a container, for example: $ podman create -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image -This command mounts a `tmpfs` at `/tmp` within the container. The supported mount +This command mounts a `tmpfs` at `/tmp` within the container. The supported mount options are the same as the Linux default `mount` flags. If you do not specify any options, the systems uses the following options: `rw,noexec,nosuid,nodev`. @@ -907,7 +949,7 @@ Remote connections use local containers.conf for defaults #### **--uidmap**=*container_uid:host_uid:amount* -UID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subuidname` flags. +UID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subuidname` flags. The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host. `--uidmap=0:30000:2000` @@ -933,11 +975,11 @@ Without this argument the command will be run as root in the container. #### **--userns**=private #### **--userns**=*ns:my_namespace* -Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled. +Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled. -- `auto`: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are - **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace. +- `auto`: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are + **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace. **uidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a UID mapping to be present in the user namespace. **gidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a GID mapping to be present in the user namespace. - `container`: join the user namespace of the specified container. @@ -946,7 +988,7 @@ Set the user namespace mode for the container. It defaults to the **PODMAN_USER - `ns`: run the container in the given existing user namespace. - `private`: create a new namespace for the container (default) -This option is incompatible with --gidmap, --uidmap, --subuid and --subgid +This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. #### **--uts**=*mode* @@ -959,9 +1001,9 @@ Set the UTS namespace mode for the container. The following values are supported #### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*] -Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman -bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the podman -container. Similarly, `-v SOURCE-VOLUME:/CONTAINER-DIR` will mount the volume +Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman +bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the Podman +container. Similarly, `-v SOURCE-VOLUME:/CONTAINER-DIR` will mount the volume in the host to the container. If no such named volume exists, Podman will create one. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup> @@ -979,18 +1021,21 @@ The _options_ is a comma delimited list and can be: The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The volume will be mounted into the container at this directory. -Volumes may specify a source as well, as either a directory on the host or the -name of a named volume. If no source is given, the volume will be created as an -anonymous named volume with a randomly generated name, and will be removed when +Volumes may specify a source as well, as either a directory on the host +or the name of a named volume. If no source is given, the volume will be created as an +anonymously named volume with a randomly generated name, and will be removed when the container is removed via the `--rm` flag or `podman rm --volumes`. If a volume source is specified, it must be a path on the host or the name of a named volume. Host paths are allowed to be absolute or relative; relative paths -are resolved relative to the directory Podman is run in. Any source that does -not begin with a `.` or `/` will be treated as the name of a named volume. -If a volume with that name does not exist, it will be created. Volumes created -with names are not anonymous. They are not removed by the `--rm` option and the -`podman rm --volumes` command. +are resolved relative to the directory Podman is run in. If the source does not +exist, Podman will return an error. Users must pre-create the source files or +directories. + +Any source that does not begin with a `.` or `/` will be treated as the name of +a named volume. If a volume with that name does not exist, it will be created. +Volumes created with names are not anonymous, and they are not removed by the `--rm` +option and the `podman rm --volumes` command. You can specify multiple **-v** options to mount one or more volumes into a container. @@ -1021,13 +1066,13 @@ Only the current container can use a private volume. The `:O` flag tells Podman to mount the directory from the host as a temporary storage using the `overlay file system`. The container processes can modify content within the mountpoint which is stored in the -container storage in a separate directory. In overlay terms, the source +container storage in a separate directory. In overlay terms, the source directory will be the lower, and the container storage directory will be the upper. Modifications to the mount point are destroyed when the container finishes executing, similar to a tmpfs mount point being unmounted. Subsequent executions of the container will see the original source directory -content, any changes from previous container executions no longer exists. +content, any changes from previous container executions no longer exist. One use case of the overlay mount is sharing the package cache from the host into the container to allow speeding up builds. @@ -1042,7 +1087,7 @@ and can read/write `container_file_t`. If you can not change the labels on a source volume, SELinux container separation must be disabled for the container to work. - The source directory mounted into the container with an overlay mount -should not be modified, it can cause unexpected failures. It is recommended +should not be modified, it can cause unexpected failures. It is recommended that you do not modify the directory until the container finishes running. `Mounts propagation` @@ -1065,7 +1110,7 @@ slave volumes, the source mount point has to be either shared or slave. <sup>[[1]](#Footnote1)</sup> If you want to recursively mount a volume and all of its submounts into a -container, then you can use the `rbind` option. By default the bind option is +container, then you can use the `rbind` option. By default the bind option is used, and submounts of the source directory will not be mounted into the container. @@ -1124,7 +1169,7 @@ default, Podman does not change the labels set by the OS. To change a label in the container context, you can add `z` to the volume mount. This suffix tells Podman to relabel file objects on the shared volumes. The `z` option tells Podman that two containers share the volume content. As a result, -podman labels the content with a shared content label. Shared volume labels allow +Podman labels the content with a shared content label. Shared volume labels allow all containers to read/write content. If the location of the volume from the source container overlaps with @@ -1179,7 +1224,7 @@ $ podman create --tz=US/Eastern alpine date ### 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. +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. @@ -1199,7 +1244,7 @@ Precedence order (later entries override earlier entries): - **--env-host** : Host environment of the process executing Podman is added. - **--http-proxy**: By default, several environment variables will be passed in from the host, such as **http_proxy** and **no_proxy**. See **--http-proxy** for details. - Container image : Any environment variables specified in the container image. -- **--env-file** : Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry. +- **--env-file** : Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry. - **--env** : Any environment variables specified will override previous settings. Create containers and set the environment ending with a __*__ and a ***** @@ -1223,7 +1268,8 @@ b NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`. ## SEE ALSO -**subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**. +**podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-kill**(1), **podman-stop**(1), +**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**. ## HISTORY October 2017, converted from Docker documentation to Podman by Dan Walsh for Podman <dwalsh@redhat.com> diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md index 130c54ba9..4495cec3a 100644 --- a/docs/source/markdown/podman-pull.1.md +++ b/docs/source/markdown/podman-pull.1.md @@ -27,25 +27,42 @@ Images are stored in local image storage. ## SOURCE The SOURCE is the location from which the container images are pulled. - The Image "SOURCE" uses a "transport":"details" format. + The Image "SOURCE" uses a "transport":"details" format. Only the `docker` (container registry) + transport is allowed for remote access. Multiple transports are supported: **dir:**_path_ - An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection. + An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This + is a non-standardized format, primarily useful for debugging or noninvasive container inspection. - **docker://**_docker-reference_ - An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`. + $ podman pull dir:/tmp/myimage + + **docker://**_docker-reference_ (Default) + An image reference stored in a remote container image registry. The reference can include a path to a + specific registry; if it does not, the registries listed in registries.conf will be queried to find a matching + image. By default, credentials from podman login (stored at $XDG_RUNTIME_DIR/containers/auth.json by default) + will be used to authenticate; if these cannot be found, we will fall back to using credentials in + $HOME/.docker/config.json. + + $ podman pull quay.io/username/myimage **docker-archive:**_path_[**:**_docker-reference_] - An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest. + An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a + file, and it must not contain a digest. + + $ podman pull docker-archive:/tmp/myimage **docker-daemon:**_docker-reference_ - An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID). + An image in _docker-reference_ format stored in the docker daemon internal storage. The _docker-reference_ can also be an image ID (docker-daemon:algo:digest). + + $ sudo podman pull docker-daemon:docker.io/library/myimage:33 **oci-archive:**_path_**:**_tag_ An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_. + $ podman pull oci-archive:/tmp/myimage + ## OPTIONS #### **--all-tags**, **a** diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md index 87e64858c..68ea528cb 100644 --- a/docs/source/markdown/podman-push.1.md +++ b/docs/source/markdown/podman-push.1.md @@ -29,18 +29,28 @@ Images are pushed from those stored in local image storage. **dir:**_path_ An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection. + $ podman push myimage dir:/tmp/myimage + **docker://**_docker-reference_ An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`. + $ podman push myimage quay.io/username/myimage + **docker-archive:**_path_[**:**_docker-reference_] An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest. + $ podman push myimage docker-archive:/tmp/myimage + **docker-daemon:**_docker-reference_ - An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID). + An image in _docker-reference_ format stored in the docker daemon internal storage. _docker-reference_ must contain a tag. + + $ sudo podman push myimage docker-daemon:docker.io/library/myimage:33 **oci-archive:**_path_**:**_tag_ An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_. + $ podman push myimage oci-archive:/tmp/myimage + ## OPTIONS #### **--authfile**=*path* diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 184c12acd..13b586e17 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -16,7 +16,7 @@ which starts the process may define defaults related to the process that will be run in the container, the networking to expose, and more, but **podman run** gives final control to the operator or administrator who starts the container from the image. For that reason **podman run** has more options than any other -podman command. +Podman command. If the _image_ is not already loaded then **podman run** will pull the _image_, and all image dependencies, from the repository in the same way running **podman @@ -37,6 +37,48 @@ Default settings are defined in `containers.conf`. Most settings for remote connections use the servers containers.conf, except when documented in man pages. +## IMAGE + + The image is specified using transport:path format. If no transport is specified, the `docker` (container registry) +transport will be used by default. For remote Podman, `docker` is the only allowed transport. + + **dir:**_path_ + An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This +is a non-standardized format, primarily useful for debugging or noninvasive container inspection. + + $ podman save --format docker-dir fedora -o /tmp/fedora + $ podman run dir:/tmp/fedora echo hello + + **docker://**_docker-reference_ (Default) + An image reference stored in a remote container image registry. Example: "quay.io/podman/stable:latest". +The reference can include a path to a specific registry; if it does not, the +registries listed in registries.conf will be queried to find a matching image. +By default, credentials from `podman login` (stored at +$XDG_RUNTIME_DIR/containers/auth.json by default) will be used to authenticate; +otherwise it falls back to using credentials in $HOME/.docker/config.json. + + $ podman run registry.fedoraproject.org/fedora:latest echo hello + + **docker-archive:**_path_[**:**_docker-reference_] +An image stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a +file, and it must not contain a digest. + + $ podman save --format docker-archive fedora -o /tmp/fedora + $ podman run docker-archive:/tmp/fedora echo hello + + **docker-daemon:**_docker-reference_ + An image in _docker-reference_ format stored in the docker daemon internal storage. The _docker-reference_ can also be an image ID (docker-daemon:algo:digest). + + $ sudo docker pull fedora + $ sudo podman run docker-daemon:docker.io/library/fedora echo hello + + **oci-archive:**_path_**:**_tag_ + An image in a directory compliant with the "Open Container Image Layout Specification" at the specified _path_ +and specified with a _tag_. + + $ podman save --format oci-archive fedora -o /tmp/fedora + $ podman run oci-archive:/tmp/fedora echo hello + ## OPTIONS #### **--add-host**=_host_:_ip_ @@ -91,7 +133,7 @@ Set the cgroup namespace mode for the container. - **private**: create a new cgroup namespace. - **ns:**_path_: join the namespace at the specified path. -If the host uses cgroups v1, the default is set to **host**. On cgroups v2, the default is **private**. +If the host uses cgroups v1, the default is set to **host**. On cgroups v2, the default is **private**. #### **--cgroups**=**enabled**|**disabled**|**no-conmon**|**split** @@ -102,7 +144,7 @@ Default is **enabled**. The **enabled** option will create a new cgroup under the cgroup-parent. The **disabled** option will force the container to not create CGroups, and thus conflicts with CGroup options (**--cgroupns** and **--cgroup-parent**). The **no-conmon** option disables a new CGroup only for the **conmon** process. -The **split** option splits the current cgroup in two sub-cgroups: one for conmon and one for the container payload. It is not possible to set **--cgroup-parent** with **split**. +The **split** option splits the current cgroup in two sub-cgroups: one for conmon and one for the container payload. It is not possible to set **--cgroup-parent** with **split**. #### **--cgroup-parent**=*path* @@ -110,7 +152,7 @@ Path to cgroups under which the cgroup for the container will be created. If the #### **--cgroup-conf**=*KEY=VALUE* -When running on cgroup v2, specify the cgroup file to write to and its value. For example **--cgroup-conf=memory.high=1073741824** sets the memory.high limit to 1GB. +When running on cgroup v2, specify the cgroup file to write to and its value. For example **--cgroup-conf=memory.high=1073741824** sets the memory.high limit to 1GB. #### **--cidfile**=*file* @@ -254,7 +296,7 @@ from inside a rootless container will fail. The **crun**(1) runtime offers a workaround for this by adding the option **--annotation run.oci.keep_original_groups=1**. Podman may load kernel modules required for using the specified -device. The devices that podman will load modules when necessary are: +device. The devices that Podman will load modules when necessary are: /dev/fuse. #### **--device-cgroup-rule**=rule @@ -280,7 +322,7 @@ Limit write rate (in IO operations per second) to a device (e.g. **--device-writ #### **--disable-content-trust** This is a Docker specific option to disable image verification to a Docker -registry and is not supported by Podman. This flag is a NOOP and provided +registry and is not supported by Podman. This flag is a NOOP and provided solely for scripting compatibility. #### **--dns**=*ipaddr* @@ -326,7 +368,7 @@ You need to specify multi option commands in the form of a json string. Set environment variables. -This option allows arbitrary environment variables that are available for the process to be launched inside of the container. If an environment variable is specified without a value, Podman will check the host environment for a value and set the variable only if it is set on the host. If an environment variable ending in __*__ is specified, Podman will search the host environment for variables starting with the prefix and will add those variables to the container. If an environment variable with a trailing ***** is specified, then a value must be supplied. +This option allows arbitrary environment variables that are available for the process to be launched inside of the container. If an environment variable is specified without a value, Podman will check the host environment for a value and set the variable only if it is set on the host. If an environment variable ending in __*__ is specified, Podman will search the host environment for variables starting with the prefix and will add those variables to the container. If an environment variable with a trailing ***** is specified, then a value must be supplied. See [**Environment**](#environment) note below for precedence and examples. @@ -356,8 +398,8 @@ Add additional groups to run as #### **--health-cmd**=*"command"* | *'["command", "arg1", ...]'* Set or alter a healthcheck command for a container. The command is a command to be executed inside your -container that determines your container health. The command is required for other healthcheck options -to be applied. A value of **none** disables existing healthchecks. +container that determines your container health. The command is required for other healthcheck options +to be applied. A value of **none** disables existing healthchecks. Multiple options can be passed in the form of a JSON array; otherwise, the command will be interpreted as an argument to **/bin/sh -c**. @@ -373,12 +415,12 @@ The number of retries allowed before a healthcheck is considered to be unhealthy #### **--health-start-period**=*period* The initialization time needed for a container to bootstrap. The value can be expressed in time format like -**2m3s**. The default value is **0s**. +**2m3s**. The default value is **0s**. #### **--health-timeout**=*timeout* -The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the -value can be expressed in a time format such as **1m22s**. The default value is **30s**. +The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the +value can be expressed in a time format such as **1m22s**. The default value is **30s**. #### **--help** @@ -393,13 +435,13 @@ Sets the container host name that is available inside the container. Can only be #### **--http-proxy**=**true**|**false** By default proxy environment variables are passed into the container if set -for the Podman process. This can be disabled by setting the value to **false**. +for the Podman process. This can be disabled by setting the value to **false**. The environment variables passed in include **http_proxy**, **https_proxy**, **ftp_proxy**, **no_proxy**, and also the upper case versions of -those. This option is only needed when the host system must use a proxy but -the container should not use any proxy. Proxy environment variables specified +those. This option is only needed when the host system must use a proxy but +the container should not use any proxy. Proxy environment variables specified for the container in any other way will override the values that would have -been passed through from the host. (Other ways to specify the proxy for the +been passed through from the host. (Other ways to specify the proxy for the container include passing the values with the **--env** flag, or hard coding the proxy environment at container build time.) (Not available for remote commands) @@ -443,7 +485,7 @@ Set the IPC namespace mode for a container. The default is to create a private IPC namespace. - **container:**_id_: reuses another container shared memory, semaphores and message queues -- **host**: use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. +- **host**: use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure. - **ns:**_path_: path to an IPC namespace to join. #### **--kernel-memory**=_number_[_unit_] @@ -522,9 +564,9 @@ as memory limit. A limit value equal to memory plus swap. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes). -Must be used with the **-m** (**--memory**) flag. +Must be used with the **-m** (**--memory**) flag. The argument value should always be larger than that of - **-m** (**--memory**). By default, it is set to double + **-m** (**--memory**) By default, it is set to double the value of **--memory**. Set _number_ to **-1** to enable unlimited swap. @@ -573,7 +615,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · bind-propagation: shared, slave, private, unbindable, rshared, rslave, runbindable, or rprivate(default). See also mount(2). - . bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive. + . bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive. . relabel: shared, private. @@ -585,7 +627,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux. - · tmpcopyup: Enable copyup from the image directory at the same location to the tmpfs. Used by default. + · tmpcopyup: Enable copyup from the image directory at the same location to the tmpfs. Used by default. · notmpcopyup: Disable copying files from the image to the tmpfs. @@ -614,10 +656,10 @@ Valid _mode_ values are: - **none**: no networking; - **container:**_id_: reuse another container's network stack; - **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- **cni-network**: connect to a user-defined network, multiple networks should be comma-separated or they can be specified with multiple uses of the **--network** option; +- _network-id_: connect to a user-defined network, multiple networks should be comma separated; - **ns:**_path_: path to a network namespace to join; - **private**: create a new namespace for the container (default) -- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: +- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`). @@ -659,7 +701,7 @@ Override the architecture, defaults to hosts, of the image to be pulled. For exa Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`. #### **--override-variant**=*VARIANT* -Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7. +Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7. #### **--pid**=*mode* @@ -683,19 +725,21 @@ If a container is run with a pod, and the pod has an infra-container, the infra- #### **--pod-id-file**=*path* -Run container in an existing pod and read the pod's ID from the specified file. If a container is run within a pod, and the pod has an infra-container, the infra-container will be started before the container is. +Run container in an existing pod and read the pod's ID from the specified file. +If a container is run within a pod, and the pod has an infra-container, the infra-container will be started before the container is. #### **--preserve-fds**=*N* -Pass down to the process N additional file descriptors (in addition to 0, 1, 2). The total FDs will be 3+N. +Pass down to the process N additional file descriptors (in addition to 0, 1, 2). +The total FDs will be 3+N. #### **--privileged**=**true**|**false** Give extended privileges to this container. The default is **false**. By default, Podman containers are unprivileged (**=false**) and cannot, for -example, modify parts of the operating system. This is because by default a -container is only allowed limited access to devices. A "privileged" container +example, modify parts of the operating system. This is because by default a +container is only allowed limited access to devices. A "privileged" container is given the same access to devices as the user launching the container. A privileged container turns off the security features that isolate the @@ -720,7 +764,7 @@ If it is not, the container port will be randomly assigned a port on the host. Use **podman port** to see the actual mapping: **podman port $CONTAINER $CONTAINERPORT**. **Note:** if a container will be run within a pod, it is not necessary to publish the port for -the containers in the pod. The port must only be published by the pod itself. Pod network +the containers in the pod. The port must only be published by the pod itself. Pod network stacks act like the network stack on the host - you have a variety of containers in the pod, and programs in the container, all sharing a single interface and IP address, and associated ports. If one container binds to a port, no other container can use that port @@ -745,7 +789,7 @@ To find the mapping between the host ports and the exposed ports, use **podman p Pull image before running. The default is **missing**. - **missing**: attempt to pull the latest image from the registries listed in registries.conf if a local image does not exist.Raise an error if the image is not in any listed registry and is not present locally. -- **always**: Pull the image from the first registry it is found in as listed in registries.conf. Raise an error if not found in the registries, even if the image is present locally. +- **always**: Pull the image from the first registry it is found in as listed in registries.conf. Raise an error if not found in the registries, even if the image is present locally. - **never**: do not pull the image from the registry, use only the local version. Raise an error if the image is not present locally. #### **--quiet**, **-q** @@ -757,7 +801,7 @@ Suppress output information when pulling images Mount the container's root filesystem as read only. By default a container will have its root filesystem writable allowing processes -to write files anywhere. By specifying the **--read-only** flag, the container will have +to write files anywhere. By specifying the **--read-only** flag, the container will have its root filesystem mounted as read only prohibiting any writes. #### **--read-only-tmpfs**=**true**|**false** @@ -766,7 +810,7 @@ If container is running in **--read-only** mode, then mount a read-write tmpfs o #### **--replace**=**true**|**false** -If another container with the same name already exists, replace and remove it. The default is **false**. +If another container with the same name already exists, replace and remove it. The default is **false**. #### **--restart**=*policy* @@ -812,9 +856,9 @@ Note: On **SELinux** systems, the rootfs needs the correct label, which is by de Determines how to use the NOTIFY_SOCKET, as passed with systemd and Type=notify. Default is **container**, which means allow the OCI runtime to proxy the socket into the -container to receive ready notification. Podman will set the MAINPID to conmon's pid. +container to receive ready notification. Podman will set the MAINPID to conmon's pid. The **conmon** option sets MAINPID to conmon's pid, and sends READY when the container -has started. The socket is never passed to the runtime or the container. +has started. The socket is never passed to the runtime or the container. The **ignore** option removes NOTIFY_SOCKET from the environment for itself and child processes, for the case where some other process above Podman uses NOTIFY_SOCKET and Podman should not use it. @@ -838,15 +882,15 @@ Security Options - **label=disable**: Turn off label separation for the container - **no-new-privileges**: Disable container processes from gaining additional privileges - **seccomp=unconfined**: Turn off seccomp confinement for the container -- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter -- **proc-opts**=_OPTIONS_ : Comma separated list of options to use for the /proc mount. More details +- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter +- **proc-opts**=_OPTIONS_ : Comma separated list of options to use for the /proc mount. More details for the possible mount options are specified at **proc(5)** man page. Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file. #### **--shm-size**=_number_[_unit_] -Size of _/dev/shm_. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes). +Size of _/dev/shm_. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes). If you omit the unit, the system uses bytes. If you omit the size entirely, the default is **64m**. When _size_ is **0**, there is no limit on the amount of memory used for IPC by the container. @@ -904,7 +948,7 @@ Note: if you use the **--network=host** option, these sysctls will not be allowe Run container in systemd mode. The default is **true**. The value *always* enforces the systemd mode is enforced without -looking at the executable name. Otherwise, if set to **true** and the +looking at the executable name. Otherwise, if set to **true** and the command you are running inside the container is systemd, _/usr/sbin/init_, _/sbin/init_ or _/usr/local/sbin/init_. @@ -922,7 +966,7 @@ It will also set the default stop signal to **SIGRTMIN+3**. This allows systemd to run in a confined container without any modifications. Note that on **SELinux** systems, systemd attempts to write to the cgroup -file system. Containers writing to the cgroup file system are denied by default. +file system. Containers writing to the cgroup file system are denied by default. The **container_manage_cgroup** boolean must be enabled for this to be allowed on an SELinux separated system. ``` setsebool -P container_manage_cgroup true @@ -938,7 +982,7 @@ Mount a temporary filesystem (**tmpfs**) mount into a container, for example: $ podman run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image ``` -This command mounts a **tmpfs** at _/tmp_ within the container. The supported mount +This command mounts a **tmpfs** at _/tmp_ within the container. The supported mount options are the same as the Linux default mount flags. If you do not specify any options, the systems uses the following options: **rw,noexec,nosuid,nodev**. @@ -987,10 +1031,10 @@ When a user namespace is not in use, the UID and GID used within the container a #### **--userns**=**auto**|**host**|**keep-id**|**container:**_id_|**ns:**_namespace_ -Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with they `--uidmapping` and `--gidmapping` options. +Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled unless an explicit mapping is set with they `--uidmapping` and `--gidmapping` options. -- **auto**: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are - **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace. +- **auto**: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are + **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace. **uidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a UID mapping to be present in the user namespace. **gidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a GID mapping to be present in the user namespace. - **host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). @@ -999,7 +1043,7 @@ Set the user namespace mode for the container. It defaults to the **PODMAN_USER - **private**: create a new namespace for the container. - **container**: join the user namespace of the specified container. -This option is incompatible with **--gidmap**, **--uidmap**, **--subuid** and **--subgid**. +This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. #### **--uts**=*mode* @@ -1010,11 +1054,11 @@ Set the UTS namespace mode for the container. The following values are supported - **ns:[path]**: run the container in the given existing UTS namespace. - **container:[container]**: join the UTS namespace of the specified container. -#### **--volume**, **-v**[=[[_source-volume_|_host-dir_:]_container-dir_[:_options_]]] +#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*] -Create a bind mount. If you specify _/host-dir_:_/container-dir_, Podman -bind mounts _host-dir_ in the host to _container-dir_ in the Podman -container. Similarly, _source-volume_:_/container-dir_ will mount the volume +Create a bind mount. If you specify _/HOST-DIR_:_/CONTAINER-DIR_, Podman +bind mounts _host-dir_ in the host to _CONTAINER-DIR_ in the Podman +container. Similarly, _SOURCE-VOLUME_:_/CONTAINER-DIR_ will mount the volume in the host to the container. If no such named volume exists, Podman will create one. @@ -1029,24 +1073,30 @@ The _options_ is a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup> * [**no**]**suid** * [**O**] -The _container-dir_ must be an absolute path. +The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The volume +will be mounted into the container at this directory. -Volumes may specify a source as well, as either a directory on the host or the -name of a named volume. If no source is given, the volume will be created as an -anonymous named volume with a randomly generated name, and will be removed when -the container is removed via the **--rm** flag or **podman rm --volumes**. +Volumes may specify a source as well, as either a directory on the host +or the name of a named volume. If no source is given, the volume will be created as an +anonymously named volume with a randomly generated name, and will be removed when +the container is removed via the `--rm` flag or `podman rm --volumes`. If a volume source is specified, it must be a path on the host or the name of a named volume. Host paths are allowed to be absolute or relative; relative paths -are resolved relative to the directory Podman is run in. Any source that does -not begin with a **.** or **/** will be treated as the name of a named volume. -If a volume with that name does not exist, it will be created. Volumes created -with names are not anonymous and are not removed by **--rm** and -**podman rm --volumes**. +are resolved relative to the directory Podman is run in. If the source does not +exist, Podman will return an error. Users must pre-create the source files or +directories. + +Any source that does not begin with a `.` or `/` will be treated as the name of +a named volume. If a volume with that name does not exist, it will be created. +Volumes created with names are not anonymous, and they are not removed by the `--rm` +option and the `podman rm --volumes` command. -You can specify multiple **-v** options to mount one or more volumes into a +You can specify multiple **-v** options to mount one or more volumes into a container. + `Write Protected Volume Mounts` + You can add **:ro** or **:rw** option to mount a volume in read-only or read-write mode, respectively. By default, the volumes are mounted read-write. @@ -1069,13 +1119,13 @@ The **Z** option tells Podman to label the content with a private unshared label The `:O` flag tells Podman to mount the directory from the host as a temporary storage using the `overlay file system`. The container processes can modify content within the mountpoint which is stored in the -container storage in a separate directory. In overlay terms, the source +container storage in a separate directory. In overlay terms, the source directory will be the lower, and the container storage directory will be the upper. Modifications to the mount point are destroyed when the container finishes executing, similar to a tmpfs mount point being unmounted. Subsequent executions of the container will see the original source directory -content, any changes from previous container executions no longer exists. +content, any changes from previous container executions no longer exist. One use case of the overlay mount is sharing the package cache from the host into the container to allow speeding up builds. @@ -1090,7 +1140,7 @@ and can read/write `container_file_t`. If you can not change the labels on a source volume, SELinux container separation must be disabled for the container to work. - The source directory mounted into the container with an overlay mount -should not be modified, it can cause unexpected failures. It is recommended +should not be modified, it can cause unexpected failures. It is recommended that you do not modify the directory until the container finishes running. Only the current container can use a private volume. @@ -1115,7 +1165,7 @@ slave volumes, source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup> If you want to recursively mount a volume and all of its submounts into a -container, then you can use the **rbind** option. By default the bind option is +container, then you can use the **rbind** option. By default the bind option is used, and submounts of the source directory will not be mounted into the container. @@ -1174,7 +1224,7 @@ default, Podman does not change the labels set by the OS. To change a label in the container context, you can add `z` to the volume mount. This suffix tells Podman to relabel file objects on the shared volumes. The `z` option tells Podman that two containers share the volume content. As a result, -podman labels the content with a shared content label. Shared volume labels allow +Podman labels the content with a shared content label. Shared volume labels allow all containers to read/write content. If the location of the volume from the source container overlaps with @@ -1192,7 +1242,7 @@ can override the working directory by using the **-w** option. ## Exit Status The exit code from **podman run** gives information about why the container -failed to run or why it exited. When **podman run** exits with a non-zero code, +failed to run or why it exited. When **podman run** exits with a non-zero code, the exit codes follow the **chroot**(1) standard, see below: **125** The error is with Podman itself @@ -1223,12 +1273,12 @@ the exit codes follow the **chroot**(1) standard, see below: ### Running container in read-only mode During container image development, containers often need to write to the image -content. Installing packages into _/usr_, for example. In production, +content. Installing packages into _/usr_, for example. In production, applications seldom need to write to the image. Container applications write -to volumes if they need to write to file systems at all. Applications can be +to volumes if they need to write to file systems at all. Applications can be made more secure by running them in read-only mode using the **--read-only** switch. This protects the containers image from modification. Read only containers may -still need to write temporary data. The best way to handle this is to mount +still need to write temporary data. The best way to handle this is to mount tmpfs directories on _/run_ and _/tmp_. ``` @@ -1462,7 +1512,7 @@ $ podman run --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello ### Configuring Storage Options from the command line Podman allows for the configuration of storage by changing the values -in the _/etc/container/storage.conf_ or by using global options. This +in the _/etc/container/storage.conf_ or by using global options. This shows how to setup and use fuse-overlayfs for a one time run of busybox using global options. @@ -1481,7 +1531,7 @@ $ podman run --tz=US/Eastern alpine date ### 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**(1) and **newgidmap**(1) executables. +be installed. The **shadow-utils** package must include the **newuidmap**(1) and **newgidmap**(1) executables. Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released. @@ -1500,7 +1550,7 @@ in the following order of precedence (later entries override earlier entries): - Container image: Any environment variables specified in the container image. - **--http-proxy**: By default, several environment variables will be passed in from the host, such as **http_proxy** and **no_proxy**. See **--http-proxy** for details. - **--env-host**: Host environment of the process executing Podman is added. -- **--env-file**: Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry. +- **--env-file**: Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry. - **--env**: Any environment variables specified will override previous settings. Run containers and set the environment ending with a __*__ and a __*****__: @@ -1523,7 +1573,8 @@ b NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`. ## SEE ALSO -**subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**. +**podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-kill**(1), **podman-stop**(1), +**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**. ## HISTORY September 2018, updated by Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> diff --git a/docs/tutorials/podman-derivative-api.md b/docs/tutorials/podman-derivative-api.md index 8a1f40fc0..e38c2b13d 100644 --- a/docs/tutorials/podman-derivative-api.md +++ b/docs/tutorials/podman-derivative-api.md @@ -46,11 +46,6 @@ Disadvantages: - Binary size - Potential skew between multiple libpod versions operating on the same storage can cause problems -Varlink ---- - -The Varlink API is presently deprecated. We do not recommend adopting it for new projects. - Making the choice --- diff --git a/docs/tutorials/varlink_remote_client.md b/docs/tutorials/varlink_remote_client.md deleted file mode 100644 index 54c648a48..000000000 --- a/docs/tutorials/varlink_remote_client.md +++ /dev/null @@ -1,89 +0,0 @@ -# Podman varlink remote-client tutorial [DEPRECATED] - -## What is the varlink client - -This API has been deprecated by the [REST API](https://docs.podman.io/en/latest/_static/api.html). -For usage on Windows and Mac, please reference the [Podman Mac/Windows tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md) -Varlink support is in maintenance mode, and will be removed in a future release. -For more details, you can see [this blog](https://podman.io/blogs/2020/01/17/podman-new-api.html). - -The purpose of the Podman remote-client is to allow users to interact with a Podman "backend" -while on a separate client. The command line interface of the remote client is exactly the -same as the regular Podman commands with the exception of some flags being removed as they -do not apply to the remote-client. - -## What you need -To use the remote-client, you will need a binary for your client and a Podman "backend"; hereafter -referred to as the Podman node. In this context, a Podman node is a Linux system with Podman -installed on it and the varlink service activated. You will also need to be able to ssh into this -system as a user with privileges to the varlink socket (more on this later). - -## Building the remote client -At this time, the Podman remote-client is not being packaged for any distribution. It must be built from -source. To set up your build environment, see [Installation notes](https://github.com/containers/podman/blob/master/install.md) and follow the -section [Building from scratch](https://github.com/containers/podman/blob/master/install.md#building-from-scratch). Once you can successfully -build the regular Podman binary, you can now build the remote-client. -``` -$ make podman-remote -``` -Like building the regular Podman, the resulting binary will be in the *bin* directory. This is the binary -you will run on the remote node later in the instructions. - -## Setting up the remote and Podman nodes - -To use the remote-client, you must perform some setup on both the remote and Podman nodes. In this case, -the remote node refers to where the remote-client is being run; and the Podman node refers to where -Podman and its storage reside. - - -### Podman node setup - -Varlink bridge support is provided by the varlink cli command and installed using: -``` -$ sudo dnf install varlink-cli -``` - -The Podman node must have Podman (not the remote-client) installed as normal. If your system uses systemd, -then simply start the Podman varlink socket. -``` -$ sudo systemctl start io.podman.socket -``` - -If your system cannot use systemd, then you can manually establish the varlink socket with the Podman -command: -``` -$ sudo podman --log-level debug varlink --timeout 0 unix://run/podman/io.podman -``` - -### Required permissions -For now, the remote-client requires that you be able to run a privileged Podman and have privileged ssh -access to the remote system. This limitation is being worked on. - -### Remote node setup - -#### Initiate an ssh session to the Podman node -To use the remote client, an ssh connection to the Podman server must be established. - -Using the varlink bridge, an ssh tunnel must be initiated to connect to the server. Podman must then be informed of the location of the sshd server on the targeted server - -``` -$ export PODMAN_VARLINK_BRIDGE=$'ssh -T -p22 root@remotehost -- "varlink -A \'podman varlink \$VARLINK_ADDRESS\' bridge"' -$ bin/podman-remote images -REPOSITORY TAG IMAGE ID CREATED SIZE -docker.io/library/ubuntu latest 47b19964fb50 2 weeks ago 90.7 MB -docker.io/library/alpine latest caf27325b298 3 weeks ago 5.8 MB -quay.io/cevich/gcloud_centos latest 641dad61989a 5 weeks ago 489 MB -k8s.gcr.io/pause 3.1 da86e6ba6ca1 14 months ago 747 kB -``` - -The PODMAN_VARLINK_BRIDGE variable may be added to your log in settings. It does not change per connection. - -If coming from a Windows machine, the PODMAN_VARLINK_BRIDGE is formatted as: -``` -set PODMAN_VARLINK_BRIDGE=C:\Windows\System32\OpenSSH\ssh.exe -T -p22 root@remotehost -- varlink -A "podman varlink $VARLINK_ADDRESS" bridge -``` - -The arguments before the `--` are presented to ssh while the arguments after are for the varlink cli. The varlink arguments should be copied verbatim. - - `-p` is the port on the remote host for the ssh tunnel. `22` is the default. - - `root` is the currently supported user, while `remotehost` is the name or IP address of the host providing the Podman service. - - `-i` may be added to select an identity file. diff --git a/docs/varlink/apidoc.go b/docs/varlink/apidoc.go deleted file mode 100644 index 87304de15..000000000 --- a/docs/varlink/apidoc.go +++ /dev/null @@ -1,279 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "sort" - "strings" - - "github.com/varlink/go/varlink/idl" -) - -func readFileToString(path string) (string, error) { - content, err := ioutil.ReadFile(path) - if err != nil { - return "", err - } - return string(content), nil -} - -func exit(err error) { - fmt.Println(err.Error()) - os.Exit(1) -} - -func typeToString(input *idl.Type) string { - switch input.Kind { - case idl.TypeString: - return "string" - case idl.TypeBool: - return "bool" - case idl.TypeFloat: - return "float" - case idl.TypeArray: - result := input.ElementType.Alias - if result == "" { - return fmt.Sprintf("[]%s", typeToString(input.ElementType)) - } - return result - case idl.TypeAlias: - return input.Alias - case idl.TypeMap: - return "map[string]" - case idl.TypeInt: - return "int" - case idl.TypeMaybe: - return fmt.Sprintf("?%s", typeToString(input.ElementType)) - } - return "" -} - -func typeToLink(input string) string { - switch input { - case "string": - return "https://godoc.org/builtin#string" - case "int": - return "https://godoc.org/builtin#int" - case "bool": - return "https://godoc.org/builtin#bool" - case "float": - return "https://golang.org/src/builtin/builtin.go#L58" - default: - return fmt.Sprintf("#%s", input) - } -} - -type funcArgs struct { - paramName string - paramKind string -} -type funcDescriber struct { - Name string - inputParams []funcArgs - returnParams []funcArgs - doc string -} - -type funcSorter []funcDescriber - -func (a funcSorter) Len() int { return len(a) } -func (a funcSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a funcSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } - -type typeAttrs struct { - Name string - AttrType string -} -type typeDescriber struct { - Name string - doc string - Attrs []typeAttrs -} - -type typeSorter []typeDescriber - -func (a typeSorter) Len() int { return len(a) } -func (a typeSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a typeSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } - -type err struct { - Name string - doc string -} - -type errorSorter []err - -func (a errorSorter) Len() int { return len(a) } -func (a errorSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a errorSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } - -// collects defined types in the idl -func getTypes(tidl *idl.IDL) []typeDescriber { - var types []typeDescriber - for _, x := range tidl.Aliases { - i := typeDescriber{ - Name: x.Name, - doc: x.Doc, - } - ta := []typeAttrs{} - for _, y := range x.Type.Fields { - result := typeToString(y.Type) - ta = append(ta, typeAttrs{Name: y.Name, AttrType: result}) - } - i.Attrs = ta - types = append(types, i) - } - return types -} - -// collects defined methods in the idl -func getMethods(midl *idl.IDL) []funcDescriber { - var methods []funcDescriber - for _, t := range midl.Methods { - m := funcDescriber{ - Name: t.Name, - doc: t.Doc, - } - fa := []funcArgs{} - fo := []funcArgs{} - - for _, i := range t.In.Fields { - fa = append(fa, funcArgs{paramName: i.Name, paramKind: typeToString(i.Type)}) - - } - for _, f := range t.Out.Fields { - fo = append(fo, funcArgs{paramName: f.Name, paramKind: typeToString(f.Type)}) - } - m.inputParams = fa - m.returnParams = fo - methods = append(methods, m) - } - return methods -} - -// collects defined errors in the idl -func getErrors(midl *idl.IDL) []err { - var errors []err - for _, e := range midl.Errors { - myError := err{ - Name: e.Name, - doc: e.Doc, - } - errors = append(errors, myError) - } - return errors -} - -// generates the index for the top of the markdown page -func generateIndex(methods []funcDescriber, types []typeDescriber, errors []err, b bytes.Buffer) bytes.Buffer { - // Sort the methods, types, and errors by alphabetical order - sort.Sort(funcSorter(methods)) - sort.Sort(typeSorter(types)) - sort.Sort(errorSorter(errors)) - - for _, method := range methods { - var inArgs []string - var outArgs []string - for _, inArg := range method.inputParams { - inArgs = append(inArgs, fmt.Sprintf("%s: %s", inArg.paramName, inArg.paramKind)) - - } - for _, outArg := range method.returnParams { - outArgs = append(outArgs, outArg.paramKind) - - } - b.WriteString(fmt.Sprintf("\n[func %s(%s) %s](#%s)\n", method.Name, strings.Join(inArgs, ", "), strings.Join(outArgs, ", "), method.Name)) - } - b.WriteString("\n") - for _, t := range types { - b.WriteString(fmt.Sprintf("[type %s](#%s)\n\n", t.Name, t.Name)) - } - for _, e := range errors { - b.WriteString(fmt.Sprintf("[error %s](#%s)\n\n", e.Name, e.Name)) - } - return b -} - -// performs the output for defined methods -func generateFuncDescriptions(methods []funcDescriber, b bytes.Buffer) bytes.Buffer { - for _, method := range methods { - b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>func %s\n", method.Name, method.Name)) - var inArgs []string - var outArgs []string - for _, inArg := range method.inputParams { - inArgs = append(inArgs, fmt.Sprintf("%s: [%s](%s)", inArg.paramName, inArg.paramKind, typeToLink(inArg.paramKind))) - } - for _, outArg := range method.returnParams { - outArgs = append(outArgs, fmt.Sprintf("[%s](%s)", outArg.paramKind, typeToLink(outArg.paramKind))) - } - b.WriteString(fmt.Sprintf("<div style=\"background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;\">\n\nmethod %s(%s) %s</div>", method.Name, strings.Join(inArgs, ", "), strings.Join(outArgs, ", "))) - b.WriteString("\n") - b.WriteString(method.doc) - b.WriteString("\n") - } - return b -} - -// performs the output for defined types/structs -func generateTypeDescriptions(types []typeDescriber, b bytes.Buffer) bytes.Buffer { - for _, t := range types { - b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>type %s\n", t.Name, t.Name)) - b.WriteString(fmt.Sprintf("\n%s\n", t.doc)) - for _, i := range t.Attrs { - b.WriteString(fmt.Sprintf("\n%s [%s](%s)\n", i.Name, i.AttrType, typeToLink(i.AttrType))) - } - } - return b -} - -// performs the output for defined errors -func generateErrorDescriptions(errors []err, b bytes.Buffer) bytes.Buffer { - for _, e := range errors { - b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>type %s\n", e.Name, e.Name)) - b.WriteString(fmt.Sprintf("\n%s\n", e.doc)) - } - return b -} - -func main() { - args := os.Args - if len(args) < 2 { - exit(fmt.Errorf("you must provide an input and output path")) - } - varlinkFile := args[1] - mdFile := args[2] - - varlinkInput, err := readFileToString(varlinkFile) - if err != nil { - exit(err) - } - varlinkInput = strings.TrimRight(varlinkInput, "\n") - - // Run the idl parser - midl, err := idl.New(varlinkInput) - if err != nil { - exit(err) - } - // Collect up the info from the idl - methods := getMethods(midl) - types := getTypes(midl) - errors := getErrors(midl) - - out := bytes.Buffer{} - out.WriteString(fmt.Sprintf("# %s\n", midl.Name)) - out.WriteString(fmt.Sprintf("%s\n", midl.Doc)) - out.WriteString("## Index\n") - out = generateIndex(methods, types, errors, out) - out.WriteString("## Methods\n") - out = generateFuncDescriptions(methods, out) - out.WriteString("## Types\n") - out = generateTypeDescriptions(types, out) - out.WriteString("## Errors\n") - out = generateErrorDescriptions(errors, out) - if err := ioutil.WriteFile(mdFile, out.Bytes(), 0755); err != nil { - fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err) - os.Exit(1) - } -} @@ -10,7 +10,7 @@ require ( github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/containernetworking/cni v0.8.0 github.com/containernetworking/plugins v0.8.7 - github.com/containers/buildah v1.18.0 + github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c github.com/containers/common v0.29.0 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.8.1 @@ -57,7 +57,6 @@ require ( github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/uber/jaeger-client-go v2.25.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible // indirect - github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b github.com/vishvananda/netlink v1.1.0 go.etcd.io/bbolt v1.3.5 go.uber.org/atomic v1.7.0 // indirect @@ -75,5 +74,3 @@ require ( ) replace github.com/cri-o/ocicni => github.com/cri-o/ocicni v0.2.1-0.20201109200316-afdc16ba66df - -replace github.com/spf13/cobra => github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706 @@ -20,8 +20,6 @@ github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706 h1:KcMtguD/NlxB4c08lzc91o5by51Sf+Ec5+1Yv9Wqvbk= -github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20200113171025-3fe6c5262873 h1:93nQ7k53GjoMQ07HVP8g6Zj1fQZDDj7Xy2VkNNtvX8o= @@ -93,16 +91,12 @@ github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjM github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M= github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= -github.com/containers/buildah v1.18.0 h1:mWEm013LVNGecF++sYo0T7fe/4pqMas/PQxQ/qviC68= -github.com/containers/buildah v1.18.0/go.mod h1:qHLk7RUL7cHfA7ve1MKkZ6cyKUxHD0YxiLJcKY+mJe8= -github.com/containers/common v0.26.3/go.mod h1:hJWZIlrl5MsE2ELNRa+MPp6I1kPbXHauuj0Ym4BsLG4= +github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c h1:vyc2iYz9b2vfDiigpLyhiXNqXITt/dmDk74HpHzlQow= +github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c/go.mod h1:B+0OkXUogxdwsEy4ax3a5/vDtJjL6vCisiV6frQZJ4A= github.com/containers/common v0.29.0 h1:hTMC+urdkk5bKfhL/OgCixIX5xjJgQ2l2jPG745ECFQ= github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= -github.com/containers/image/v5 v5.7.0/go.mod h1:8aOy+YaItukxghRORkvhq5ibWttHErzDLy6egrKfKos= -github.com/containers/image/v5 v5.8.0 h1:B3FGHi0bdGXgg698kBIGOlHCXN5n+scJr6/5354GOPU= -github.com/containers/image/v5 v5.8.0/go.mod h1:jKxdRtyIDumVa56hdsZvV+gwx4zB50hRou6pIuCWLkg= github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q= github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= @@ -111,10 +105,7 @@ github.com/containers/ocicrypt v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6Gz github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g= github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA= github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= -github.com/containers/storage v1.23.6/go.mod h1:haFs0HRowKwyzvWEx9EgI3WsL8XCSnBDb5f8P5CAxJY= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= -github.com/containers/storage v1.24.0 h1:Fo2LkF7tkMLmo38sTZ/G8wHjcn8JfUFPfyTxM4WwMfk= -github.com/containers/storage v1.24.0/go.mod h1:A4d3BzuZK9b3oLVEsiSRhZLPIx3z7utgiPyXLK/YMhY= github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc= github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -322,8 +313,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= -github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= @@ -372,7 +361,6 @@ github.com/moby/sys/mount v0.1.1 h1:mdhBytJ1SMmMat0gtzWWjFX/87K5j6E/7Q5z7rR0cZY= github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= -github.com/moby/sys/mountinfo v0.3.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/term v0.0.0-20200429084858-129dac9f73f6/go.mod h1:or9wGItza1sRcM4Wd3dIv8DsFHYQuFsMHEdxUIlUxms= @@ -510,6 +498,9 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -544,8 +535,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b h1:hdDRrn9OP/roL8a/e/5Zu85ldrcdndu9IeBj2OEvQm0= -github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s= github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ= diff --git a/hack/golangci-lint.sh b/hack/golangci-lint.sh index 03c29c89a..50bf29bb8 100755 --- a/hack/golangci-lint.sh +++ b/hack/golangci-lint.sh @@ -6,8 +6,8 @@ set -e declare -A BUILD_TAGS # TODO: add systemd tag BUILD_TAGS[default]="apparmor,seccomp,selinux" -BUILD_TAGS[abi]="${BUILD_TAGS[default]},varlink,!remoteclient" -BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,varlink,remoteclient" +BUILD_TAGS[abi]="${BUILD_TAGS[default]},!remoteclient" +BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,remoteclient" declare -A SKIP_DIRS SKIP_DIRS[abi]="" diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages index 55c8b6582..08e56c77e 100755 --- a/hack/xref-helpmsgs-manpages +++ b/hack/xref-helpmsgs-manpages @@ -176,9 +176,6 @@ sub xref_by_man { # Special case: weirdness with Cobra and global/local options next if $k eq '--namespace' && $man =~ /-ps/; - # Special case: these require compiling with 'varlink' tag, - # which doesn't happen in CI gating task. - next if $k eq 'varlink'; next if "@subcommand" eq 'system' && $k eq 'service'; # Special case: podman completion is a hidden command diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index b81f3f716..56575c195 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -22,9 +22,9 @@ import ( cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/buildah/pkg/overlay" - "github.com/containers/buildah/pkg/secrets" "github.com/containers/common/pkg/apparmor" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/subscriptions" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/pkg/annotations" @@ -1435,7 +1435,7 @@ func (c *Container) makeBindMounts() error { } // Add Secret Mounts - secretMounts := secrets.SecretMountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.Containers.DefaultMountsFile, c.state.Mountpoint, c.RootUID(), c.RootGID(), rootless.IsRootless(), false) + secretMounts := subscriptions.MountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.Containers.DefaultMountsFile, c.state.Mountpoint, c.RootUID(), c.RootGID(), rootless.IsRootless(), false) for _, mount := range secretMounts { if _, ok := c.state.BindMounts[mount.Destination]; !ok { c.state.BindMounts[mount.Destination] = mount.Source diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go index d6d4c6084..161367d75 100644 --- a/libpod/container_top_linux.go +++ b/libpod/container_top_linux.go @@ -80,8 +80,8 @@ func (c *Container) Top(descriptors []string) ([]string, error) { func (c *Container) GetContainerPidInformation(descriptors []string) ([]string, error) { pid := strconv.Itoa(c.state.PID) // TODO: psgo returns a [][]string to give users the ability to apply - // filters on the data. We need to change the API here and the - // varlink API to return a [][]string if we want to make use of + // filters on the data. We need to change the API here + // to return a [][]string if we want to make use of // filtering. opts := psgo.JoinNamespaceOpts{FillMappings: rootless.IsRootless()} diff --git a/libpod/events/config.go b/libpod/events/config.go index af09a65ae..fc1457289 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -95,10 +95,6 @@ type Type string type Status string const ( - // If you add or subtract any values to the following lists, make sure you also update - // the switch statements below and the enums for EventType or EventStatus in the - // varlink description file. - // Container - event is related to containers Container Type = "container" // Image - event is related to images diff --git a/libpod/network/files.go b/libpod/network/files.go index 34cc5fa73..83cb1c23a 100644 --- a/libpod/network/files.go +++ b/libpod/network/files.go @@ -15,6 +15,9 @@ import ( "github.com/sirupsen/logrus" ) +// ErrNoSuchNetworkInterface indicates that no network interface exists +var ErrNoSuchNetworkInterface = errors.New("unable to find interface name for network") + // GetCNIConfDir get CNI configuration directory func GetCNIConfDir(configArg *config.Config) string { if len(configArg.Network.NetworkConfigDir) < 1 { @@ -172,7 +175,7 @@ func GetInterfaceNameFromConfig(path string) (string, error) { } } if len(name) == 0 { - return "", errors.New("unable to find interface name for network") + return "", ErrNoSuchNetworkInterface } return name, nil } diff --git a/libpod/network/network.go b/libpod/network/network.go index 7327a1a7d..0febb52f6 100644 --- a/libpod/network/network.go +++ b/libpod/network/network.go @@ -10,6 +10,7 @@ import ( "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -181,21 +182,26 @@ func RemoveNetwork(config *config.Config, name string) error { // Before we delete the configuration file, we need to make sure we can read and parse // it to get the network interface name so we can remove that too interfaceName, err := GetInterfaceNameFromConfig(cniPath) - if err != nil { - return errors.Wrapf(err, "failed to find network interface name in %q", cniPath) - } - liveNetworkNames, err := GetLiveNetworkNames() - if err != nil { - return errors.Wrapf(err, "failed to get live network names") - } - if util.StringInSlice(interfaceName, liveNetworkNames) { - if err := RemoveInterface(interfaceName); err != nil { - return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName) + if err == nil { + // Don't try to remove the network interface if we are not root + if !rootless.IsRootless() { + liveNetworkNames, err := GetLiveNetworkNames() + if err != nil { + return errors.Wrapf(err, "failed to get live network names") + } + if util.StringInSlice(interfaceName, liveNetworkNames) { + if err := RemoveInterface(interfaceName); err != nil { + return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName) + } + } } + } else if err != ErrNoSuchNetworkInterface { + // Don't error if we couldn't find the network interface name + return err } // Remove the configuration file if err := os.Remove(cniPath); err != nil { - return errors.Wrapf(err, "failed to remove network configuration file %q", cniPath) + return errors.Wrap(err, "failed to remove network configuration") } return nil } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 4e7ffaf81..15e470c80 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -1047,21 +1047,25 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return err } + if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil { + return err + } + + c.newNetworkEvent(events.NetworkDisconnect, netName) if c.state.State != define.ContainerStateRunning { - return errors.Wrapf(define.ErrCtrStateInvalid, "cannot disconnect container %s from networks as it is not running", nameOrID) + return nil } + if c.state.NetNS == nil { return errors.Wrapf(define.ErrNoNetwork, "unable to disconnect %s from %s", nameOrID, netName) } + podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions) if err := c.runtime.netPlugin.TearDownPod(podConfig); err != nil { return err } - if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil { - return err - } - // update network status + // update network status if container is not running networkStatus := c.state.NetworkStatus // clip out the index of the network tmpNetworkStatus := make([]*cnitypes.Result, len(networkStatus)-1) @@ -1071,7 +1075,6 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro } } c.state.NetworkStatus = tmpNetworkStatus - c.newNetworkEvent(events.NetworkDisconnect, netName) return c.save() } @@ -1096,15 +1099,16 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return err } + if err := c.runtime.state.NetworkConnect(c, netName, aliases); err != nil { + return err + } + c.newNetworkEvent(events.NetworkConnect, netName) if c.state.State != define.ContainerStateRunning { - return errors.Wrapf(define.ErrCtrStateInvalid, "cannot connect container %s to networks as it is not running", nameOrID) + return nil } if c.state.NetNS == nil { return errors.Wrapf(define.ErrNoNetwork, "unable to connect %s to %s", nameOrID, netName) } - if err := c.runtime.state.NetworkConnect(c, netName, aliases); err != nil { - return err - } ctrNetworks, _, err := c.networks() if err != nil { @@ -1159,7 +1163,6 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e networkStatus[index] = networkResults[0] c.state.NetworkStatus = networkStatus } - c.newNetworkEvent(events.NetworkConnect, netName) return c.save() } diff --git a/libpod/pod_top_linux.go b/libpod/pod_top_linux.go index 15ba02389..0e42c62df 100644 --- a/libpod/pod_top_linux.go +++ b/libpod/pod_top_linux.go @@ -53,9 +53,8 @@ func (p *Pod) GetPodPidInformation(descriptors []string) ([]string, error) { } // TODO: psgo returns a [][]string to give users the ability to apply - // filters on the data. We need to change the API here and the - // varlink API to return a [][]string if we want to make use of - // filtering. + // filters on the data. We need to change the API here to return + // a [][]string if we want to make use of filtering. opts := psgo.JoinNamespaceOpts{FillMappings: rootless.IsRootless()} output, err := psgo.JoinNamespaceAndProcessInfoByPidsWithOptions(pids, psgoDescriptors, &opts) if err != nil { diff --git a/libpod/runtime.go b/libpod/runtime.go index 792492db6..df3dfae2b 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -162,6 +162,10 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R runtime.config = conf + if err := SetXdgDirs(); err != nil { + return nil, err + } + storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID()) if err != nil { return nil, err diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 76419587a..3e4185db1 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -34,40 +34,56 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm // Set Pod hostname g.Config.Hostname = p.config.Hostname + var options []CtrCreateOption + + // Command: If user-specified, use that preferentially. + // If not set and the config file is set, fall back to that. + var infraCtrCommand []string + if p.config.InfraContainer.InfraCommand != nil { + logrus.Debugf("User-specified infra container entrypoint %v", p.config.InfraContainer.InfraCommand) + infraCtrCommand = p.config.InfraContainer.InfraCommand + } else if r.config.Engine.InfraCommand != "" { + logrus.Debugf("Config-specified infra container entrypoint %s", r.config.Engine.InfraCommand) + infraCtrCommand = []string{r.config.Engine.InfraCommand} + } + // Only if set by the user or containers.conf, we set entrypoint for the + // infra container. + // This is only used by commit, so it shouldn't matter... But someone + // may eventually want to commit an infra container? + // TODO: Should we actually do this if set by containers.conf? + if infraCtrCommand != nil { + // Need to duplicate the array - we are going to add Cmd later + // so the current array will be changed. + newArr := make([]string, 0, len(infraCtrCommand)) + newArr = append(newArr, infraCtrCommand...) + options = append(options, WithEntrypoint(newArr)) + } + isRootless := rootless.IsRootless() - entrypointSet := len(p.config.InfraContainer.InfraCommand) > 0 - entryPoint := p.config.InfraContainer.InfraCommand - entryCmd := []string{} - var options []CtrCreateOption // I've seen circumstances where config is being passed as nil. // Let's err on the side of safety and make sure it's safe to use. if config != nil { - // default to entrypoint in image if there is one - if !entrypointSet { - if len(config.Entrypoint) > 0 { - entrypointSet = true - entryPoint = config.Entrypoint - entryCmd = config.Entrypoint + if infraCtrCommand == nil { + // If we have no entrypoint and command from the image, + // we can't go on - the infra container has no command. + if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { + return nil, errors.Errorf("infra container has no command") } - } else { // so use the InfraCommand - entrypointSet = true - entryCmd = entryPoint - } - - if len(config.Cmd) > 0 { - // We can't use the default pause command, since we're - // sourcing from the image. If we didn't already set an - // entrypoint, set one now. - if !entrypointSet { + if len(config.Entrypoint) > 0 { + infraCtrCommand = config.Entrypoint + } else { // Use the Docker default "/bin/sh -c" // entrypoint, as we're overriding command. // If an image doesn't want this, it can // override entrypoint too. - entryCmd = []string{"/bin/sh", "-c"} + infraCtrCommand = []string{"/bin/sh", "-c"} } - entryCmd = append(entryCmd, config.Cmd...) } + if len(config.Cmd) > 0 { + infraCtrCommand = append(infraCtrCommand, config.Cmd...) + } + if len(config.Env) > 0 { for _, nameValPair := range config.Env { nameValSlice := strings.Split(nameValPair, "=") @@ -127,9 +143,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm } g.SetRootReadonly(true) - g.SetProcessArgs(entryCmd) + g.SetProcessArgs(infraCtrCommand) - logrus.Debugf("Using %q as infra container entrypoint", entryCmd) + logrus.Debugf("Using %q as infra container command", infraCtrCommand) g.RemoveMount("/dev/shm") if isRootless { @@ -148,9 +164,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) options = append(options, WithName(containerName)) options = append(options, withIsInfra()) - if entrypointSet { - options = append(options, WithEntrypoint(entryPoint)) - } if len(p.config.InfraContainer.ConmonPidFile) > 0 { options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) } diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index 293a17e0f..1dd563393 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -1,12 +1,379 @@ package compat import ( - "errors" - "net/http" + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "path/filepath" + "strings" + "github.com/containers/buildah/copier" + "github.com/containers/buildah/pkg/chrootuser" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" + + "net/http" + "os" + "time" + + "github.com/gorilla/schema" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func Archive(w http.ResponseWriter, r *http.Request) { - utils.Error(w, "not implemented", http.StatusNotImplemented, errors.New("not implemented")) + decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + switch r.Method { + case http.MethodPut: + handlePut(w, r, decoder, runtime) + case http.MethodGet, http.MethodHead: + handleHeadOrGet(w, r, decoder, runtime) + default: + utils.Error(w, fmt.Sprintf("not implemented, method: %v", r.Method), http.StatusNotImplemented, errors.New(fmt.Sprintf("not implemented, method: %v", r.Method))) + } +} + +func handleHeadOrGet(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { + query := struct { + Path string `schema:"path"` + }{} + + err := decoder.Decode(&query, r.URL.Query()) + if err != nil { + utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) + return + } + + if query.Path == "" { + utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.New("missing `path` parameter")) + return + } + + containerName := utils.GetName(r) + + ctr, err := runtime.LookupContainer(containerName) + if errors.Cause(err) == define.ErrNoSuchCtr { + utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists")) + return + } else if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + + mountPoint, err := ctr.Mount() + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to mount the container")) + return + } + + defer func() { + if err := ctr.Unmount(false); err != nil { + logrus.Warnf("failed to unmount container %s: %q", containerName, err) + } + }() + + opts := copier.StatOptions{} + + mountPoint, path, err := fixUpMountPointAndPath(runtime, ctr, mountPoint, query.Path) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + + stats, err := copier.Stat(mountPoint, "", opts, []string{filepath.Join(mountPoint, path)}) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to get stats about file")) + return + } + + if len(stats) <= 0 || len(stats[0].Globbed) <= 0 { + errs := make([]string, 0, len(stats)) + + for _, stat := range stats { + if stat.Error != "" { + errs = append(errs, stat.Error) + } + } + + utils.Error(w, "Not found.", http.StatusNotFound, fmt.Errorf("file doesn't exist (errs: %q)", strings.Join(errs, ";"))) + + return + } + + statHeader, err := statsToHeader(stats[0].Results[stats[0].Globbed[0]]) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + + w.Header().Add("X-Docker-Container-Path-Stat", statHeader) + + if r.Method == http.MethodGet { + idMappingOpts, err := ctr.IDMappings() + if err != nil { + utils.Error(w, "Not found.", http.StatusInternalServerError, + errors.Wrapf(err, "error getting IDMappingOptions")) + return + } + + destOwner := idtools.IDPair{UID: os.Getuid(), GID: os.Getgid()} + + opts := copier.GetOptions{ + UIDMap: idMappingOpts.UIDMap, + GIDMap: idMappingOpts.GIDMap, + ChownDirs: &destOwner, + ChownFiles: &destOwner, + KeepDirectoryNames: true, + } + + w.WriteHeader(http.StatusOK) + + err = copier.Get(mountPoint, "", opts, []string{filepath.Join(mountPoint, path)}, w) + if err != nil { + logrus.Error(errors.Wrapf(err, "failed to copy from the %s container path %s", containerName, query.Path)) + return + } + } else { + w.WriteHeader(http.StatusOK) + } +} + +func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { + query := struct { + Path string `schema:"path"` + // TODO handle params below + NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"` + CopyUIDGID bool `schema:"copyUIDGID"` + }{} + + err := decoder.Decode(&query, r.URL.Query()) + if err != nil { + utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) + return + } + + ctrName := utils.GetName(r) + + ctr, err := runtime.LookupContainer(ctrName) + if err != nil { + utils.Error(w, "Not found", http.StatusNotFound, errors.Wrapf(err, "the %s container doesn't exists", ctrName)) + return + } + + mountPoint, err := ctr.Mount() + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, errors.Wrapf(err, "failed to mount the %s container", ctrName)) + return + } + + defer func() { + if err := ctr.Unmount(false); err != nil { + logrus.Warnf("failed to unmount container %s", ctrName) + } + }() + + user, err := getUser(mountPoint, ctr.User()) + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return + } + + idMappingOpts, err := ctr.IDMappings() + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, errors.Wrapf(err, "error getting IDMappingOptions")) + return + } + + destOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} + + opts := copier.PutOptions{ + UIDMap: idMappingOpts.UIDMap, + GIDMap: idMappingOpts.GIDMap, + ChownDirs: &destOwner, + ChownFiles: &destOwner, + } + + mountPoint, path, err := fixUpMountPointAndPath(runtime, ctr, mountPoint, query.Path) + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return + } + + w.WriteHeader(http.StatusOK) + + err = copier.Put(mountPoint, filepath.Join(mountPoint, path), opts, r.Body) + if err != nil { + logrus.Error(errors.Wrapf(err, "failed to copy to the %s container path %s", ctrName, query.Path)) + return + } +} + +func statsToHeader(stats *copier.StatForItem) (string, error) { + statsDTO := struct { + Name string `json:"name"` + Size int64 `json:"size"` + Mode os.FileMode `json:"mode"` + ModTime time.Time `json:"mtime"` + LinkTarget string `json:"linkTarget"` + }{ + Name: filepath.Base(stats.Name), + Size: stats.Size, + Mode: stats.Mode, + ModTime: stats.ModTime, + LinkTarget: stats.ImmediateTarget, + } + + jsonBytes, err := json.Marshal(&statsDTO) + if err != nil { + return "", errors.Wrap(err, "failed to serialize file stats") + } + + buff := bytes.NewBuffer(make([]byte, 0, 128)) + base64encoder := base64.NewEncoder(base64.StdEncoding, buff) + + _, err = base64encoder.Write(jsonBytes) + if err != nil { + return "", err + } + + err = base64encoder.Close() + if err != nil { + return "", err + } + + return buff.String(), nil +} + +// the utility functions below are copied from abi/cp.go + +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 fixUpMountPointAndPath(runtime *libpod.Runtime, ctr *libpod.Container, mountPoint, ctrPath string) (string, string, error) { + if !filepath.IsAbs(ctrPath) { + endsWithSep := strings.HasSuffix(ctrPath, string(filepath.Separator)) + ctrPath = filepath.Join(ctr.WorkingDir(), ctrPath) + + if endsWithSep { + ctrPath = ctrPath + string(filepath.Separator) + } + } + if isVol, volDestName, volName := isVolumeDestName(ctrPath, ctr); isVol { //nolint(gocritic) + newMountPoint, path, err := pathWithVolumeMount(runtime, volDestName, volName, ctrPath) + if err != nil { + return "", "", errors.Wrapf(err, "error getting source path from volume %s", volDestName) + } + + mountPoint = newMountPoint + ctrPath = path + } else if isBindMount, mount := isBindMountDestName(ctrPath, ctr); isBindMount { //nolint(gocritic) + newMountPoint, path := pathWithBindMountSource(mount, ctrPath) + mountPoint = newMountPoint + ctrPath = path + } + + return mountPoint, ctrPath, nil +} + +func isVolumeDestName(path string, ctr *libpod.Container) (bool, string, string) { + separator := string(os.PathSeparator) + + if filepath.IsAbs(path) { + path = strings.TrimPrefix(path, separator) + } + + if path == "" { + return false, "", "" + } + + for _, vol := range ctr.Config().NamedVolumes { + volNamePath := strings.TrimPrefix(vol.Dest, separator) + if matchVolumePath(path, volNamePath) { + return true, vol.Dest, vol.Name + } + } + + return false, "", "" +} + +func pathWithVolumeMount(runtime *libpod.Runtime, volDestName, volName, path string) (string, string, error) { + destVolume, err := runtime.GetVolume(volName) + if err != nil { + return "", "", errors.Wrapf(err, "error getting volume destination %s", volName) + } + + if !filepath.IsAbs(path) { + path = filepath.Join(string(os.PathSeparator), path) + } + + return destVolume.MountPoint(), strings.TrimPrefix(path, volDestName), err +} + +func isBindMountDestName(path string, ctr *libpod.Container) (bool, specs.Mount) { + separator := string(os.PathSeparator) + + if filepath.IsAbs(path) { + path = strings.TrimPrefix(path, string(os.PathSeparator)) + } + + if path == "" { + return false, specs.Mount{} + } + + for _, m := range ctr.Config().Spec.Mounts { + if m.Type != "bind" { + continue + } + + mDest := strings.TrimPrefix(m.Destination, separator) + if matchVolumePath(path, mDest) { + return true, m + } + } + + return false, specs.Mount{} +} + +func matchVolumePath(path, target string) bool { + pathStr := filepath.Clean(path) + target = filepath.Clean(target) + + for len(pathStr) > len(target) && strings.Contains(pathStr, string(os.PathSeparator)) { + pathStr = pathStr[:strings.LastIndex(pathStr, string(os.PathSeparator))] + } + + return pathStr == target +} + +func pathWithBindMountSource(m specs.Mount, path string) (string, string) { + if !filepath.IsAbs(path) { + path = filepath.Join(string(os.PathSeparator), path) + } + + return m.Source, strings.TrimPrefix(path, m.Destination) } diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 55264b3b6..be5a394de 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -422,8 +422,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { source := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path if _, err := utils.ParseStorageReference(source); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", source)) + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } @@ -433,8 +432,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { } if _, err := utils.ParseDockerReference(destination); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "image destination %q is not a docker-transport reference", destination)) + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 05e4b8258..5e2727e95 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -53,8 +53,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { imageRef, err := utils.ParseDockerReference(query.Reference) if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "image destination %q is not a docker-transport reference", query.Reference)) + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 185b724fc..1439a3a75 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -1,14 +1,11 @@ package utils import ( - "context" "net/http" "time" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/domain/entities" - createconfig "github.com/containers/podman/v2/pkg/spec" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -59,18 +56,3 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { } return con.WaitForConditionWithInterval(interval, condition) } - -func CreateContainer(ctx context.Context, w http.ResponseWriter, runtime *libpod.Runtime, cc *createconfig.CreateConfig) { - var pod *libpod.Pod - ctr, err := createconfig.CreateContainerFromCreateConfig(ctx, runtime, cc, pod) - if err != nil { - Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "CreateContainerFromCreateConfig()")) - return - } - - response := entities.ContainerCreateResponse{ - ID: ctr.ID(), - Warnings: []string{}} - - WriteResponse(w, http.StatusCreated, response) -} diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index ed40a37ab..b4e8446cb 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -3,6 +3,7 @@ package entities import ( "sort" "strings" + "time" "github.com/containers/podman/v2/pkg/ps/define" "github.com/cri-o/ocicni/pkg/ocicni" @@ -14,7 +15,7 @@ type ListContainer struct { // Container command Command []string // Container creation time - Created int64 + Created time.Time // Human readable container creation time. CreatedAt string // If container has exited/stopped @@ -137,7 +138,7 @@ func (a psSortedSize) Less(i, j int) bool { type PsSortedCreateTime struct{ SortListContainers } func (a PsSortedCreateTime) Less(i, j int) bool { - return a.SortListContainers[i].Created < a.SortListContainers[j].Created + return a.SortListContainers[i].Created.Before(a.SortListContainers[j].Created) } func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainers, error) { diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index b051d3eec..df7da616a 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -75,7 +75,6 @@ type ContainerEngine interface { Shutdown(ctx context.Context) SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error) Unshare(ctx context.Context, args []string) error - VarlinkService(ctx context.Context, opts ServiceOptions) error Version(ctx context.Context) (*SystemVersionReport, error) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 4bcc6469c..3aeb6a2ee 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -8,7 +8,6 @@ import ( "os" "strings" - "github.com/containers/buildah/pkg/parse" "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/image" @@ -24,13 +23,6 @@ import ( v1 "k8s.io/api/core/v1" ) -const ( - // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath - kubeDirectoryPermission = 0755 - // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath - kubeFilePermission = 0644 -) - func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { var ( kubeObject v1.ObjectReference @@ -168,62 +160,9 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY DockerInsecureSkipTLSVerify: options.SkipTLSVerify, } - // map from name to mount point - volumes := make(map[string]string) - for _, volume := range podYAML.Spec.Volumes { - hostPath := volume.VolumeSource.HostPath - if hostPath == nil { - return nil, errors.Errorf("HostPath is currently the only supported VolumeSource") - } - if hostPath.Type != nil { - switch *hostPath.Type { - case v1.HostPathDirectoryOrCreate: - if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { - if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil { - return nil, err - } - } - // Label a newly created volume - if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) - } - case v1.HostPathFileOrCreate: - if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { - f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) - if err != nil { - return nil, errors.Wrap(err, "error creating HostPath") - } - if err := f.Close(); err != nil { - logrus.Warnf("Error in closing newly created HostPath file: %v", err) - } - } - // unconditionally label a newly created volume - if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) - } - case v1.HostPathSocket: - st, err := os.Stat(hostPath.Path) - if err != nil { - return nil, errors.Wrap(err, "error checking HostPathSocket") - } - if st.Mode()&os.ModeSocket != os.ModeSocket { - return nil, errors.Errorf("error checking HostPathSocket: path %s is not a socket", hostPath.Path) - } - - case v1.HostPathDirectory: - case v1.HostPathFile: - case v1.HostPathUnset: - // do nothing here because we will verify the path exists in validateVolumeHostDir - break - default: - return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type) - } - } - - if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error in parsing HostPath in YAML") - } - volumes[volume.Name] = hostPath.Path + volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes) + if err != nil { + return nil, err } seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot) diff --git a/pkg/domain/infra/abi/system_novalink.go b/pkg/domain/infra/abi/system_novalink.go deleted file mode 100644 index 3518e9db6..000000000 --- a/pkg/domain/infra/abi/system_novalink.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !varlink - -package abi - -import ( - "context" - - "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/pkg/errors" -) - -func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { - return errors.Errorf("varlink is not supported") -} diff --git a/pkg/domain/infra/abi/system_varlink.go b/pkg/domain/infra/abi/system_varlink.go deleted file mode 100644 index ead84fc84..000000000 --- a/pkg/domain/infra/abi/system_varlink.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build varlink - -package abi - -import ( - "context" - - "github.com/containers/podman/v2/pkg/domain/entities" - iopodman "github.com/containers/podman/v2/pkg/varlink" - iopodmanAPI "github.com/containers/podman/v2/pkg/varlinkapi" - "github.com/containers/podman/v2/version" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/varlink/go/varlink" -) - -func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { - var varlinkInterfaces = []*iopodman.VarlinkInterface{ - iopodmanAPI.New(opts.Command, ic.Libpod), - } - - service, err := varlink.NewService( - "Atomic", - "podman", - version.Version.String(), - "https://github.com/containers/podman", - ) - if err != nil { - return errors.Wrapf(err, "unable to create new varlink service") - } - - for _, i := range varlinkInterfaces { - if err := service.RegisterInterface(i); err != nil { - return errors.Errorf("unable to register varlink interface %v", i) - } - } - - // Run the varlink server at the given address - if err = service.Listen(opts.URI, opts.Timeout); err != nil { - switch err.(type) { - case varlink.ServiceTimeoutError: - logrus.Infof("varlink service expired (use --time to increase session time beyond %s ms, 0 means never timeout)", opts.Timeout.String()) - return nil - default: - return errors.Wrapf(err, "unable to start varlink service") - } - } - return nil -} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 1aa5afbe7..63677719b 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -235,7 +235,7 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, if len(options.ImageName) > 0 { ref, err := reference.Parse(options.ImageName) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "error parsing reference %q", options.ImageName) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 61ac2141c..09931de12 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -119,7 +119,7 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, ) ref, err := reference.Parse(newTag) if err != nil { - return err + return errors.Wrapf(err, "error parsing reference %q", newTag) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() @@ -148,7 +148,7 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string ) ref, err := reference.Parse(newTag) if err != nil { - return err + return errors.Wrapf(err, "error parsing reference %q", newTag) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index c276e15c5..f3e8fbcb1 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -14,10 +14,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { return system.Info(ic.ClientCxt) } -func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error { - panic(errors.New("varlink service is not supported when tunneling")) -} - func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { panic(errors.New("rootless engine mode is not supported when tunneling")) } diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 3dd7eb0c6..cfdf3ee49 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -180,7 +180,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities ps := entities.ListContainer{ Command: conConfig.Command, - Created: conConfig.CreatedTime.Unix(), + Created: conConfig.CreatedTime, Exited: exited, ExitCode: exitCode, ExitedAt: exitedTime.Unix(), @@ -231,7 +231,7 @@ func ListStorageContainer(rt *libpod.Runtime, ctr storage.Container, opts entiti ps := entities.ListContainer{ ID: ctr.ID, - Created: ctr.Created.Unix(), + Created: ctr.Created, ImageID: ctr.ImageID, State: "storage", Names: []string{name}, @@ -301,5 +301,5 @@ func (a SortPSContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } type SortPSCreateTime struct{ SortPSContainers } func (a SortPSCreateTime) Less(i, j int) bool { - return a.SortPSContainers[i].Created > a.SortPSContainers[j].Created + return a.SortPSContainers[i].Created.Before(a.SortPSContainers[j].Created) } diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go deleted file mode 100644 index 319cce61f..000000000 --- a/pkg/spec/config_linux.go +++ /dev/null @@ -1,371 +0,0 @@ -// +build linux - -package createconfig - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/containers/podman/v2/pkg/rootless" - "github.com/opencontainers/runc/libcontainer/configs" - "github.com/opencontainers/runc/libcontainer/devices" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object. -func Device(d *configs.Device) spec.LinuxDevice { - return spec.LinuxDevice{ - Type: string(d.Type), - Path: d.Path, - Major: d.Major, - Minor: d.Minor, - FileMode: fmPtr(int64(d.FileMode)), - UID: u32Ptr(int64(d.Uid)), - GID: u32Ptr(int64(d.Gid)), - } -} - -// DevicesFromPath computes a list of devices -func DevicesFromPath(g *generate.Generator, devicePath string) error { - devs := strings.Split(devicePath, ":") - resolvedDevicePath := devs[0] - // check if it is a symbolic link - if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink { - if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil { - resolvedDevicePath = linkedPathOnHost - } - } - st, err := os.Stat(resolvedDevicePath) - if err != nil { - return errors.Wrapf(err, "cannot stat %s", devicePath) - } - if st.IsDir() { - found := false - src := resolvedDevicePath - dest := src - var devmode string - if len(devs) > 1 { - if len(devs[1]) > 0 && devs[1][0] == '/' { - dest = devs[1] - } else { - devmode = devs[1] - } - } - if len(devs) > 2 { - if devmode != "" { - return errors.Wrapf(unix.EINVAL, "invalid device specification %s", devicePath) - } - devmode = devs[2] - } - - // mount the internal devices recursively - if err := filepath.Walk(resolvedDevicePath, func(dpath string, f os.FileInfo, e error) error { - - if f.Mode()&os.ModeDevice == os.ModeDevice { - found = true - device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src))) - if devmode != "" { - device = fmt.Sprintf("%s:%s", device, devmode) - } - if err := addDevice(g, device); err != nil { - return errors.Wrapf(err, "failed to add %s device", dpath) - } - } - return nil - }); err != nil { - return err - } - if !found { - return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath) - } - return nil - } - - return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) -} - -func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error { - for _, deviceCgroupRule := range deviceCgroupRules { - if err := validateDeviceCgroupRule(deviceCgroupRule); err != nil { - return err - } - ss := parseDeviceCgroupRule(deviceCgroupRule) - if len(ss[0]) != 5 { - return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) - } - matches := ss[0] - var major, minor *int64 - if matches[2] == "*" { - majorDev := int64(-1) - major = &majorDev - } else { - majorDev, err := strconv.ParseInt(matches[2], 10, 64) - if err != nil { - return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) - } - major = &majorDev - } - if matches[3] == "*" { - minorDev := int64(-1) - minor = &minorDev - } else { - minorDev, err := strconv.ParseInt(matches[2], 10, 64) - if err != nil { - return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) - } - minor = &minorDev - } - g.AddLinuxResourcesDevice(true, matches[1], major, minor, matches[4]) - } - return nil -} - -func addDevice(g *generate.Generator, device string) error { - src, dst, permissions, err := ParseDevice(device) - if err != nil { - return err - } - dev, err := devices.DeviceFromPath(src, permissions) - if err != nil { - return errors.Wrapf(err, "%s is not a valid device", src) - } - if rootless.IsRootless() { - if _, err := os.Stat(src); err != nil { - if os.IsNotExist(err) { - return errors.Wrapf(err, "the specified device %s doesn't exist", src) - } - return errors.Wrapf(err, "stat device %s exist", src) - } - perm := "ro" - if strings.Contains(permissions, "w") { - perm = "rw" - } - devMnt := spec.Mount{ - Destination: dst, - Type: TypeBind, - Source: src, - Options: []string{"slave", "nosuid", "noexec", perm, "rbind"}, - } - g.Config.Mounts = append(g.Config.Mounts, devMnt) - return nil - } - dev.Path = dst - linuxdev := spec.LinuxDevice{ - Path: dev.Path, - Type: string(dev.Type), - Major: dev.Major, - Minor: dev.Minor, - FileMode: &dev.FileMode, - UID: &dev.Uid, - GID: &dev.Gid, - } - g.AddDevice(linuxdev) - g.AddLinuxResourcesDevice(true, string(dev.Type), &dev.Major, &dev.Minor, string(dev.Permissions)) - return nil -} - -// based on getDevices from runc (libcontainer/devices/devices.go) -func getDevices(path string) ([]*configs.Device, error) { - files, err := ioutil.ReadDir(path) - if err != nil { - if rootless.IsRootless() && os.IsPermission(err) { - return nil, nil - } - return nil, err - } - out := []*configs.Device{} - for _, f := range files { - switch { - case f.IsDir(): - switch f.Name() { - // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 - case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": - continue - default: - sub, err := getDevices(filepath.Join(path, f.Name())) - if err != nil { - return nil, err - } - if sub != nil { - out = append(out, sub...) - } - continue - } - case f.Name() == "console": - continue - case f.Mode()&os.ModeSymlink != 0: - // do not add symlink'd devices to privileged devices - continue - } - device, err := devices.DeviceFromPath(filepath.Join(path, f.Name()), "rwm") - if err != nil { - if err == devices.ErrNotADevice { - continue - } - if os.IsNotExist(err) { - continue - } - return nil, err - } - out = append(out, device) - } - return out, nil -} - -func addPrivilegedDevices(g *generate.Generator) error { - hostDevices, err := getDevices("/dev") - if err != nil { - return err - } - g.ClearLinuxDevices() - - if rootless.IsRootless() { - mounts := make(map[string]interface{}) - for _, m := range g.Mounts() { - mounts[m.Destination] = true - } - newMounts := []spec.Mount{} - for _, d := range hostDevices { - devMnt := spec.Mount{ - Destination: d.Path, - Type: TypeBind, - Source: d.Path, - Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"}, - } - if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") { - continue - } - if _, found := mounts[d.Path]; found { - continue - } - st, err := os.Stat(d.Path) - if err != nil { - if err == unix.EPERM { - continue - } - return errors.Wrapf(err, "stat %s", d.Path) - } - // Skip devices that the user has not access to. - if st.Mode()&0007 == 0 { - continue - } - newMounts = append(newMounts, devMnt) - } - g.Config.Mounts = append(newMounts, g.Config.Mounts...) - g.Config.Linux.Resources.Devices = nil - } else { - for _, d := range hostDevices { - g.AddDevice(Device(d)) - } - // Add resources device - need to clear the existing one first. - g.Config.Linux.Resources.Devices = nil - g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") - } - - return nil -} - -func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) { - var ret *spec.LinuxBlockIO - bio := &spec.LinuxBlockIO{} - if c.Resources.BlkioWeight > 0 { - ret = bio - bio.Weight = &c.Resources.BlkioWeight - } - if len(c.Resources.BlkioWeightDevice) > 0 { - var lwds []spec.LinuxWeightDevice - ret = bio - for _, i := range c.Resources.BlkioWeightDevice { - wd, err := ValidateweightDevice(i) - if err != nil { - return ret, errors.Wrapf(err, "invalid values for blkio-weight-device") - } - wdStat, err := GetStatFromPath(wd.Path) - if err != nil { - return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path) - } - lwd := spec.LinuxWeightDevice{ - Weight: &wd.Weight, - } - lwd.Major = int64(unix.Major(wdStat.Rdev)) - lwd.Minor = int64(unix.Minor(wdStat.Rdev)) - lwds = append(lwds, lwd) - } - bio.WeightDevice = lwds - } - if len(c.Resources.DeviceReadBps) > 0 { - ret = bio - readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps) - if err != nil { - return ret, err - } - bio.ThrottleReadBpsDevice = readBps - } - if len(c.Resources.DeviceWriteBps) > 0 { - ret = bio - writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps) - if err != nil { - return ret, err - } - bio.ThrottleWriteBpsDevice = writeBpds - } - if len(c.Resources.DeviceReadIOps) > 0 { - ret = bio - readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops) - if err != nil { - return ret, err - } - bio.ThrottleReadIOPSDevice = readIOps - } - if len(c.Resources.DeviceWriteIOps) > 0 { - ret = bio - writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops) - if err != nil { - return ret, err - } - bio.ThrottleWriteIOPSDevice = writeIOps - } - return ret, nil -} - -func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) { - var ( - ltds []spec.LinuxThrottleDevice - t *throttleDevice - err error - ) - for _, i := range throttleInput { - if rateType == bps { - t, err = validateBpsDevice(i) - } else { - t, err = validateIOpsDevice(i) - } - if err != nil { - return []spec.LinuxThrottleDevice{}, err - } - ltdStat, err := GetStatFromPath(t.path) - if err != nil { - return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path) - } - ltd := spec.LinuxThrottleDevice{ - Rate: t.rate, - } - ltd.Major = int64(unix.Major(ltdStat.Rdev)) - ltd.Minor = int64(unix.Minor(ltdStat.Rdev)) - ltds = append(ltds, ltd) - } - return ltds, nil -} - -func GetStatFromPath(path string) (unix.Stat_t, error) { - s := unix.Stat_t{} - err := unix.Stat(path, &s) - return s, err -} diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go deleted file mode 100644 index d0891b574..000000000 --- a/pkg/spec/config_linux_cgo.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build linux,cgo - -package createconfig - -import ( - "io/ioutil" - - goSeccomp "github.com/containers/common/pkg/seccomp" - "github.com/containers/podman/v2/pkg/seccomp" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { - var seccompConfig *spec.LinuxSeccomp - var err error - - if config.SeccompPolicy == seccomp.PolicyImage && config.SeccompProfileFromImage != "" { - logrus.Debug("Loading seccomp profile from the security config") - seccompConfig, err = goSeccomp.LoadProfile(config.SeccompProfileFromImage, configSpec) - if err != nil { - return nil, errors.Wrap(err, "loading seccomp profile failed") - } - return seccompConfig, nil - } - - if config.SeccompProfilePath != "" { - logrus.Debugf("Loading seccomp profile from %q", config.SeccompProfilePath) - seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath) - if err != nil { - return nil, errors.Wrap(err, "opening seccomp profile failed") - } - seccompConfig, err = goSeccomp.LoadProfile(string(seccompProfile), configSpec) - if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) - } - } else { - logrus.Debug("Loading default seccomp profile") - seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec) - if err != nil { - return nil, errors.Wrapf(err, "loading default seccomp profile failed") - } - } - - return seccompConfig, nil -} diff --git a/pkg/spec/config_linux_nocgo.go b/pkg/spec/config_linux_nocgo.go deleted file mode 100644 index 8d720b6d4..000000000 --- a/pkg/spec/config_linux_nocgo.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build linux,!cgo - -package createconfig - -import ( - spec "github.com/opencontainers/runtime-spec/specs-go" -) - -func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { - return nil, nil -} diff --git a/pkg/spec/config_unsupported.go b/pkg/spec/config_unsupported.go deleted file mode 100644 index 568afde55..000000000 --- a/pkg/spec/config_unsupported.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build !linux - -package createconfig - -import ( - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" -) - -func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { - return nil, errors.New("function not supported on non-linux OS's") -} -func addDevice(g *generate.Generator, device string) error { - return errors.New("function not implemented") -} - -func addPrivilegedDevices(g *generate.Generator) error { - return errors.New("function not implemented") -} - -func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) { - return nil, errors.New("function not implemented") -} - -func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) { - return nil, errors.New("function not implemented") -} - -func DevicesFromPath(g *generate.Generator, devicePath string) error { - return errors.New("function not implemented") -} - -func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error { - return errors.New("function not implemented") -} diff --git a/pkg/spec/containerconfig.go b/pkg/spec/containerconfig.go deleted file mode 100644 index f11d85b7e..000000000 --- a/pkg/spec/containerconfig.go +++ /dev/null @@ -1,41 +0,0 @@ -package createconfig - -import ( - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// MakeContainerConfig generates all configuration necessary to start a -// container with libpod from a completed CreateConfig struct. -func (config *CreateConfig) MakeContainerConfig(runtime *libpod.Runtime, pod *libpod.Pod) (*spec.Spec, []libpod.CtrCreateOption, error) { - if config.Pod != "" && pod == nil { - return nil, nil, errors.Wrapf(define.ErrInvalidArg, "pod was specified but no pod passed") - } else if config.Pod == "" && pod != nil { - return nil, nil, errors.Wrapf(define.ErrInvalidArg, "pod was given but no pod is specified") - } - - // Parse volumes flag into OCI spec mounts and libpod Named Volumes. - // If there is an identical mount in the OCI spec, we will replace it - // with a mount generated here. - mounts, namedVolumes, err := config.parseVolumes(runtime) - if err != nil { - return nil, nil, err - } - - runtimeSpec, err := config.createConfigToOCISpec(runtime, mounts) - if err != nil { - return nil, nil, err - } - - options, err := config.getContainerCreateOptions(runtime, pod, mounts, namedVolumes) - if err != nil { - return nil, nil, err - } - - logrus.Debugf("created OCI spec and options for new container") - - return runtimeSpec, options, nil -} diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go deleted file mode 100644 index 4887e9262..000000000 --- a/pkg/spec/createconfig.go +++ /dev/null @@ -1,426 +0,0 @@ -package createconfig - -import ( - "context" - "os" - "strconv" - "strings" - "syscall" - - "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/namespaces" - "github.com/containers/podman/v2/pkg/seccomp" - "github.com/containers/storage" - "github.com/docker/go-connections/nat" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// Type constants -const ( - bps = iota - iops -) - -// CreateResourceConfig represents resource elements in CreateConfig -// structures -type CreateResourceConfig struct { - BlkioWeight uint16 // blkio-weight - BlkioWeightDevice []string // blkio-weight-device - CgroupConf map[string]string - CPUPeriod uint64 // cpu-period - CPUQuota int64 // cpu-quota - CPURtPeriod uint64 // cpu-rt-period - CPURtRuntime int64 // cpu-rt-runtime - CPUShares uint64 // cpu-shares - CPUs float64 // cpus - CPUsetCPUs string - CPUsetMems string // cpuset-mems - DeviceCgroupRules []string //device-cgroup-rule - DeviceReadBps []string // device-read-bps - DeviceReadIOps []string // device-read-iops - DeviceWriteBps []string // device-write-bps - DeviceWriteIOps []string // device-write-iops - DisableOomKiller bool // oom-kill-disable - KernelMemory int64 // kernel-memory - Memory int64 //memory - MemoryReservation int64 // memory-reservation - MemorySwap int64 //memory-swap - MemorySwappiness int // memory-swappiness - OomScoreAdj int //oom-score-adj - PidsLimit int64 // pids-limit - ShmSize int64 - Ulimit []string //ulimit -} - -// PidConfig configures the pid namespace for the container -type PidConfig struct { - PidMode namespaces.PidMode //pid -} - -// IpcConfig configures the ipc namespace for the container -type IpcConfig struct { - IpcMode namespaces.IpcMode //ipc -} - -// CgroupConfig configures the cgroup namespace for the container -type CgroupConfig struct { - Cgroups string - Cgroupns string - CgroupParent string // cgroup-parent - CgroupMode namespaces.CgroupMode //cgroup -} - -// UserConfig configures the user namespace for the container -type UserConfig struct { - GroupAdd []string // group-add - IDMappings *storage.IDMappingOptions - UsernsMode namespaces.UsernsMode //userns - User string //user -} - -// UtsConfig configures the uts namespace for the container -type UtsConfig struct { - UtsMode namespaces.UTSMode //uts - NoHosts bool - HostAdd []string //add-host - Hostname string -} - -// NetworkConfig configures the network namespace for the container -type NetworkConfig struct { - DNSOpt []string //dns-opt - DNSSearch []string //dns-search - DNSServers []string //dns - ExposedPorts map[nat.Port]struct{} - HTTPProxy bool - IP6Address string //ipv6 - IPAddress string //ip - LinkLocalIP []string // link-local-ip - MacAddress string //mac-address - NetMode namespaces.NetworkMode //net - Network string //network - NetworkAlias []string //network-alias - PortBindings nat.PortMap - Publish []string //publish - PublishAll bool //publish-all -} - -// SecurityConfig configures the security features for the container -type SecurityConfig struct { - CapAdd []string // cap-add - CapDrop []string // cap-drop - CapRequired []string // cap-required - LabelOpts []string //SecurityOpts - NoNewPrivs bool //SecurityOpts - ApparmorProfile string //SecurityOpts - SeccompProfilePath string //SecurityOpts - SeccompProfileFromImage string // seccomp profile from the container image - SeccompPolicy seccomp.Policy - SecurityOpts []string - Privileged bool //privileged - ReadOnlyRootfs bool //read-only - ReadOnlyTmpfs bool //read-only-tmpfs - Sysctl map[string]string //sysctl - ProcOpts []string -} - -// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI -// swagger:model CreateConfig -type CreateConfig struct { - Annotations map[string]string - Args []string - CidFile string - ConmonPidFile string - Command []string // Full command that will be used - UserCommand []string // User-entered command (or image CMD) - Detach bool // detach - Devices []string // device - Entrypoint []string //entrypoint - Env map[string]string //env - HealthCheck *manifest.Schema2HealthConfig - Init bool // init - InitPath string //init-path - Image string - ImageID string - RawImageName string - BuiltinImgVolumes map[string]struct{} // volumes defined in the image config - ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore - Interactive bool //interactive - Labels map[string]string //label - LogDriver string // log-driver - LogDriverOpt []string // log-opt - Name string //name - PodmanPath string - Pod string //pod - Quiet bool //quiet - Resources CreateResourceConfig - RestartPolicy string - Rm bool //rm - Rmi bool //rmi - StopSignal syscall.Signal // stop-signal - StopTimeout uint // stop-timeout - Systemd bool - Tmpfs []string // tmpfs - Tty bool //tty - Mounts []spec.Mount - MountsFlag []string // mounts - NamedVolumes []*libpod.ContainerNamedVolume - Volumes []string //volume - VolumesFrom []string - WorkDir string //workdir - Rootfs string - Security SecurityConfig - Syslog bool // Whether to enable syslog on exit commands - - // Namespaces - Pid PidConfig - Ipc IpcConfig - Cgroup CgroupConfig - User UserConfig - Uts UtsConfig - Network NetworkConfig -} - -func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } -func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } - -// CreateBlockIO returns a LinuxBlockIO struct from a CreateConfig -func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) { - return c.createBlockIO() -} - -func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, error) { - config, err := runtime.GetConfig() - if err != nil { - return nil, err - } - storageConfig := runtime.StorageConfig() - - // We need a cleanup process for containers in the current model. - // But we can't assume that the caller is Podman - it could be another - // user of the API. - // As such, provide a way to specify a path to Podman, so we can - // still invoke a cleanup process. - cmd := c.PodmanPath - if cmd == "" { - cmd, _ = os.Executable() - } - - command := []string{cmd, - "--root", storageConfig.GraphRoot, - "--runroot", storageConfig.RunRoot, - "--log-level", logrus.GetLevel().String(), - "--cgroup-manager", config.Engine.CgroupManager, - "--tmpdir", config.Engine.TmpDir, - } - if config.Engine.OCIRuntime != "" { - command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...) - } - if storageConfig.GraphDriverName != "" { - command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...) - } - for _, opt := range storageConfig.GraphDriverOptions { - command = append(command, []string{"--storage-opt", opt}...) - } - if config.Engine.EventsLogger != "" { - command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...) - } - - if c.Syslog { - command = append(command, "--syslog", "true") - } - command = append(command, []string{"container", "cleanup"}...) - - if c.Rm { - command = append(command, "--rm") - } - - if c.Rmi { - command = append(command, "--rmi") - } - - return command, nil -} - -// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions -func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) { - var options []libpod.CtrCreateOption - var err error - - if c.Interactive { - options = append(options, libpod.WithStdin()) - } - if c.Systemd { - options = append(options, libpod.WithSystemd()) - } - if c.Name != "" { - logrus.Debugf("setting container name %s", c.Name) - options = append(options, libpod.WithName(c.Name)) - } - if c.Pod != "" { - logrus.Debugf("adding container to pod %s", c.Pod) - options = append(options, runtime.WithPod(pod)) - } - - // handle some spec from the InfraContainer when it's a pod - if pod != nil && pod.HasInfraContainer() { - InfraCtr, err := pod.InfraContainer() - if err != nil { - return nil, err - } - // handle the pod.spec.hostAliases - options = append(options, libpod.WithHosts(InfraCtr.HostsAdd())) - } - - if len(mounts) != 0 || len(namedVolumes) != 0 { - destinations := []string{} - - // Take all mount and named volume destinations. - for _, mount := range mounts { - destinations = append(destinations, mount.Destination) - } - for _, volume := range namedVolumes { - destinations = append(destinations, volume.Dest) - } - - options = append(options, libpod.WithUserVolumes(destinations)) - } - - if len(namedVolumes) != 0 { - options = append(options, libpod.WithNamedVolumes(namedVolumes)) - } - - if len(c.UserCommand) != 0 { - options = append(options, libpod.WithCommand(c.UserCommand)) - } - - // Add entrypoint if it was set - // If it's empty it's because it was explicitly set to "" - if c.Entrypoint != nil { - options = append(options, libpod.WithEntrypoint(c.Entrypoint)) - } - - // TODO: MNT, USER, CGROUP - options = append(options, libpod.WithStopSignal(c.StopSignal)) - options = append(options, libpod.WithStopTimeout(c.StopTimeout)) - - logPath, logTag := getLoggingOpts(c.LogDriverOpt) - if logPath != "" { - options = append(options, libpod.WithLogPath(logPath)) - } - if logTag != "" { - options = append(options, libpod.WithLogTag(logTag)) - } - - if c.LogDriver != "" { - options = append(options, libpod.WithLogDriver(c.LogDriver)) - } - - secOpts, err := c.Security.ToCreateOptions() - if err != nil { - return nil, err - } - options = append(options, secOpts...) - - nsOpts, err := c.Cgroup.ToCreateOptions(runtime) - if err != nil { - return nil, err - } - options = append(options, nsOpts...) - - nsOpts, err = c.Ipc.ToCreateOptions(runtime) - if err != nil { - return nil, err - } - options = append(options, nsOpts...) - - nsOpts, err = c.Pid.ToCreateOptions(runtime) - if err != nil { - return nil, err - } - options = append(options, nsOpts...) - - nsOpts, err = c.Network.ToCreateOptions(runtime, &c.User) - if err != nil { - return nil, err - } - options = append(options, nsOpts...) - - nsOpts, err = c.Uts.ToCreateOptions(runtime, pod) - if err != nil { - return nil, err - } - options = append(options, nsOpts...) - - nsOpts, err = c.User.ToCreateOptions(runtime) - if err != nil { - return nil, err - } - options = append(options, nsOpts...) - - // Gather up the options for NewContainer which consist of With... funcs - options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, c.RawImageName)) - options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile)) - options = append(options, libpod.WithLabels(c.Labels)) - options = append(options, libpod.WithShmSize(c.Resources.ShmSize)) - if c.Rootfs != "" { - options = append(options, libpod.WithRootFS(c.Rootfs)) - } - // Default used if not overridden on command line - - if c.RestartPolicy != "" { - if c.RestartPolicy == "unless-stopped" { - return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported") - } - - split := strings.Split(c.RestartPolicy, ":") - if len(split) > 1 { - numTries, err := strconv.Atoi(split[1]) - if err != nil { - return nil, errors.Wrapf(err, "%s is not a valid number of retries for restart policy", split[1]) - } - if numTries < 0 { - return nil, errors.Wrapf(define.ErrInvalidArg, "restart policy requires a positive number of retries") - } - options = append(options, libpod.WithRestartRetries(uint(numTries))) - } - options = append(options, libpod.WithRestartPolicy(split[0])) - } - - // Always use a cleanup process to clean up Podman after termination - exitCmd, err := c.createExitCommand(runtime) - if err != nil { - return nil, err - } - options = append(options, libpod.WithExitCommand(exitCmd)) - - if c.HealthCheck != nil { - options = append(options, libpod.WithHealthCheck(c.HealthCheck)) - logrus.Debugf("New container has a health check") - } - return options, nil -} - -// AddPrivilegedDevices iterates through host devices and adds all -// host devices to the spec -func AddPrivilegedDevices(g *generate.Generator) error { - return addPrivilegedDevices(g) -} - -func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *CreateConfig, pod *libpod.Pod) (*libpod.Container, error) { - runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod) - if err != nil { - return nil, err - } - - ctr, err := r.NewContainer(ctx, runtimeSpec, options...) - if err != nil { - return nil, err - } - return ctr, nil -} diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go deleted file mode 100644 index 8f98a9786..000000000 --- a/pkg/spec/namespaces.go +++ /dev/null @@ -1,463 +0,0 @@ -package createconfig - -import ( - "net" - "os" - "strconv" - "strings" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/cgroups" - "github.com/cri-o/ocicni/pkg/ocicni" - "github.com/docker/go-connections/nat" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// DefaultKernelNamespaces is a comma-separated list of default kernel -// namespaces. -const DefaultKernelNamespaces = "cgroup,ipc,net,uts" - -// ToCreateOptions converts the input to a slice of container create options. -func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) { - var portBindings []ocicni.PortMapping - var err error - if len(c.PortBindings) > 0 { - portBindings, err = NatToOCIPortBindings(c.PortBindings) - if err != nil { - return nil, errors.Wrapf(err, "unable to create port bindings") - } - } - - options := make([]libpod.CtrCreateOption, 0) - userNetworks := c.NetMode.UserDefined() - networks := make([]string, 0) - - if IsPod(userNetworks) { - userNetworks = "" - } - if userNetworks != "" { - for _, netName := range strings.Split(userNetworks, ",") { - if netName == "" { - return nil, errors.Errorf("container networks %q invalid", userNetworks) - } - networks = append(networks, netName) - } - } - - switch { - case c.NetMode.IsNS(): - ns := c.NetMode.NS() - if ns == "" { - return nil, errors.Errorf("invalid empty user-defined network namespace") - } - _, err := os.Stat(ns) - if err != nil { - return nil, err - } - case c.NetMode.IsContainer(): - connectedCtr, err := runtime.LookupContainer(c.NetMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container()) - } - options = append(options, libpod.WithNetNSFrom(connectedCtr)) - case !c.NetMode.IsHost() && !c.NetMode.IsNone(): - postConfigureNetNS := userns.getPostConfigureNetNS() - options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks)) - } - - if len(c.DNSSearch) > 0 { - options = append(options, libpod.WithDNSSearch(c.DNSSearch)) - } - if len(c.DNSServers) > 0 { - if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" { - options = append(options, libpod.WithUseImageResolvConf()) - } else { - options = append(options, libpod.WithDNS(c.DNSServers)) - } - } - if len(c.DNSOpt) > 0 { - options = append(options, libpod.WithDNSOption(c.DNSOpt)) - } - if c.IPAddress != "" { - ip := net.ParseIP(c.IPAddress) - if ip == nil { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress) - } else if ip.To4() == nil { - return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress) - } - options = append(options, libpod.WithStaticIP(ip)) - } - - if c.MacAddress != "" { - mac, err := net.ParseMAC(c.MacAddress) - if err != nil { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err) - } - options = append(options, libpod.WithStaticMAC(mac)) - } - - return options, nil -} - -// ConfigureGenerator configures the generator based according to the current -// state of the NetworkConfig. -func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error { - netMode := c.NetMode - netCtr := netMode.Container() - switch { - case netMode.IsHost(): - logrus.Debug("Using host netmode") - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return err - } - case netMode.IsNone(): - logrus.Debug("Using none netmode") - case netMode.IsBridge(): - logrus.Debug("Using bridge netmode") - case netCtr != "": - logrus.Debugf("using container %s netmode", netCtr) - case IsNS(string(netMode)): - logrus.Debug("Using ns netmode") - if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil { - return err - } - case IsPod(string(netMode)): - logrus.Debug("Using pod netmode, unless pod is not sharing") - case netMode.IsSlirp4netns(): - logrus.Debug("Using slirp4netns netmode") - case netMode.IsUserDefined(): - logrus.Debug("Using user defined netmode") - default: - return errors.Errorf("unknown network mode") - } - - if c.HTTPProxy { - for _, envSpec := range []string{ - "http_proxy", - "HTTP_PROXY", - "https_proxy", - "HTTPS_PROXY", - "ftp_proxy", - "FTP_PROXY", - "no_proxy", - "NO_PROXY", - } { - envVal := os.Getenv(envSpec) - if envVal != "" { - g.AddProcessEnv(envSpec, envVal) - } - } - } - - if g.Config.Annotations == nil { - g.Config.Annotations = make(map[string]string) - } - - if c.PublishAll { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue - } else { - g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse - } - - return nil -} - -// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice -func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) { - var portBindings []ocicni.PortMapping - for containerPb, hostPb := range ports { - var pm ocicni.PortMapping - pm.ContainerPort = int32(containerPb.Int()) - for _, i := range hostPb { - var hostPort int - var err error - pm.HostIP = i.HostIP - if i.HostPort == "" { - hostPort = containerPb.Int() - } else { - hostPort, err = strconv.Atoi(i.HostPort) - if err != nil { - return nil, errors.Wrapf(err, "unable to convert host port to integer") - } - } - - pm.HostPort = int32(hostPort) - pm.Protocol = containerPb.Proto() - portBindings = append(portBindings, pm) - } - } - return portBindings, nil -} - -// ToCreateOptions converts the input to container create options. -func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { - options := make([]libpod.CtrCreateOption, 0) - if c.CgroupMode.IsNS() { - ns := c.CgroupMode.NS() - if ns == "" { - return nil, errors.Errorf("invalid empty user-defined network namespace") - } - _, err := os.Stat(ns) - if err != nil { - return nil, err - } - } else if c.CgroupMode.IsContainer() { - connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container()) - } - options = append(options, libpod.WithCgroupNSFrom(connectedCtr)) - } - - if c.CgroupParent != "" { - options = append(options, libpod.WithCgroupParent(c.CgroupParent)) - } - - if c.Cgroups != "" { - options = append(options, libpod.WithCgroupsMode(c.Cgroups)) - } - - return options, nil -} - -// ToCreateOptions converts the input to container create options. -func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { - options := make([]libpod.CtrCreateOption, 0) - switch { - case c.UsernsMode.IsNS(): - ns := c.UsernsMode.NS() - if ns == "" { - return nil, errors.Errorf("invalid empty user-defined user namespace") - } - _, err := os.Stat(ns) - if err != nil { - return nil, err - } - options = append(options, libpod.WithIDMappings(*c.IDMappings)) - case c.UsernsMode.IsContainer(): - connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container()) - } - options = append(options, libpod.WithUserNSFrom(connectedCtr)) - default: - options = append(options, libpod.WithIDMappings(*c.IDMappings)) - } - - options = append(options, libpod.WithUser(c.User)) - options = append(options, libpod.WithGroups(c.GroupAdd)) - - return options, nil -} - -// ConfigureGenerator configures the generator according to the current state -// of the UserConfig. -func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error { - if IsNS(string(c.UsernsMode)) { - if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil { - return err - } - // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping - g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) - g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) - } - - if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() { - if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { - return err - } - } - for _, uidmap := range c.IDMappings.UIDMap { - g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) - } - for _, gidmap := range c.IDMappings.GIDMap { - g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) - } - return nil -} - -func (c *UserConfig) getPostConfigureNetNS() bool { - hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0 - postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost() - return postConfigureNetNS -} - -// InNS returns true if the UserConfig indicates to be in a dedicated user -// namespace. -func (c *UserConfig) InNS(isRootless bool) bool { - hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0 - return isRootless || (hasUserns && !c.UsernsMode.IsHost()) -} - -// ToCreateOptions converts the input to container create options. -func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { - options := make([]libpod.CtrCreateOption, 0) - if c.IpcMode.IsHost() { - options = append(options, libpod.WithShmDir("/dev/shm")) - } else if c.IpcMode.IsContainer() { - connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container()) - } - - options = append(options, libpod.WithIPCNSFrom(connectedCtr)) - options = append(options, libpod.WithShmDir(connectedCtr.ShmDir())) - } - - return options, nil -} - -// ConfigureGenerator configures the generator according to the current state -// of the IpcConfig. -func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error { - ipcMode := c.IpcMode - if IsNS(string(ipcMode)) { - return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode))) - } - if ipcMode.IsHost() { - return g.RemoveLinuxNamespace(string(spec.IPCNamespace)) - } - if ipcCtr := ipcMode.Container(); ipcCtr != "" { - logrus.Debugf("Using container %s ipcmode", ipcCtr) - } - - return nil -} - -// ConfigureGenerator configures the generator according to the current state -// of the CgroupConfig. -func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error { - cgroupMode := c.CgroupMode - if cgroupMode.IsDefaultValue() { - // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1. - unified, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - return err - } - if unified { - cgroupMode = "private" - } else { - cgroupMode = "host" - } - } - if cgroupMode.IsNS() { - return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode))) - } - if cgroupMode.IsHost() { - return g.RemoveLinuxNamespace(string(spec.CgroupNamespace)) - } - if cgroupMode.IsPrivate() { - return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "") - } - if cgCtr := cgroupMode.Container(); cgCtr != "" { - logrus.Debugf("Using container %s cgroup mode", cgCtr) - } - return nil -} - -// ToCreateOptions converts the input to container create options. -func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { - options := make([]libpod.CtrCreateOption, 0) - if c.PidMode.IsContainer() { - connectedCtr, err := runtime.LookupContainer(c.PidMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container()) - } - - options = append(options, libpod.WithPIDNSFrom(connectedCtr)) - } - - return options, nil -} - -// ConfigureGenerator configures the generator according to the current state -// of the PidConfig. -func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error { - pidMode := c.PidMode - if IsNS(string(pidMode)) { - return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode))) - } - if pidMode.IsHost() { - return g.RemoveLinuxNamespace(string(spec.PIDNamespace)) - } - if pidCtr := pidMode.Container(); pidCtr != "" { - logrus.Debugf("using container %s pidmode", pidCtr) - } - if IsPod(string(pidMode)) { - logrus.Debug("using pod pidmode") - } - return nil -} - -// ToCreateOptions converts the input to container create options. -func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) { - options := make([]libpod.CtrCreateOption, 0) - if IsPod(string(c.UtsMode)) { - options = append(options, libpod.WithUTSNSFromPod(pod)) - } - if c.UtsMode.IsContainer() { - connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container()) - } - - options = append(options, libpod.WithUTSNSFrom(connectedCtr)) - } - if c.NoHosts { - options = append(options, libpod.WithUseImageHosts()) - } - if len(c.HostAdd) > 0 && !c.NoHosts { - options = append(options, libpod.WithHosts(c.HostAdd)) - } - - return options, nil -} - -// ConfigureGenerator configures the generator according to the current state -// of the UtsConfig. -func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error { - hostname := c.Hostname - utsCtrID := c.UtsMode.Container() - var err error - if hostname == "" { - switch { - case utsCtrID != "": - utsCtr, err := runtime.LookupContainer(utsCtrID) - if err != nil { - return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID) - } - hostname = utsCtr.Hostname() - case net.NetMode.IsHost() || c.UtsMode.IsHost(): - hostname, err = os.Hostname() - if err != nil { - return errors.Wrap(err, "unable to retrieve hostname of the host") - } - default: - logrus.Debug("No hostname set; container's hostname will default to runtime default") - } - } - g.RemoveHostname() - if c.Hostname != "" || !c.UtsMode.IsHost() { - // Set the hostname in the OCI configuration only - // if specified by the user or if we are creating - // a new UTS namespace. - g.SetHostname(hostname) - } - g.AddProcessEnv("HOSTNAME", hostname) - - utsMode := c.UtsMode - if IsNS(string(utsMode)) { - return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode))) - } - if utsMode.IsHost() { - return g.RemoveLinuxNamespace(string(spec.UTSNamespace)) - } - if utsCtr := utsMode.Container(); utsCtr != "" { - logrus.Debugf("using container %s utsmode", utsCtr) - } - return nil -} diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go deleted file mode 100644 index 9ebcf8d29..000000000 --- a/pkg/spec/parse.go +++ /dev/null @@ -1,225 +0,0 @@ -package createconfig - -import ( - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/docker/go-units" - "github.com/pkg/errors" -) - -// deviceCgroupRulegex defines the valid format of device-cgroup-rule -var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`) - -// Pod signifies a kernel namespace is being shared -// by a container with the pod it is associated with -const Pod = "pod" - -// weightDevice is a structure that holds device:weight pair -type weightDevice struct { - Path string - Weight uint16 -} - -func (w *weightDevice) String() string { - return fmt.Sprintf("%s:%d", w.Path, w.Weight) -} - -// LinuxNS is a struct that contains namespace information -// It implemented Valid to show it is a valid namespace -type LinuxNS interface { - Valid() bool -} - -// IsNS returns if the specified string has a ns: prefix -func IsNS(s string) bool { - parts := strings.SplitN(s, ":", 2) - return len(parts) > 1 && parts[0] == "ns" -} - -// IsPod returns if the specified string is pod -func IsPod(s string) bool { - return s == Pod -} - -// Valid checks the validity of a linux namespace -// s should be the string representation of ns -func Valid(s string, ns LinuxNS) bool { - return IsPod(s) || IsNS(s) || ns.Valid() -} - -// NS is the path to the namespace to join. -func NS(s string) string { - parts := strings.SplitN(s, ":", 2) - if len(parts) > 1 { - return parts[1] - } - return "" -} - -// ValidateweightDevice validates that the specified string has a valid device-weight format -// for blkio-weight-device flag -func ValidateweightDevice(val string) (*weightDevice, error) { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - weight, err := strconv.ParseUint(split[1], 10, 0) - if err != nil { - return nil, fmt.Errorf("invalid weight for device: %s", val) - } - if weight > 0 && (weight < 10 || weight > 1000) { - return nil, fmt.Errorf("invalid weight for device: %s", val) - } - - return &weightDevice{ - Path: split[0], - Weight: uint16(weight), - }, nil -} - -// throttleDevice is a structure that holds device:rate_per_second pair -type throttleDevice struct { - path string - rate uint64 -} - -func (t *throttleDevice) String() string { - return fmt.Sprintf("%s:%d", t.path, t.rate) -} - -// validateBpsDevice validates that the specified string has a valid device-rate format -// for device-read-bps and device-write-bps flags -func validateBpsDevice(val string) (*throttleDevice, error) { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := units.RAMInBytes(split[1]) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - if rate < 0 { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - - return &throttleDevice{ - path: split[0], - rate: uint64(rate), - }, nil -} - -// validateIOpsDevice validates that the specified string has a valid device-rate format -// for device-write-iops and device-read-iops flags -func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := strconv.ParseUint(split[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val) - } - return &throttleDevice{ - path: split[0], - rate: rate, - }, nil -} - -// getLoggingOpts splits the path= and tag= options provided to --log-opt. -func getLoggingOpts(opts []string) (string, string) { - var path, tag string - for _, opt := range opts { - arr := strings.SplitN(opt, "=", 2) - if len(arr) == 2 { - if strings.TrimSpace(arr[0]) == "path" { - path = strings.TrimSpace(arr[1]) - } else if strings.TrimSpace(arr[0]) == "tag" { - tag = strings.TrimSpace(arr[1]) - } - } - if path != "" && tag != "" { - break - } - } - return path, tag -} - -// ParseDevice parses device mapping string to a src, dest & permissions string -func ParseDevice(device string) (string, string, string, error) { //nolint - src := "" - dst := "" - permissions := "rwm" - arr := strings.Split(device, ":") - switch len(arr) { - case 3: - if !IsValidDeviceMode(arr[2]) { - return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2]) - } - permissions = arr[2] - fallthrough - case 2: - if IsValidDeviceMode(arr[1]) { - permissions = arr[1] - } else { - if len(arr[1]) == 0 || arr[1][0] != '/' { - return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1]) - } - dst = arr[1] - } - fallthrough - case 1: - src = arr[0] - default: - return "", "", "", fmt.Errorf("invalid device specification: %s", device) - } - - if dst == "" { - dst = src - } - return src, dst, permissions, nil -} - -// IsValidDeviceMode checks if the mode for device is valid or not. -// IsValid mode is a composition of r (read), w (write), and m (mknod). -func IsValidDeviceMode(mode string) bool { - var legalDeviceMode = map[rune]bool{ - 'r': true, - 'w': true, - 'm': true, - } - if mode == "" { - return false - } - for _, c := range mode { - if !legalDeviceMode[c] { - return false - } - legalDeviceMode[c] = false - } - return true -} - -// validateDeviceCgroupRule validates the format of deviceCgroupRule -func validateDeviceCgroupRule(deviceCgroupRule string) error { - if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) { - return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) - } - return nil -} - -// parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice -func parseDeviceCgroupRule(deviceCgroupRule string) [][]string { - return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) -} diff --git a/pkg/spec/ports.go b/pkg/spec/ports.go deleted file mode 100644 index bdd26bd83..000000000 --- a/pkg/spec/ports.go +++ /dev/null @@ -1,107 +0,0 @@ -package createconfig - -import ( - "fmt" - "net" - "strconv" - - "github.com/docker/go-connections/nat" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ExposedPorts parses user and image ports and returns binding information -func ExposedPorts(expose, publish []string, publishAll bool, imageExposedPorts map[string]struct{}) (map[nat.Port][]nat.PortBinding, error) { - containerPorts := make(map[string]string) - - // add expose ports from the image itself - for expose := range imageExposedPorts { - _, port := nat.SplitProtoPort(expose) - containerPorts[port] = "" - } - - // add the expose ports from the user (--expose) - // can be single or a range - for _, expose := range expose { - //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] - _, port := nat.SplitProtoPort(expose) - //parse the start and end port and create a sequence of ports to expose - //if expose a port, the start and end port are the same - start, end, err := nat.ParsePortRange(port) - if err != nil { - return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err) - } - for i := start; i <= end; i++ { - containerPorts[strconv.Itoa(int(i))] = "" - } - } - - // parse user inputted port bindings - pbPorts, portBindings, err := nat.ParsePortSpecs(publish) - if err != nil { - return nil, err - } - - // delete exposed container ports if being used by -p - for i := range pbPorts { - delete(containerPorts, i.Port()) - } - - // iterate container ports and make port bindings from them - if publishAll { - for e := range containerPorts { - //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] - //proto, port := nat.SplitProtoPort(e) - p, err := nat.NewPort("tcp", e) - if err != nil { - return nil, err - } - rp, err := getRandomPort() - if err != nil { - return nil, err - } - logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int())) - portBindings[p] = CreatePortBinding(rp, "") - } - } - - // We need to see if any host ports are not populated and if so, we need to assign a - // random port to them. - for k, pb := range portBindings { - if pb[0].HostPort == "" { - hostPort, err := getRandomPort() - if err != nil { - return nil, err - } - logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port())) - pb[0].HostPort = strconv.Itoa(hostPort) - } - } - return portBindings, nil -} - -func getRandomPort() (int, error) { - l, err := net.Listen("tcp", ":0") - if err != nil { - return 0, errors.Wrapf(err, "unable to get free port") - } - defer l.Close() - _, randomPort, err := net.SplitHostPort(l.Addr().String()) - if err != nil { - return 0, errors.Wrapf(err, "unable to determine free port") - } - rp, err := strconv.Atoi(randomPort) - if err != nil { - return 0, errors.Wrapf(err, "unable to convert random port to int") - } - return rp, nil -} - -//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs -func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding { - pb := nat.PortBinding{ - HostPort: strconv.Itoa(hostPort), - } - pb.HostIP = hostIP - return []nat.PortBinding{pb} -} diff --git a/pkg/spec/security.go b/pkg/spec/security.go deleted file mode 100644 index 5f7db7edb..000000000 --- a/pkg/spec/security.go +++ /dev/null @@ -1,204 +0,0 @@ -package createconfig - -import ( - "fmt" - "strings" - - "github.com/containers/common/pkg/capabilities" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/util" - "github.com/opencontainers/runtime-tools/generate" - "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ToCreateOptions convert the SecurityConfig to a slice of container create -// options. -func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) { - options := make([]libpod.CtrCreateOption, 0) - options = append(options, libpod.WithSecLabels(c.LabelOpts)) - options = append(options, libpod.WithPrivileged(c.Privileged)) - return options, nil -} - -// SetLabelOpts sets the label options of the SecurityConfig according to the -// input. -func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error { - if c.Privileged { - c.LabelOpts = label.DisableSecOpt() - return nil - } - - var labelOpts []string - if pidConfig.PidMode.IsHost() { - labelOpts = append(labelOpts, label.DisableSecOpt()...) - } else if pidConfig.PidMode.IsContainer() { - ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container()) - if err != nil { - return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container()) - } - secopts, err := label.DupSecOpt(ctr.ProcessLabel()) - if err != nil { - return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) - } - labelOpts = append(labelOpts, secopts...) - } - - if ipcConfig.IpcMode.IsHost() { - labelOpts = append(labelOpts, label.DisableSecOpt()...) - } else if ipcConfig.IpcMode.IsContainer() { - ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container()) - if err != nil { - return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container()) - } - secopts, err := label.DupSecOpt(ctr.ProcessLabel()) - if err != nil { - return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) - } - labelOpts = append(labelOpts, secopts...) - } - - c.LabelOpts = append(c.LabelOpts, labelOpts...) - return nil -} - -// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.). -func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error { - for _, opt := range securityOpts { - if opt == "no-new-privileges" { - c.NoNewPrivs = true - } else { - con := strings.SplitN(opt, "=", 2) - if len(con) != 2 { - return fmt.Errorf("invalid --security-opt 1: %q", opt) - } - - switch con[0] { - case "proc-opts": - c.ProcOpts = strings.Split(con[1], ",") - case "label": - c.LabelOpts = append(c.LabelOpts, con[1]) - case "apparmor": - c.ApparmorProfile = con[1] - case "seccomp": - c.SeccompProfilePath = con[1] - default: - return fmt.Errorf("invalid --security-opt 2: %q", opt) - } - } - } - - if c.SeccompProfilePath == "" { - var err error - c.SeccompProfilePath, err = libpod.DefaultSeccompPath() - if err != nil { - return err - } - } - c.SecurityOpts = securityOpts - return nil -} - -// ConfigureGenerator configures the generator according to the input. -func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error { - // HANDLE CAPABILITIES - // NOTE: Must happen before SECCOMP - if c.Privileged { - g.SetupPrivileged(true) - } - - useNotRoot := func(user string) bool { - if user == "" || user == "root" || user == "0" { - return false - } - return true - } - - configSpec := g.Config - var err error - var defaultCaplist []string - bounding := configSpec.Process.Capabilities.Bounding - if useNotRoot(user.User) { - configSpec.Process.Capabilities.Bounding = defaultCaplist - } - defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop) - if err != nil { - return err - } - - privCapRequired := []string{} - - if !c.Privileged && len(c.CapRequired) > 0 { - // Pass CapRequired in CapAdd field to normalize capabilities names - capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil) - if err != nil { - logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ",")) - } else { - // Verify all capRequiered are in the defaultCapList - for _, cap := range capRequired { - if !util.StringInSlice(cap, defaultCaplist) { - privCapRequired = append(privCapRequired, cap) - } - } - } - if len(privCapRequired) == 0 { - defaultCaplist = capRequired - } else { - logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ",")) - } - } - configSpec.Process.Capabilities.Bounding = defaultCaplist - configSpec.Process.Capabilities.Permitted = defaultCaplist - configSpec.Process.Capabilities.Inheritable = defaultCaplist - configSpec.Process.Capabilities.Effective = defaultCaplist - configSpec.Process.Capabilities.Ambient = defaultCaplist - if useNotRoot(user.User) { - defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop) - if err != nil { - return err - } - } - configSpec.Process.Capabilities.Bounding = defaultCaplist - - // HANDLE SECCOMP - if c.SeccompProfilePath != "unconfined" { - seccompConfig, err := getSeccompConfig(c, configSpec) - if err != nil { - return err - } - configSpec.Linux.Seccomp = seccompConfig - } - - // Clear default Seccomp profile from Generator for privileged containers - if c.SeccompProfilePath == "unconfined" || c.Privileged { - configSpec.Linux.Seccomp = nil - } - - for _, opt := range c.SecurityOpts { - // Split on both : and = - splitOpt := strings.SplitN(opt, "=", 2) - if len(splitOpt) == 1 { - splitOpt = strings.Split(opt, ":") - } - if len(splitOpt) < 2 { - continue - } - switch splitOpt[0] { - case "label": - configSpec.Annotations[define.InspectAnnotationLabel] = splitOpt[1] - case "seccomp": - configSpec.Annotations[define.InspectAnnotationSeccomp] = splitOpt[1] - case "apparmor": - configSpec.Annotations[define.InspectAnnotationApparmor] = splitOpt[1] - } - } - - g.SetRootReadonly(c.ReadOnlyRootfs) - for sysctlKey, sysctlVal := range c.Sysctl { - g.AddLinuxSysctl(sysctlKey, sysctlVal) - } - - return nil -} diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go deleted file mode 100644 index 81620997f..000000000 --- a/pkg/spec/spec.go +++ /dev/null @@ -1,593 +0,0 @@ -package createconfig - -import ( - "strings" - - "github.com/containers/common/pkg/capabilities" - cconfig "github.com/containers/common/pkg/config" - "github.com/containers/common/pkg/sysinfo" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/cgroups" - "github.com/containers/podman/v2/pkg/env" - "github.com/containers/podman/v2/pkg/rootless" - "github.com/containers/podman/v2/pkg/util" - "github.com/docker/go-units" - "github.com/opencontainers/runc/libcontainer/user" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" -) - -const CpuPeriod = 100000 - -func GetAvailableGids() (int64, error) { - idMap, err := user.ParseIDMapFile("/proc/self/gid_map") - if err != nil { - return 0, err - } - count := int64(0) - for _, r := range idMap { - count += r.Count - } - return count, nil -} - -// CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec -func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userMounts []spec.Mount) (*spec.Spec, error) { - cgroupPerm := "ro" - g, err := generate.New("linux") - if err != nil { - return nil, err - } - // Remove the default /dev/shm mount to ensure we overwrite it - g.RemoveMount("/dev/shm") - g.HostSpecific = true - addCgroup := true - canMountSys := true - - isRootless := rootless.IsRootless() - inUserNS := config.User.InNS(isRootless) - - if inUserNS && config.Network.NetMode.IsHost() { - canMountSys = false - } - - if config.Security.Privileged && canMountSys { - cgroupPerm = "rw" - g.RemoveMount("/sys") - sysMnt := spec.Mount{ - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, - } - g.AddMount(sysMnt) - } else if !canMountSys { - addCgroup = false - g.RemoveMount("/sys") - r := "ro" - if config.Security.Privileged { - r = "rw" - } - sysMnt := spec.Mount{ - Destination: "/sys", - Type: TypeBind, - Source: "/sys", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, - } - g.AddMount(sysMnt) - if !config.Security.Privileged && isRootless { - g.AddLinuxMaskedPaths("/sys/kernel") - } - } - var runtimeConfig *cconfig.Config - - if runtime != nil { - runtimeConfig, err = runtime.GetConfig() - if err != nil { - return nil, err - } - g.Config.Process.Capabilities.Bounding = runtimeConfig.Containers.DefaultCapabilities - sysctls, err := util.ValidateSysctls(runtimeConfig.Containers.DefaultSysctls) - if err != nil { - return nil, err - } - - for name, val := range config.Security.Sysctl { - sysctls[name] = val - } - config.Security.Sysctl = sysctls - if !util.StringInSlice("host", config.Resources.Ulimit) { - config.Resources.Ulimit = append(runtimeConfig.Containers.DefaultUlimits, config.Resources.Ulimit...) - } - if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() { - config.Resources.PidsLimit = runtimeConfig.Containers.PidsLimit - } - - } else { - g.Config.Process.Capabilities.Bounding = cconfig.DefaultCapabilities - if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() { - config.Resources.PidsLimit = cconfig.DefaultPidsLimit - } - } - - gid5Available := true - if isRootless { - nGids, err := GetAvailableGids() - if err != nil { - return nil, err - } - gid5Available = nGids >= 5 - } - // When using a different user namespace, check that the GID 5 is mapped inside - // the container. - if gid5Available && len(config.User.IDMappings.GIDMap) > 0 { - mappingFound := false - for _, r := range config.User.IDMappings.GIDMap { - if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { - mappingFound = true - break - } - } - if !mappingFound { - gid5Available = false - } - - } - if !gid5Available { - // If we have no GID mappings, the gid=5 default option would fail, so drop it. - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, - } - g.AddMount(devPts) - } - - if inUserNS && config.Ipc.IpcMode.IsHost() { - g.RemoveMount("/dev/mqueue") - devMqueue := spec.Mount{ - Destination: "/dev/mqueue", - Type: TypeBind, - Source: "/dev/mqueue", - Options: []string{"bind", "nosuid", "noexec", "nodev"}, - } - g.AddMount(devMqueue) - } - if inUserNS && config.Pid.PidMode.IsHost() { - g.RemoveMount("/proc") - procMount := spec.Mount{ - Destination: "/proc", - Type: TypeBind, - Source: "/proc", - Options: []string{"rbind", "nosuid", "noexec", "nodev"}, - } - g.AddMount(procMount) - } - - if addCgroup { - cgroupMnt := spec.Mount{ - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Source: "cgroup", - Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm}, - } - g.AddMount(cgroupMnt) - } - g.SetProcessCwd(config.WorkDir) - - ProcessArgs := make([]string, 0) - // We need to iterate the input for entrypoint because it is a []string - // but "" is a legit json input, which translates into a []string with an - // empty position. This messes up the eventual command being executed - // in the container - for _, a := range config.Entrypoint { - if len(a) > 0 { - ProcessArgs = append(ProcessArgs, a) - } - } - // Same issue as explained above for config.Entrypoint. - for _, a := range config.Command { - if len(a) > 0 { - ProcessArgs = append(ProcessArgs, a) - } - } - - g.SetProcessArgs(ProcessArgs) - g.SetProcessTerminal(config.Tty) - - for key, val := range config.Annotations { - g.AddAnnotation(key, val) - } - - addedResources := false - - // RESOURCES - MEMORY - if config.Resources.Memory != 0 { - g.SetLinuxResourcesMemoryLimit(config.Resources.Memory) - // If a swap limit is not explicitly set, also set a swap limit - // Default to double the memory limit - if config.Resources.MemorySwap == 0 { - g.SetLinuxResourcesMemorySwap(2 * config.Resources.Memory) - } - addedResources = true - } - if config.Resources.MemoryReservation != 0 { - g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation) - addedResources = true - } - if config.Resources.MemorySwap != 0 { - g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap) - addedResources = true - } - if config.Resources.KernelMemory != 0 { - g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory) - addedResources = true - } - if config.Resources.MemorySwappiness != -1 { - g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness)) - addedResources = true - } - g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller) - g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj) - - // RESOURCES - CPU - if config.Resources.CPUShares != 0 { - g.SetLinuxResourcesCPUShares(config.Resources.CPUShares) - addedResources = true - } - if config.Resources.CPUQuota != 0 { - g.SetLinuxResourcesCPUQuota(config.Resources.CPUQuota) - addedResources = true - } - if config.Resources.CPUPeriod != 0 { - g.SetLinuxResourcesCPUPeriod(config.Resources.CPUPeriod) - addedResources = true - } - if config.Resources.CPUs != 0 { - g.SetLinuxResourcesCPUPeriod(CpuPeriod) - g.SetLinuxResourcesCPUQuota(int64(config.Resources.CPUs * CpuPeriod)) - addedResources = true - } - if config.Resources.CPURtRuntime != 0 { - g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CPURtRuntime) - addedResources = true - } - if config.Resources.CPURtPeriod != 0 { - g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CPURtPeriod) - addedResources = true - } - if config.Resources.CPUsetCPUs != "" { - g.SetLinuxResourcesCPUCpus(config.Resources.CPUsetCPUs) - addedResources = true - } - if config.Resources.CPUsetMems != "" { - g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems) - addedResources = true - } - - // Devices - if config.Security.Privileged { - // If privileged, we need to add all the host devices to the - // spec. We do not add the user provided ones because we are - // already adding them all. - if err := AddPrivilegedDevices(&g); err != nil { - return nil, err - } - } else { - for _, devicePath := range config.Devices { - if err := DevicesFromPath(&g, devicePath); err != nil { - return nil, err - } - } - if len(config.Resources.DeviceCgroupRules) != 0 { - if err := deviceCgroupRules(&g, config.Resources.DeviceCgroupRules); err != nil { - return nil, err - } - addedResources = true - } - } - - g.SetProcessNoNewPrivileges(config.Security.NoNewPrivs) - - if !config.Security.Privileged { - g.SetProcessApparmorProfile(config.Security.ApparmorProfile) - } - - // Unless already set via the CLI, check if we need to disable process - // labels or set the defaults. - if len(config.Security.LabelOpts) == 0 && runtimeConfig != nil { - if !runtimeConfig.Containers.EnableLabeling { - // Disabled in the config. - config.Security.LabelOpts = append(config.Security.LabelOpts, "disable") - } else if err := config.Security.SetLabelOpts(runtime, &config.Pid, &config.Ipc); err != nil { - // Defaults! - return nil, err - } - } - - BlockAccessToKernelFilesystems(config.Security.Privileged, config.Pid.PidMode.IsHost(), &g) - - // RESOURCES - PIDS - if config.Resources.PidsLimit > 0 { - // if running on rootless on a cgroupv1 machine or using the cgroupfs manager, pids - // limit is not supported. If the value is still the default - // then ignore the settings. If the caller asked for a - // non-default, then try to use it. - setPidLimit := true - if rootless.IsRootless() { - cgroup2, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - return nil, err - } - if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.Engine.CgroupManager != cconfig.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() { - setPidLimit = false - } - } - if setPidLimit { - g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit) - addedResources = true - } - } - - // Make sure to always set the default variables unless overridden in the - // config. - var defaultEnv map[string]string - if runtimeConfig == nil { - defaultEnv = env.DefaultEnvVariables() - } else { - defaultEnv, err = env.ParseSlice(runtimeConfig.Containers.Env) - if err != nil { - return nil, errors.Wrap(err, "Env fields in containers.conf failed to parse") - } - defaultEnv = env.Join(env.DefaultEnvVariables(), defaultEnv) - } - - if err := addRlimits(config, &g); err != nil { - return nil, err - } - - // NAMESPACES - - if err := config.Pid.ConfigureGenerator(&g); err != nil { - return nil, err - } - - if err := config.User.ConfigureGenerator(&g); err != nil { - return nil, err - } - - if err := config.Network.ConfigureGenerator(&g); err != nil { - return nil, err - } - - if err := config.Uts.ConfigureGenerator(&g, &config.Network, runtime); err != nil { - return nil, err - } - - if err := config.Ipc.ConfigureGenerator(&g); err != nil { - return nil, err - } - - if err := config.Cgroup.ConfigureGenerator(&g); err != nil { - return nil, err - } - - config.Env = env.Join(defaultEnv, config.Env) - for name, val := range config.Env { - g.AddProcessEnv(name, val) - } - configSpec := g.Config - - // If the container image specifies an label with a - // capabilities.ContainerImageLabel then split the comma separated list - // of capabilities and record them. This list indicates the only - // capabilities, required to run the container. - var capRequired []string - for key, val := range config.Labels { - if util.StringInSlice(key, capabilities.ContainerImageLabels) { - capRequired = strings.Split(val, ",") - } - } - config.Security.CapRequired = capRequired - - if err := config.Security.ConfigureGenerator(&g, &config.User); err != nil { - return nil, err - } - - // BIND MOUNTS - configSpec.Mounts = SupercedeUserMounts(userMounts, configSpec.Mounts) - // Process mounts to ensure correct options - if err := InitFSMounts(configSpec.Mounts); err != nil { - return nil, err - } - - // BLOCK IO - blkio, err := config.CreateBlockIO() - if err != nil { - return nil, errors.Wrapf(err, "error creating block io") - } - if blkio != nil { - configSpec.Linux.Resources.BlockIO = blkio - addedResources = true - } - - if rootless.IsRootless() { - cgroup2, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - return nil, err - } - if !addedResources { - configSpec.Linux.Resources = &spec.LinuxResources{} - } - - canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.Engine.CgroupManager == cconfig.SystemdCgroupsManager) - - if addedResources && !canUseResources { - return nil, errors.New("invalid configuration, cannot specify resource limits without cgroups v2 and --cgroup-manager=systemd") - } - if !canUseResources { - // Force the resources block to be empty instead of having default values. - configSpec.Linux.Resources = &spec.LinuxResources{} - } - } - - switch config.Cgroup.Cgroups { - case "disabled": - if addedResources { - return nil, errors.New("cannot specify resource limits when cgroups are disabled is specified") - } - configSpec.Linux.Resources = &spec.LinuxResources{} - case "enabled", "no-conmon", "": - // Do nothing - default: - return nil, errors.New("unrecognized option for cgroups; supported are 'default', 'disabled', 'no-conmon'") - } - - // Add annotations - if configSpec.Annotations == nil { - configSpec.Annotations = make(map[string]string) - } - - if config.CidFile != "" { - configSpec.Annotations[define.InspectAnnotationCIDFile] = config.CidFile - } - - if config.Rm { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse - } - - if len(config.VolumesFrom) > 0 { - configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",") - } - - if config.Security.Privileged { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse - } - - if config.Init { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue - } else { - configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse - } - - return configSpec, nil -} - -func (config *CreateConfig) cgroupDisabled() bool { - return config.Cgroup.Cgroups == "disabled" -} - -func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) { - if !privileged { - for _, mp := range []string{ - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - "/sys/fs/selinux", - } { - g.AddLinuxMaskedPaths(mp) - } - - if pidModeIsHost && rootless.IsRootless() { - return - } - - for _, rp := range []string{ - "/proc/asound", - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger", - } { - g.AddLinuxReadonlyPaths(rp) - } - } -} - -func addRlimits(config *CreateConfig, g *generate.Generator) error { - var ( - isRootless = rootless.IsRootless() - nofileSet = false - nprocSet = false - ) - - for _, u := range config.Resources.Ulimit { - if u == "host" { - if len(config.Resources.Ulimit) != 1 { - return errors.New("ulimit can use host only once") - } - g.Config.Process.Rlimits = nil - break - } - - ul, err := units.ParseUlimit(u) - if err != nil { - return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) - } - - if ul.Name == "nofile" { - nofileSet = true - } else if ul.Name == "nproc" { - nprocSet = true - } - - g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft)) - } - - // If not explicitly overridden by the user, default number of open - // files and number of processes to the maximum they can be set to - // (without overriding a sysctl) - if !nofileSet { - max := define.RLimitDefaultValue - current := define.RLimitDefaultValue - if isRootless { - var rlimit unix.Rlimit - if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { - logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err) - } - if rlimit.Cur < current { - current = rlimit.Cur - } - if rlimit.Max < max { - max = rlimit.Max - } - } - g.AddProcessRlimits("RLIMIT_NOFILE", max, current) - } - if !nprocSet { - max := define.RLimitDefaultValue - current := define.RLimitDefaultValue - if isRootless { - var rlimit unix.Rlimit - if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { - logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err) - } - if rlimit.Cur < current { - current = rlimit.Cur - } - if rlimit.Max < max { - max = rlimit.Max - } - } - g.AddProcessRlimits("RLIMIT_NPROC", max, current) - } - - return nil -} diff --git a/pkg/spec/spec_test.go b/pkg/spec/spec_test.go deleted file mode 100644 index d01102fa2..000000000 --- a/pkg/spec/spec_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package createconfig - -import ( - "runtime" - "testing" - - "github.com/containers/common/pkg/sysinfo" - "github.com/containers/podman/v2/pkg/cgroups" - "github.com/containers/podman/v2/pkg/rootless" - "github.com/containers/storage" - "github.com/containers/storage/pkg/idtools" - "github.com/docker/go-units" - "github.com/stretchr/testify/assert" -) - -var ( - sysInfo = sysinfo.New(true) -) - -// Make createconfig to test with -func makeTestCreateConfig() *CreateConfig { - cc := new(CreateConfig) - cc.Resources = CreateResourceConfig{} - cc.User.IDMappings = new(storage.IDMappingOptions) - cc.User.IDMappings.UIDMap = []idtools.IDMap{} - cc.User.IDMappings.GIDMap = []idtools.IDMap{} - - return cc -} - -func doCommonSkipChecks(t *testing.T) { - // The default configuration of podman enables seccomp, which is not available on non-Linux systems. - // Thus, any tests that use the default seccomp setting would fail. - // Skip the tests on non-Linux platforms rather than explicitly disable seccomp in the test and possibly affect the test result. - if runtime.GOOS != "linux" { - t.Skip("seccomp, which is enabled by default, is only supported on Linux") - } - - if rootless.IsRootless() { - isCgroupV2, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if !isCgroupV2 { - t.Skip("cgroups v1 cannot be used when rootless") - } - } -} - -// TestPIDsLimit verifies the given pid-limit is correctly defined in the spec -func TestPIDsLimit(t *testing.T) { - doCommonSkipChecks(t) - - if !sysInfo.PidsLimit { - t.Skip("running test not supported by the host system") - } - - cc := makeTestCreateConfig() - cc.Resources.PidsLimit = 22 - - spec, err := cc.createConfigToOCISpec(nil, nil) - assert.NoError(t, err) - - assert.Equal(t, spec.Linux.Resources.Pids.Limit, int64(22)) -} - -// TestBLKIOWeightDevice verifies the given blkio weight is correctly set in the -// spec. -func TestBLKIOWeightDevice(t *testing.T) { - doCommonSkipChecks(t) - - if !sysInfo.BlkioWeightDevice { - t.Skip("running test not supported by the host system") - } - - cc := makeTestCreateConfig() - cc.Resources.BlkioWeightDevice = []string{"/dev/zero:100"} - - spec, err := cc.createConfigToOCISpec(nil, nil) - assert.NoError(t, err) - - // /dev/zero is guaranteed 1,5 by the Linux kernel - assert.Equal(t, spec.Linux.Resources.BlockIO.WeightDevice[0].Major, int64(1)) - assert.Equal(t, spec.Linux.Resources.BlockIO.WeightDevice[0].Minor, int64(5)) - assert.Equal(t, *(spec.Linux.Resources.BlockIO.WeightDevice[0].Weight), uint16(100)) -} - -// TestMemorySwap verifies that the given swap memory limit is correctly set in -// the spec. -func TestMemorySwap(t *testing.T) { - doCommonSkipChecks(t) - - if !sysInfo.SwapLimit { - t.Skip("running test not supported by the host system") - } - - swapLimit, err := units.RAMInBytes("45m") - assert.NoError(t, err) - - cc := makeTestCreateConfig() - cc.Resources.MemorySwap = swapLimit - - spec, err := cc.createConfigToOCISpec(nil, nil) - assert.NoError(t, err) - - assert.Equal(t, *(spec.Linux.Resources.Memory.Swap), swapLimit) -} diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go deleted file mode 100644 index b441daf08..000000000 --- a/pkg/spec/storage.go +++ /dev/null @@ -1,875 +0,0 @@ -package createconfig - -import ( - "fmt" - "os" - "path" - "path/filepath" - "strings" - - "github.com/containers/buildah/pkg/parse" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/util" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -const ( - // TypeBind is the type for mounting host dir - TypeBind = "bind" - // TypeVolume is the type for named volumes - TypeVolume = "volume" - // TypeTmpfs is the type for mounting tmpfs - TypeTmpfs = "tmpfs" -) - -var ( - errDuplicateDest = errors.Errorf("duplicate mount destination") - optionArgError = errors.Errorf("must provide an argument for option") - noDestError = errors.Errorf("must set volume destination") -) - -// Parse all volume-related options in the create config into a set of mounts -// and named volumes to add to the container. -// Handles --volumes-from, --volumes, --tmpfs, --init, and --init-path flags. -// TODO: Named volume options - should we default to rprivate? It bakes into a -// bind mount under the hood... -// TODO: handle options parsing/processing via containers/storage/pkg/mount -func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, []*libpod.ContainerNamedVolume, error) { - // Add image volumes. - baseMounts, baseVolumes, err := config.getImageVolumes() - if err != nil { - return nil, nil, err - } - - // Add --volumes-from. - // Overrides image volumes unconditionally. - vFromMounts, vFromVolumes, err := config.getVolumesFrom(runtime) - if err != nil { - return nil, nil, err - } - for dest, mount := range vFromMounts { - baseMounts[dest] = mount - } - for dest, volume := range vFromVolumes { - baseVolumes[dest] = volume - } - - // Next mounts from the --mounts flag. - // Do not override yet. - unifiedMounts, unifiedVolumes, err := config.getMounts() - if err != nil { - return nil, nil, err - } - - // Next --volumes flag. - // Do not override yet. - volumeMounts, volumeVolumes, err := config.getVolumeMounts() - if err != nil { - return nil, nil, err - } - - // Next --tmpfs flag. - // Do not override yet. - tmpfsMounts, err := config.getTmpfsMounts() - if err != nil { - return nil, nil, err - } - - // Unify mounts from --mount, --volume, --tmpfs. - // Also add mounts + volumes directly from createconfig. - // Start with --volume. - for dest, mount := range volumeMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = mount - } - for dest, volume := range volumeVolumes { - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedVolumes[dest] = volume - } - // Now --tmpfs - for dest, tmpfs := range tmpfsMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = tmpfs - } - // Now spec mounts and volumes - for _, mount := range config.Mounts { - dest := mount.Destination - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = mount - } - for _, volume := range config.NamedVolumes { - dest := volume.Dest - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedVolumes[dest] = volume - } - - // If requested, add container init binary - if config.Init { - initPath := config.InitPath - if initPath == "" { - rtc, err := runtime.GetConfig() - if err != nil { - return nil, nil, err - } - initPath = rtc.Engine.InitPath - } - initMount, err := config.addContainerInitBinary(initPath) - if err != nil { - return nil, nil, err - } - if _, ok := unifiedMounts[initMount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) - } - unifiedMounts[initMount.Destination] = initMount - } - - // Before superseding, we need to find volume mounts which conflict with - // named volumes, and vice versa. - // We'll delete the conflicts here as we supersede. - for dest := range unifiedMounts { - if _, ok := baseVolumes[dest]; ok { - delete(baseVolumes, dest) - } - } - for dest := range unifiedVolumes { - if _, ok := baseMounts[dest]; ok { - delete(baseMounts, dest) - } - } - - // Supersede volumes-from/image volumes with unified volumes from above. - // This is an unconditional replacement. - for dest, mount := range unifiedMounts { - baseMounts[dest] = mount - } - for dest, volume := range unifiedVolumes { - baseVolumes[dest] = volume - } - - // If requested, add tmpfs filesystems for read-only containers. - if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs { - readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} - options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} - for _, dest := range readonlyTmpfs { - if _, ok := baseMounts[dest]; ok { - continue - } - if _, ok := baseVolumes[dest]; ok { - continue - } - localOpts := options - if dest == "/run" { - localOpts = append(localOpts, "noexec", "size=65536k") - } else { - localOpts = append(localOpts, "exec") - } - baseMounts[dest] = spec.Mount{ - Destination: dest, - Type: "tmpfs", - Source: "tmpfs", - Options: localOpts, - } - } - } - - // Check for conflicts between named volumes and mounts - for dest := range baseMounts { - if _, ok := baseVolumes[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) - } - } - for dest := range baseVolumes { - if _, ok := baseMounts[dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) - } - } - - // Final step: maps to arrays - finalMounts := make([]spec.Mount, 0, len(baseMounts)) - for _, mount := range baseMounts { - if mount.Type == TypeBind { - absSrc, err := filepath.Abs(mount.Source) - if err != nil { - return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) - } - mount.Source = absSrc - } - finalMounts = append(finalMounts, mount) - } - finalVolumes := make([]*libpod.ContainerNamedVolume, 0, len(baseVolumes)) - for _, volume := range baseVolumes { - finalVolumes = append(finalVolumes, volume) - } - - return finalMounts, finalVolumes, nil -} - -// Parse volumes from - a set of containers whose volumes we will mount in. -// Grab the containers, retrieve any user-created spec mounts and all named -// volumes, and return a list of them. -// Conflicts are resolved simply - the last container specified wins. -// Container names may be suffixed by mount options after a colon. -// TODO: We should clean these paths if possible -func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { - // Both of these are maps of mount destination to mount type. - // We ensure that each destination is only mounted to once in this way. - finalMounts := make(map[string]spec.Mount) - finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume) - - for _, vol := range config.VolumesFrom { - var ( - options = []string{} - err error - splitVol = strings.SplitN(vol, ":", 2) - ) - if len(splitVol) == 2 { - splitOpts := strings.Split(splitVol[1], ",") - for _, checkOpt := range splitOpts { - switch checkOpt { - case "z", "ro", "rw": - // Do nothing, these are valid options - default: - return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1]) - } - } - - if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil { - return nil, nil, err - } - } - ctr, err := runtime.LookupContainer(splitVol[0]) - if err != nil { - return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0]) - } - - logrus.Debugf("Adding volumes from container %s", ctr.ID()) - - // Look up the container's user volumes. This gets us the - // destinations of all mounts the user added to the container. - userVolumesArr := ctr.UserVolumes() - - // We're going to need to access them a lot, so convert to a map - // to reduce looping. - // We'll also use the map to indicate if we missed any volumes along the way. - userVolumes := make(map[string]bool) - for _, dest := range userVolumesArr { - userVolumes[dest] = false - } - - // Now we get the container's spec and loop through its volumes - // and append them in if we can find them. - spec := ctr.Spec() - if spec == nil { - return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID()) - } - for _, mnt := range spec.Mounts { - if mnt.Type != TypeBind { - continue - } - if _, exists := userVolumes[mnt.Destination]; exists { - userVolumes[mnt.Destination] = true - - if len(options) != 0 { - mnt.Options = options - } - - if _, ok := finalMounts[mnt.Destination]; ok { - logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID()) - } - finalMounts[mnt.Destination] = mnt - } - } - - // We're done with the spec mounts. Add named volumes. - // Add these unconditionally - none of them are automatically - // part of the container, as some spec mounts are. - namedVolumes := ctr.NamedVolumes() - for _, namedVol := range namedVolumes { - if _, exists := userVolumes[namedVol.Dest]; exists { - userVolumes[namedVol.Dest] = true - } - - if len(options) != 0 { - namedVol.Options = options - } - - if _, ok := finalMounts[namedVol.Dest]; ok { - logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID()) - } - finalNamedVolumes[namedVol.Dest] = namedVol - } - - // Check if we missed any volumes - for volDest, found := range userVolumes { - if !found { - logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID()) - } - } - } - - return finalMounts, finalNamedVolumes, nil -} - -// getMounts takes user-provided input from the --mount flag and creates OCI -// spec mounts and Libpod named volumes. -// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... -// podman run --mount type=tmpfs,target=/dev/shm ... -// podman run --mount type=volume,source=test-volume, ... -func (config *CreateConfig) getMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { - finalMounts := make(map[string]spec.Mount) - finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume) - - errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]") - - // TODO(vrothberg): the manual parsing can be replaced with a regular expression - // to allow a more robust parsing of the mount format and to give - // precise errors regarding supported format versus supported options. - for _, mount := range config.MountsFlag { - arr := strings.SplitN(mount, ",", 2) - if len(arr) < 2 { - return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount) - } - kv := strings.Split(arr[0], "=") - // TODO: type is not explicitly required in Docker. - // If not specified, it defaults to "volume". - if len(kv) != 2 || kv[0] != "type" { - return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount) - } - - tokens := strings.Split(arr[1], ",") - switch kv[1] { - case TypeBind: - mount, err := getBindMount(tokens) - if err != nil { - return nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case TypeTmpfs: - mount, err := getTmpfsMount(tokens) - if err != nil { - return nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case "volume": - volume, err := getNamedVolume(tokens) - if err != nil { - return nil, nil, err - } - if _, ok := finalNamedVolumes[volume.Dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) - } - finalNamedVolumes[volume.Dest] = volume - default: - return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1]) - } - } - - return finalMounts, finalNamedVolumes, nil -} - -// Parse a single bind mount entry from the --mount flag. -func getBindMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: TypeBind, - } - - var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "bind-nonrecursive": - newMount.Options = append(newMount.Options, "bind") - case "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once") - } - setRORW = true - // Can be formatted as one of: - // ro - // ro=[true|false] - // rw - // rw=[true|false] - switch len(kv) { - case 1: - newMount.Options = append(newMount.Options, kv[0]) - case 2: - switch strings.ToLower(kv[1]) { - case "true": - newMount.Options = append(newMount.Options, kv[0]) - case "false": - // Set the opposite only for rw - // ro's opposite is the default - if kv[0] == "rw" { - newMount.Options = append(newMount.Options, "ro") - } - default: - return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1]) - } - default: - return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) - } - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": - newMount.Options = append(newMount.Options, kv[0]) - case "bind-propagation": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, kv[1]) - case "src", "source": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeHostDir(kv[1]); err != nil { - return newMount, err - } - newMount.Source = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "relabel": - if setRelabel { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once") - } - setRelabel = true - if len(kv) != 2 { - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - switch kv[1] { - case "private": - newMount.Options = append(newMount.Options, "z") - case "shared": - newMount.Options = append(newMount.Options, "Z") - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - if !setSource { - newMount.Source = newMount.Destination - } - - options, err := parse.ValidateVolumeOpts(newMount.Options) - if err != nil { - return newMount, err - } - newMount.Options = options - return newMount, nil -} - -// Parse a single tmpfs mount entry from the --mount flag -func getTmpfsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: TypeTmpfs, - Source: TypeTmpfs, - } - - var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "tmpcopyup", "notmpcopyup": - if setTmpcopyup { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once") - } - setTmpcopyup = true - newMount.Options = append(newMount.Options, kv[0]) - case "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newMount.Options = append(newMount.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "tmpfs-mode": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) - case "tmpfs-size": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) - case "src", "source": - return newMount, errors.Errorf("source is not supported with tmpfs mounts") - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single volume mount entry from the --mount flag. -// Note that the volume-label option for named volumes is currently NOT supported. -// TODO: add support for --volume-label -func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { - newVolume := new(libpod.ContainerNamedVolume) - - var setSource, setDest, setRORW, setSuid, setDev, setExec bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "ro", "rw": - if setRORW { - return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nodev", "dev": - if setDev { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "noexec", "exec": - if setExec { - return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "volume-label": - return nil, errors.Errorf("the --volume-label option is not presently implemented") - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Name = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Dest = filepath.Clean(kv[1]) - setDest = true - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setSource { - return nil, errors.Errorf("must set source volume") - } - if !setDest { - return nil, noDestError - } - - return newVolume, nil -} - -func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { - mounts := make(map[string]spec.Mount) - volumes := make(map[string]*libpod.ContainerNamedVolume) - - volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") - - for _, vol := range config.Volumes { - var ( - options []string - src string - dest string - err error - ) - - splitVol := strings.Split(vol, ":") - if len(splitVol) > 3 { - return nil, nil, errors.Wrapf(volumeFormatErr, vol) - } - - src = splitVol[0] - if len(splitVol) == 1 { - // This is an anonymous named volume. Only thing given - // is destination. - // Name/source will be blank, and populated by libpod. - src = "" - dest = splitVol[0] - } else if len(splitVol) > 1 { - dest = splitVol[1] - } - if len(splitVol) > 2 { - if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil { - return nil, nil, err - } - } - - // Do not check source dir for anonymous volumes - if len(splitVol) > 1 { - if err := parse.ValidateVolumeHostDir(src); err != nil { - return nil, nil, err - } - } - if err := parse.ValidateVolumeCtrDir(dest); err != nil { - return nil, nil, err - } - - cleanDest := filepath.Clean(dest) - - if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { - // This is not a named volume - newMount := spec.Mount{ - Destination: cleanDest, - Type: string(TypeBind), - Source: src, - Options: options, - } - if _, ok := mounts[newMount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination) - } - mounts[newMount.Destination] = newMount - } else { - // This is a named volume - newNamedVol := new(libpod.ContainerNamedVolume) - newNamedVol.Name = src - newNamedVol.Dest = cleanDest - newNamedVol.Options = options - - if _, ok := volumes[newNamedVol.Dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest) - } - volumes[newNamedVol.Dest] = newNamedVol - } - - logrus.Debugf("User mount %s:%s options %v", src, dest, options) - } - - return mounts, volumes, nil -} - -// Get mounts for container's image volumes -func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { - mounts := make(map[string]spec.Mount) - volumes := make(map[string]*libpod.ContainerNamedVolume) - - if config.ImageVolumeType == "ignore" { - return mounts, volumes, nil - } - - for vol := range config.BuiltinImgVolumes { - cleanDest := filepath.Clean(vol) - logrus.Debugf("Adding image volume at %s", cleanDest) - if config.ImageVolumeType == "tmpfs" { - // Tmpfs image volumes are handled as mounts - mount := spec.Mount{ - Destination: cleanDest, - Source: TypeTmpfs, - Type: TypeTmpfs, - Options: []string{"rprivate", "rw", "nodev", "exec"}, - } - mounts[cleanDest] = mount - } else { - // Anonymous volumes have no name. - namedVolume := new(libpod.ContainerNamedVolume) - namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"} - namedVolume.Dest = cleanDest - volumes[cleanDest] = namedVolume - } - } - - return mounts, volumes, nil -} - -// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts -func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) { - m := make(map[string]spec.Mount) - for _, i := range config.Tmpfs { - // Default options if nothing passed - var options []string - spliti := strings.Split(i, ":") - destPath := spliti[0] - if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil { - return nil, err - } - if len(spliti) > 1 { - options = strings.Split(spliti[1], ",") - } - - if _, ok := m[destPath]; ok { - return nil, errors.Wrapf(errDuplicateDest, destPath) - } - - mount := spec.Mount{ - Destination: filepath.Clean(destPath), - Type: string(TypeTmpfs), - Options: options, - Source: string(TypeTmpfs), - } - m[destPath] = mount - } - return m, nil -} - -// AddContainerInitBinary adds the init binary specified by path iff the -// container will run in a private PID namespace that is not shared with the -// host or another pre-existing container, where an init-like process is -// already running. -// -// Note that AddContainerInitBinary prepends "/dev/init" "--" to the command -// to execute the bind-mounted binary as PID 1. -func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, error) { - mount := spec.Mount{ - Destination: "/dev/init", - Type: TypeBind, - Source: path, - Options: []string{TypeBind, "ro"}, - } - - if path == "" { - return mount, fmt.Errorf("please specify a path to the container-init binary") - } - if !config.Pid.PidMode.IsPrivate() { - return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)") - } - if config.Systemd { - return mount, fmt.Errorf("cannot use container-init binary with systemd") - } - if _, err := os.Stat(path); os.IsNotExist(err) { - return mount, errors.Wrap(err, "container-init binary not found on the host") - } - config.Command = append([]string{"/dev/init", "--"}, config.Command...) - return mount, nil -} - -// Supersede existing mounts in the spec with new, user-specified mounts. -// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by -// one mount, and we already have /tmp/a and /tmp/b, should we remove -// the /tmp/a and /tmp/b mounts in favor of the more general /tmp? -func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount { - if len(mounts) > 0 { - // If we have overlappings mounts, remove them from the spec in favor of - // the user-added volume mounts - destinations := make(map[string]bool) - for _, mount := range mounts { - destinations[path.Clean(mount.Destination)] = true - } - // Copy all mounts from spec to defaultMounts, except for - // - mounts overridden by a user supplied mount; - // - all mounts under /dev if a user supplied /dev is present; - mountDev := destinations["/dev"] - for _, mount := range configMount { - if _, ok := destinations[path.Clean(mount.Destination)]; !ok { - if mountDev && strings.HasPrefix(mount.Destination, "/dev/") { - // filter out everything under /dev if /dev is user-mounted - continue - } - - logrus.Debugf("Adding mount %s", mount.Destination) - mounts = append(mounts, mount) - } - } - return mounts - } - return configMount -} - -// Ensure mount options on all mounts are correct -func InitFSMounts(mounts []spec.Mount) error { - for i, m := range mounts { - switch { - case m.Type == TypeBind: - opts, err := util.ProcessOptions(m.Options, false, m.Source) - if err != nil { - return err - } - mounts[i].Options = opts - case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev": - opts, err := util.ProcessOptions(m.Options, true, "") - if err != nil { - return err - } - mounts[i].Options = opts - } - } - return nil -} diff --git a/pkg/spec/storage_test.go b/pkg/spec/storage_test.go deleted file mode 100644 index 04a9d6976..000000000 --- a/pkg/spec/storage_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package createconfig - -import ( - "testing" - - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/stretchr/testify/assert" -) - -func TestGetVolumeMountsOneVolume(t *testing.T) { - data := spec.Mount{ - Destination: "/foobar", - Type: "bind", - Source: "/tmp", - Options: []string{"ro"}, - } - config := CreateConfig{ - Volumes: []string{"/tmp:/foobar:ro"}, - } - specMount, _, err := config.getVolumeMounts() - assert.NoError(t, err) - assert.EqualValues(t, data, specMount[data.Destination]) -} - -func TestGetTmpfsMounts(t *testing.T) { - data := spec.Mount{ - Destination: "/homer", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"rw", "size=787448k", "mode=1777"}, - } - config := CreateConfig{ - Tmpfs: []string{"/homer:rw,size=787448k,mode=1777"}, - } - tmpfsMount, err := config.getTmpfsMounts() - assert.NoError(t, err) - assert.EqualValues(t, data, tmpfsMount[data.Destination]) -} diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 2ee8f2441..c7e62d185 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -257,7 +257,19 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } } - return verifyContainerResources(s) + warnings, err := verifyContainerResources(s) + if err != nil { + return warnings, err + } + + // Warn on net=host/container/pod/none and port mappings. + if (s.NetNS.NSMode == specgen.Host || s.NetNS.NSMode == specgen.FromContainer || + s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.NoNetwork) && + len(s.PortMappings) > 0 { + warnings = append(warnings, "Port mappings have been discarded as one of the Host, Container, Pod, and None network modes are in use") + } + + return warnings, nil } // finishThrottleDevices takes the temporary representation of the throttle diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e1202956c..5f72d28bb 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -47,7 +47,7 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) return p, nil } -func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]string, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) { +func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]*KubeVolume, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) { s := specgen.NewSpecGenerator(iid, false) // podName should be non-empty for Deployment objects to be able to create @@ -163,22 +163,36 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI s.Env = envs for _, volume := range containerYAML.VolumeMounts { - hostPath, exists := volumes[volume.Name] + volumeSource, exists := volumes[volume.Name] if !exists { return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) } - if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil { - return nil, errors.Wrapf(err, "error in parsing MountPath") - } - mount := spec.Mount{ - Destination: volume.MountPath, - Source: hostPath, - Type: "bind", - } - if volume.ReadOnly { - mount.Options = []string{"ro"} + switch volumeSource.Type { + case KubeVolumeTypeBindMount: + if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil { + return nil, errors.Wrapf(err, "error in parsing MountPath") + } + mount := spec.Mount{ + Destination: volume.MountPath, + Source: volumeSource.Source, + Type: "bind", + } + if volume.ReadOnly { + mount.Options = []string{"ro"} + } + s.Mounts = append(s.Mounts, mount) + case KubeVolumeTypeNamed: + namedVolume := specgen.NamedVolume{ + Dest: volume.MountPath, + Name: volumeSource.Source, + } + if volume.ReadOnly { + namedVolume.Options = []string{"ro"} + } + s.Volumes = append(s.Volumes, &namedVolume) + default: + return nil, errors.Errorf("Unsupported volume source type") } - s.Mounts = append(s.Mounts, mount) } s.RestartPolicy = restartPolicy diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go new file mode 100644 index 000000000..2ef0f4c23 --- /dev/null +++ b/pkg/specgen/generate/kube/volume.go @@ -0,0 +1,124 @@ +package kube + +import ( + "os" + + "github.com/containers/buildah/pkg/parse" + "github.com/containers/podman/v2/libpod" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" +) + +const ( + // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath + kubeDirectoryPermission = 0755 + // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath + kubeFilePermission = 0644 +) + +type KubeVolumeType int + +const ( + KubeVolumeTypeBindMount KubeVolumeType = iota + KubeVolumeTypeNamed KubeVolumeType = iota +) + +type KubeVolume struct { + // Type of volume to create + Type KubeVolumeType + // Path for bind mount or volume name for named volume + Source string +} + +// Create a KubeVolume from an HostPathVolumeSource +func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) { + if hostPath.Type != nil { + switch *hostPath.Type { + case v1.HostPathDirectoryOrCreate: + if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { + if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil { + return nil, err + } + } + // Label a newly created volume + if err := libpod.LabelVolumePath(hostPath.Path); err != nil { + return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) + } + case v1.HostPathFileOrCreate: + if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { + f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) + if err != nil { + return nil, errors.Wrap(err, "error creating HostPath") + } + if err := f.Close(); err != nil { + logrus.Warnf("Error in closing newly created HostPath file: %v", err) + } + } + // unconditionally label a newly created volume + if err := libpod.LabelVolumePath(hostPath.Path); err != nil { + return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) + } + case v1.HostPathSocket: + st, err := os.Stat(hostPath.Path) + if err != nil { + return nil, errors.Wrap(err, "error checking HostPathSocket") + } + if st.Mode()&os.ModeSocket != os.ModeSocket { + return nil, errors.Errorf("error checking HostPathSocket: path %s is not a socket", hostPath.Path) + } + + case v1.HostPathDirectory: + case v1.HostPathFile: + case v1.HostPathUnset: + // do nothing here because we will verify the path exists in validateVolumeHostDir + break + default: + return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type) + } + } + + if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { + return nil, errors.Wrapf(err, "error in parsing HostPath in YAML") + } + + return &KubeVolume{ + Type: KubeVolumeTypeBindMount, + Source: hostPath.Path, + }, nil +} + +// Create a KubeVolume from a PersistentVolumeClaimVolumeSource +func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource) (*KubeVolume, error) { + return &KubeVolume{ + Type: KubeVolumeTypeNamed, + Source: claim.ClaimName, + }, nil +} + +// Create a KubeVolume from one of the supported VolumeSource +func VolumeFromSource(volumeSource v1.VolumeSource) (*KubeVolume, error) { + if volumeSource.HostPath != nil { + return VolumeFromHostPath(volumeSource.HostPath) + } else if volumeSource.PersistentVolumeClaim != nil { + return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim) + } else { + return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the conly supported VolumeSource") + } +} + +// Create a map of volume name to KubeVolume +func InitializeVolumes(specVolumes []v1.Volume) (map[string]*KubeVolume, error) { + volumes := make(map[string]*KubeVolume) + + for _, specVolume := range specVolumes { + volume, err := VolumeFromSource(specVolume.VolumeSource) + if err != nil { + return nil, err + } + + volumes[specVolume.Name] = volume + } + + return volumes, nil +} diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 11108a5c1..d15745fa0 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -27,19 +27,25 @@ const ( // Private indicates the namespace is private Private NamespaceMode = "private" // NoNetwork indicates no network namespace should - // be joined. loopback should still exists + // be joined. loopback should still exists. + // Only used with the network namespace, invalid otherwise. NoNetwork NamespaceMode = "none" // Bridge indicates that a CNI network stack - // should be used + // should be used. + // Only used with the network namespace, invalid otherwise. Bridge NamespaceMode = "bridge" // Slirp indicates that a slirp4netns network stack should - // be used + // be used. + // Only used with the network namespace, invalid otherwise. Slirp NamespaceMode = "slirp4netns" // KeepId indicates a user namespace to keep the owner uid inside - // of the namespace itself + // of the namespace itself. + // Only used with the user namespace, invalid otherwise. KeepID NamespaceMode = "keep-id" - // KeepId indicates to automatically create a user namespace + // Auto indicates to automatically create a user namespace. + // Only used with the user namespace, invalid otherwise. Auto NamespaceMode = "auto" + // DefaultKernelNamespaces is a comma-separated list of default kernel // namespaces. DefaultKernelNamespaces = "cgroup,ipc,net,uts" @@ -272,10 +278,16 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { toReturn.NSMode = Private case strings.HasPrefix(ns, "ns:"): split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, nil, errors.Errorf("must provide a path to a namespace when specifying ns:") + } toReturn.NSMode = Path toReturn.Value = split[1] case strings.HasPrefix(ns, "container:"): split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying container:") + } toReturn.NSMode = FromContainer toReturn.Value = split[1] default: diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index 41efee3b3..a4f42d715 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -84,6 +84,7 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na return nil, nil, nil, err } } + // Do not check source dir for anonymous volumes if len(splitVol) > 1 { if len(src) == 0 { diff --git a/pkg/varlink/generate.go b/pkg/varlink/generate.go deleted file mode 100644 index b3f58d4a5..000000000 --- a/pkg/varlink/generate.go +++ /dev/null @@ -1,3 +0,0 @@ -package iopodman - -//go:generate go run ../../vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go io.podman.varlink diff --git a/pkg/varlink/io.podman.varlink b/pkg/varlink/io.podman.varlink deleted file mode 100644 index cd6316011..000000000 --- a/pkg/varlink/io.podman.varlink +++ /dev/null @@ -1,1398 +0,0 @@ -# Podman Service Interface and API description. The master version of this document can be found -# in the [API.md](https://github.com/containers/podman/blob/master/API.md) file in the upstream libpod repository. -interface io.podman - -type Volume ( - name: string, - labels: [string]string, - mountPoint: string, - driver: string, - options: [string]string -) - -type NotImplemented ( - comment: string -) - -type StringResponse ( - message: string -) - -type RemoveImageResponse ( - untagged: []string, - deleted: string -) - -type LogLine ( - device: string, - parseLogType : string, - time: string, - msg: string, - cid: string -) - -# ContainerChanges describes the return struct for ListContainerChanges -type ContainerChanges ( - changed: []string, - added: []string, - deleted: []string -) - -type ImageSaveOptions ( - name: string, - format: string, - output: string, - outputType: string, - moreTags: []string, - quiet: bool, - compress: bool -) - -type VolumeCreateOpts ( - volumeName: string, - driver: string, - labels: [string]string, - options: [string]string -) - -type VolumeRemoveOpts ( - volumes: []string, - all: bool, - force: bool -) - -type Image ( - id: string, - digest: string, - digests: []string, - parentId: string, - repoTags: []string, - repoDigests: []string, - created: string, # as RFC3339 - size: int, - virtualSize: int, - containers: int, - labels: [string]string, - isParent: bool, - topLayer: string, - readOnly: bool, - history: []string -) - -# ImageHistory describes the returned structure from ImageHistory. -type ImageHistory ( - id: string, - created: string, # as RFC3339 - createdBy: string, - tags: []string, - size: int, - comment: string -) - -# Represents a single search result from SearchImages -type ImageSearchResult ( - description: string, - is_official: bool, - is_automated: bool, - registry: string, - name: string, - star_count: int -) - -type ImageSearchFilter ( - is_official: ?bool, - is_automated: ?bool, - star_count: int -) - -type AuthConfig ( - username: string, - password: string -) - -type KubePodService ( - pod: string, - service: string -) - -type Container ( - id: string, - image: string, - imageid: string, - command: []string, - createdat: string, # as RFC3339 - runningfor: string, - status: string, - ports: []ContainerPortMappings, - rootfssize: int, - rwsize: int, - names: string, - labels: [string]string, - mounts: []ContainerMount, - containerrunning: bool, - namespaces: ContainerNameSpace -) - -# ContainerStats is the return struct for the stats of a container -type ContainerStats ( - id: string, - name: string, - cpu: float, - cpu_nano: int, - system_nano: int, - mem_usage: int, - mem_limit: int, - mem_perc: float, - net_input: int, - net_output: int, - block_output: int, - block_input: int, - pids: int -) - -type PsOpts ( - all: bool, - filters: ?[]string, - last: ?int, - latest: ?bool, - noTrunc: ?bool, - pod: ?bool, - quiet: ?bool, - size: ?bool, - sort: ?string, - sync: ?bool -) - -type PsContainer ( - id: string, - image: string, - command: string, - created: string, - ports: string, - names: string, - isInfra: bool, - status: string, - state: string, - pidNum: int, - rootFsSize: int, - rwSize: int, - pod: string, - createdAt: string, - exitedAt: string, - startedAt: string, - labels: [string]string, - nsPid: string, - cgroup: string, - ipc: string, - mnt: string, - net: string, - pidNs: string, - user: string, - uts: string, - mounts: string -) - -# ContainerMount describes the struct for mounts in a container -type ContainerMount ( - destination: string, - type: string, - source: string, - options: []string -) - -# ContainerPortMappings describes the struct for portmappings in an existing container -type ContainerPortMappings ( - host_port: string, - host_ip: string, - protocol: string, - container_port: string -) - -# ContainerNamespace describes the namespace structure for an existing container -type ContainerNameSpace ( - user: string, - uts: string, - pidns: string, - pid: string, - cgroup: string, - net: string, - mnt: string, - ipc: string -) - -# InfoDistribution describes the host's distribution -type InfoDistribution ( - distribution: string, - version: string -) - -# InfoHost describes the host stats portion of PodmanInfo -type InfoHost ( - buildah_version: string, - distribution: InfoDistribution, - mem_free: int, - mem_total: int, - swap_free: int, - swap_total: int, - arch: string, - cpus: int, - hostname: string, - kernel: string, - os: string, - uptime: string, - eventlogger: string -) - -# InfoGraphStatus describes the detailed status of the storage driver -type InfoGraphStatus ( - backing_filesystem: string, - native_overlay_diff: string, - supports_d_type: string -) - -# InfoRegistry describes the host's registry information -type InfoRegistry ( - search: []string, - insecure: []string, - blocked: []string -) - -# InfoStore describes the host's storage information -type InfoStore ( - containers: int, - images: int, - graph_driver_name: string, - graph_driver_options: string, - graph_root: string, - graph_status: InfoGraphStatus, - run_root: string -) - -# InfoPodman provides details on the Podman binary -type InfoPodmanBinary ( - compiler: string, - go_version: string, - podman_version: string, - git_commit: string -) - -# PodmanInfo describes the Podman host and build -type PodmanInfo ( - host: InfoHost, - registries: InfoRegistry, - store: InfoStore, - podman: InfoPodmanBinary -) - -# Sockets describes sockets location for a container -type Sockets( - container_id: string, - io_socket: string, - control_socket: string -) - -# Create is an input structure for creating containers. -# args[0] is the image name or id -# args[1-] are the new commands if changed -type Create ( - args: []string, - addHost: ?[]string, - annotation: ?[]string, - attach: ?[]string, - blkioWeight: ?string, - blkioWeightDevice: ?[]string, - capAdd: ?[]string, - capDrop: ?[]string, - cgroupParent: ?string, - cidFile: ?string, - conmonPidfile: ?string, - command: ?[]string, - cpuPeriod: ?int, - cpuQuota: ?int, - cpuRtPeriod: ?int, - cpuRtRuntime: ?int, - cpuShares: ?int, - cpus: ?float, - cpuSetCpus: ?string, - cpuSetMems: ?string, - detach: ?bool, - detachKeys: ?string, - device: ?[]string, - deviceReadBps: ?[]string, - deviceReadIops: ?[]string, - deviceWriteBps: ?[]string, - deviceWriteIops: ?[]string, - dns: ?[]string, - dnsOpt: ?[]string, - dnsSearch: ?[]string, - dnsServers: ?[]string, - entrypoint: ?string, - env: ?[]string, - envFile: ?[]string, - expose: ?[]string, - gidmap: ?[]string, - groupadd: ?[]string, - healthcheckCommand: ?string, - healthcheckInterval: ?string, - healthcheckRetries: ?int, - healthcheckStartPeriod: ?string, - healthcheckTimeout:?string, - hostname: ?string, - imageVolume: ?string, - init: ?bool, - initPath: ?string, - interactive: ?bool, - ip: ?string, - ipc: ?string, - kernelMemory: ?string, - label: ?[]string, - labelFile: ?[]string, - logDriver: ?string, - logOpt: ?[]string, - macAddress: ?string, - memory: ?string, - memoryReservation: ?string, - memorySwap: ?string, - memorySwappiness: ?int, - name: ?string, - network: ?string, - noHosts: ?bool, - oomKillDisable: ?bool, - oomScoreAdj: ?int, - overrideArch: ?string, - overrideOS: ?string, - pid: ?string, - pidsLimit: ?int, - pod: ?string, - privileged: ?bool, - publish: ?[]string, - publishAll: ?bool, - pull: ?string, - quiet: ?bool, - readonly: ?bool, - readonlytmpfs: ?bool, - restart: ?string, - rm: ?bool, - rootfs: ?bool, - securityOpt: ?[]string, - shmSize: ?string, - stopSignal: ?string, - stopTimeout: ?int, - storageOpt: ?[]string, - subuidname: ?string, - subgidname: ?string, - sysctl: ?[]string, - systemd: ?string, - tmpfs: ?[]string, - tty: ?bool, - uidmap: ?[]string, - ulimit: ?[]string, - user: ?string, - userns: ?string, - uts: ?string, - mount: ?[]string, - volume: ?[]string, - volumesFrom: ?[]string, - workDir: ?string -) - -# 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 ( - architecture: string, - addCapabilities: []string, - additionalTags: []string, - annotations: []string, - buildArgs: [string]string, - buildOptions: BuildOptions, - cniConfigDir: string, - cniPluginDir: string, - compression: string, - contextDir: string, - defaultsMountFilePath: string, - devices: []string, - dockerfiles: []string, - dropCapabilities: []string, - err: string, - forceRmIntermediateCtrs: bool, - iidfile: string, - label: []string, - layers: bool, - nocache: bool, - os: string, - out: string, - output: string, - outputFormat: string, - pullPolicy: string, - quiet: bool, - remoteIntermediateCtrs: bool, - reportWriter: string, - runtimeArgs: []string, - signBy: string, - squash: bool, - target: string, - transientMounts: []string -) - -# MoreResponse is a struct for when responses from varlink requires longer output -type MoreResponse ( - logs: []string, - id: string -) - -# ListPodContainerInfo is a returned struct for describing containers -# in a pod. -type ListPodContainerInfo ( - name: string, - id: string, - status: string -) - -# PodCreate is an input structure for creating pods. -# It emulates options to podman pod create. The infraCommand and -# infraImage options are currently NotSupported. -type PodCreate ( - name: string, - cgroupParent: string, - labels: [string]string, - share: []string, - infra: bool, - infraCommand: string, - infraImage: string, - publish: []string -) - -# ListPodData is the returned struct for an individual pod -type ListPodData ( - id: string, - name: string, - createdat: string, - cgroup: string, - status: string, - labels: [string]string, - numberofcontainers: string, - containersinfo: []ListPodContainerInfo -) - -type PodContainerErrorData ( - containerid: string, - reason: string -) - -# Runlabel describes the required input for container runlabel -type Runlabel( - image: string, - authfile: string, - display: bool, - name: string, - pull: bool, - label: string, - extraArgs: []string, - opts: [string]string -) - -# Event describes a libpod struct -type Event( - # TODO: make status and type a enum at some point? - # id is the container, volume, pod, image ID - id: string, - # image is the image name where applicable - image: string, - # name is the name of the pod, container, image - name: string, - # status describes the event that happened (i.e. create, remove, ...) - status: string, - # time the event happened - time: string, - # type describes object the event happened with (image, container...) - type: string -) - -type DiffInfo( - # path that is different - path: string, - # Add, Delete, Modify - changeType: string -) - -type ExecOpts( - # container name or id - name: string, - # Create pseudo tty - tty: bool, - # privileged access in container - privileged: bool, - # command to execute in container - cmd: []string, - # user to use in container - user: ?string, - # workdir to run command in container - workdir: ?string, - # slice of keyword=value environment variables - env: ?[]string, - # string of detach keys - detachKeys: ?string -) - -# 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 -) - -# Reset resets Podman back to its initial state. -# Removes all Pods, Containers, Images and Volumes -method Reset() -> () - -# 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 information about all containers. -# See also [GetContainer](#GetContainer). -method ListContainers() -> (containers: []Container) - -method Ps(opts: PsOpts) -> (containers: []PsContainer) - -method GetContainersByStatus(status: []string) -> (containerS: []Container) - -method Top (nameOrID: string, descriptors: []string) -> (top: []string) - -# HealthCheckRun executes defined container's healthcheck command -# and returns the container's health status. -method HealthCheckRun (nameOrID: string) -> (healthCheckStatus: string) - -# 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) - -# GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of -# container names. The definition of latest container means the latest by creation date. In a multi- -# user environment, results might differ from what you expect. -method GetContainersByContext(all: bool, latest: bool, args: []string) -> (containers: []string) - -# CreateContainer creates a new container from an image. It uses a [Create](#Create) type for input. -method CreateContainer(create: Create) -> (container: string) - -# InspectContainer data takes a name or ID of a container returns the inspection -# data in string format. You can then serialize the string into JSON. A [ContainerNotFound](#ContainerNotFound) -# error will be returned if the container cannot be found. See also [InspectImage](#InspectImage). -method InspectContainer(name: string) -> (container: string) - -# ListContainerProcesses takes a name or ID of a container and returns the processes -# running inside the container as array of strings. It will accept an array of string -# arguments that represent ps options. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) -# error will be returned. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ListContainerProcesses '{"name": "135d71b9495f", "opts": []}' -# { -# "container": [ -# " UID PID PPID C STIME TTY TIME CMD", -# " 0 21220 21210 0 09:05 pts/0 00:00:00 /bin/sh", -# " 0 21232 21220 0 09:05 pts/0 00:00:00 top", -# " 0 21284 21220 0 09:05 pts/0 00:00:00 vi /etc/hosts" -# ] -# } -# ~~~ -method ListContainerProcesses(name: string, opts: []string) -> (container: []string) - -# GetContainerLogs takes a name or ID of a container and returns the logs of that container. -# If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) error will be returned. -# The container logs are returned as an array of strings. GetContainerLogs will honor the streaming -# capability of varlink if the client invokes it. -method GetContainerLogs(name: string) -> (container: []string) - -method GetContainersLogs(names: []string, follow: bool, latest: bool, since: string, tail: int, timestamps: bool) -> (log: LogLine) - -# ListContainerChanges takes a name or ID of a container and returns changes between the container and -# its base image. It returns a struct of changed, deleted, and added path names. -method ListContainerChanges(name: string) -> (container: ContainerChanges) - -# ExportContainer creates an image from a container. It takes the name or ID of a container and a -# path representing the target tarfile. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) -# error will be returned. -# The return value is the written tarfile. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ExportContainer '{"name": "flamboyant_payne", "path": "/tmp/payne.tar" }' -# { -# "tarfile": "/tmp/payne.tar" -# } -# ~~~ -method ExportContainer(name: string, path: string) -> (tarfile: string) - -# GetContainerStats takes the name or ID of a container and returns a single ContainerStats structure which -# contains attributes like memory and cpu usage. If the container cannot be found, a -# [ContainerNotFound](#ContainerNotFound) error will be returned. If the container is not running, a [NoContainerRunning](#NoContainerRunning) -# error will be returned -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.GetContainerStats '{"name": "c33e4164f384"}' -# { -# "container": { -# "block_input": 0, -# "block_output": 0, -# "cpu": 2.571123918839990154678e-08, -# "cpu_nano": 49037378, -# "id": "c33e4164f384aa9d979072a63319d66b74fd7a128be71fa68ede24f33ec6cfee", -# "mem_limit": 33080606720, -# "mem_perc": 2.166828456524753747370e-03, -# "mem_usage": 716800, -# "name": "competent_wozniak", -# "net_input": 768, -# "net_output": 5910, -# "pids": 1, -# "system_nano": 10000000 -# } -# } -# ~~~ -method GetContainerStats(name: string) -> (container: ContainerStats) - -# GetContainerStatsWithHistory takes a previous set of container statistics and uses libpod functions -# to calculate the containers statistics based on current and previous measurements. -method GetContainerStatsWithHistory(previousStats: ContainerStats) -> (container: ContainerStats) - -# This method has not be implemented yet. -# 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) -# error will be returned. See also [CreateContainer](#CreateContainer). -method StartContainer(name: string) -> (container: string) - -# StopContainer stops a container given a timeout. It takes the name or ID of a container as well as a -# timeout value. The timeout value the time before a forcible stop to the container is applied. It -# returns the container ID once stopped. If the container cannot be found, a [ContainerNotFound](#ContainerNotFound) -# error will be returned instead. See also [KillContainer](KillContainer). -# #### Error -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.StopContainer '{"name": "135d71b9495f", "timeout": 5}' -# { -# "container": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6" -# } -# ~~~ -method StopContainer(name: string, timeout: int) -> (container: string) - -# InitContainer initializes the given container. It accepts a container name or -# ID, and will initialize the container matching that ID if possible, and error -# if not. Containers can only be initialized when they are in the Created or -# Exited states. Initialization prepares a container to be started, but does not -# start the container. It is intended to be used to debug a container's state -# prior to starting it. -method InitContainer(name: string) -> (container: string) - -# RestartContainer will restart a running container given a container name or ID and timeout value. The timeout -# value is the time before a forcible stop is used to stop the container. If the container cannot be found by -# name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise, the ID of the -# container will be returned. -method RestartContainer(name: string, timeout: int) -> (container: string) - -# KillContainer takes the name or ID of a container as well as a signal to be applied to the container. Once the -# container has been killed, the container's ID is returned. If the container cannot be found, a -# [ContainerNotFound](#ContainerNotFound) error is returned. See also [StopContainer](StopContainer). -method KillContainer(name: string, signal: int) -> (container: string) - -# This method has not be implemented yet. -# method UpdateContainer() -> (notimplemented: NotImplemented) - -# This method has not be implemented yet. -# 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. -# See also [UnpauseContainer](#UnpauseContainer). -method PauseContainer(name: string) -> (container: string) - -# UnpauseContainer takes the name or ID of container and unpauses a paused container. If the container cannot be -# found, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise the ID of the container is returned. -# See also [PauseContainer](#PauseContainer). -method UnpauseContainer(name: string) -> (container: string) - -# Attach takes the name or ID of a container and sets up the ability to remotely attach to its console. The start -# bool is whether you wish to start the container in question first. -method Attach(name: string, detachKeys: string, start: bool) -> () - -method AttachControl(name: string) -> () - -# 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 -# second is a "control" socket where things like resizing the TTY events are sent. If the container cannot be -# found, a [ContainerNotFound](#ContainerNotFound) error will be returned. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/io.podman/io.podman.GetAttachSockets '{"name": "b7624e775431219161"}' -# { -# "sockets": { -# "container_id": "b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca", -# "control_socket": "/var/lib/containers/storage/overlay-containers/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/userdata/ctl", -# "io_socket": "/var/run/libpod/socket/b7624e7754312191613245ce1a46844abee60025818fe3c3f3203435623a1eca/attach" -# } -# } -# ~~~ -method GetAttachSockets(name: string) -> (sockets: Sockets) - -# WaitContainer takes the name or ID of a container and waits the given interval in milliseconds 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. -method WaitContainer(name: string, interval: int) -> (exitcode: int) - -# RemoveContainer requires the name or ID of a container as well as a boolean that -# indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean -# indicating whether 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. -# See also [EvictContainer](EvictContainer). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}' -# { -# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" -# } -# ~~~ -method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string) - -# EvictContainer requires the name or ID of a container as well as a boolean that -# indicates to remove builtin volumes. Upon successful eviction of the container, -# its ID is returned. If the container cannot be found by name or ID, -# a [ContainerNotFound](#ContainerNotFound) error will be returned. -# See also [RemoveContainer](RemoveContainer). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}' -# { -# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" -# } -# ~~~ -method EvictContainer(name: string, 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). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteStoppedContainers -# { -# "containers": [ -# "451410b931d00def8aa9b4f8084e4d4a39e5e04ea61f358cf53a5cf95afcdcee", -# "8b60f754a3e01389494a9581ade97d35c2765b6e2f19acd2d3040c82a32d1bc0", -# "cf2e99d4d3cad6073df199ed32bbe64b124f3e1aba6d78821aa8460e70d30084", -# "db901a329587312366e5ecff583d08f0875b4b79294322df67d90fc6eed08fc1" -# ] -# } -# ~~~ -method DeleteStoppedContainers() -> (containers: []string) - -# ListImages returns information about the images that are currently in storage. -# See also [InspectImage](#InspectImage). -method ListImages() -> (images: []Image) - -# ListImagesWithFilters returns information about the images that are currently in storage -# after one or more filters has been applied. -# See also [InspectImage](#InspectImage). -method ListImagesWithFilters(filters: []string) -> (images: []Image) - -# 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 -# contextDir tarball path, the 'dockerfiles' path, and 'output' option in the BuildInfo structure. The 'output' -# options is the name of the of the resulting build. It will return a [MoreResponse](#MoreResponse) structure -# that contains the build logs and resulting image ID. -# #### Example -# ~~~ -# $ sudo varlink call -m unix:///run/podman/io.podman/io.podman.BuildImage '{"build":{"contextDir":"/tmp/t/context.tar","dockerfiles":["Dockerfile"], "output":"foobar"}}' -# { -# "image": { -# "id": "", -# "logs": [ -# "STEP 1: FROM alpine\n" -# ] -# } -# } -# { -# "image": { -# "id": "", -# "logs": [ -# "STEP 2: COMMIT foobar\n" -# ] -# } -# } -# { -# "image": { -# "id": "", -# "logs": [ -# "b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9\n" -# ] -# } -# } -# { -# "image": { -# "id": "b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9", -# "logs": [] -# } -# } -# ~~~ -method BuildImage(build: BuildInfo) -> (image: MoreResponse) - -# This function is not implemented yet. -# method CreateImage() -> (notimplemented: NotImplemented) - -# InspectImage takes the name or ID of an image and returns a string representation of data associated with the -#image. You must serialize the string into JSON to use it further. An [ImageNotFound](#ImageNotFound) error will -# be returned if the image cannot be found. -method InspectImage(name: string) -> (image: string) - -# HistoryImage takes the name or ID of an image and returns information about its history and layers. The returned -# history is in the form of an array of ImageHistory structures. If the image cannot be found, an -# [ImageNotFound](#ImageNotFound) error is returned. -method HistoryImage(name: string) -> (history: []ImageHistory) - -# PushImage takes two input arguments: the name or ID of an image, the fully-qualified destination name of the image, -# It will return an [ImageNotFound](#ImageNotFound) error if -# the image cannot be found in local storage; otherwise it will return a [MoreResponse](#MoreResponse) -method PushImage(name: string, tag: 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. -method TagImage(name: string, tagged: string) -> (image: string) - -# UntagImage takes the name or ID of an image in local storage as well as the -# tag name to be removed. If the image cannot be found, an -# [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of -# the image is returned on success. -method UntagImage(name: string, tag: string) -> (image: string) - -# RemoveImage takes the name or ID of an image as well as a boolean that determines if containers using that image -# should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The -# ID of the removed image is returned when complete. See also [DeleteUnusedImages](DeleteUnusedImages). -# #### Example -# ~~~ -# varlink call -m unix:/run/podman/io.podman/io.podman.RemoveImage '{"name": "registry.fedoraproject.org/fedora", "force": true}' -# { -# "image": "426866d6fa419873f97e5cbd320eeb22778244c1dfffa01c944db3114f55772e" -# } -# ~~~ -method RemoveImage(name: string, force: bool) -> (image: string) - -# RemoveImageWithResponse takes the name or ID of an image as well as a boolean that determines if containers using that image -# should be deleted. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned. The response is -# in the form of a RemoveImageResponse . -method RemoveImageWithResponse(name: string, force: bool) -> (response: RemoveImageResponse) - -# 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, filter: ImageSearchFilter) -> (results: []ImageSearchResult) - -# DeleteUnusedImages deletes any images not associated with a container. The IDs of the deleted images are returned -# in a string array. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages -# { -# "images": [ -# "166ea6588079559c724c15223f52927f514f73dd5c5cf2ae2d143e3b2e6e9b52", -# "da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e", -# "3ef70f7291f47dfe2b82931a993e16f5a44a0e7a68034c3e0e086d77f5829adc", -# "59788edf1f3e78cd0ebe6ce1446e9d10788225db3dedcfd1a59f764bad2b2690" -# ] -# } -# ~~~ -method DeleteUnusedImages() -> (images: []string) - -# Commit, creates an image from an existing container. It requires the name or -# ID of the container as well as the resulting image name. Optionally, you can define an author and message -# to be added to the resulting image. You can also define changes to the resulting image for the following -# attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOLUME, and WORKDIR_. To pause the -# container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot -# be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise, -# the resulting image's ID will be returned as a string inside a MoreResponse. -method Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) -> (reply: MoreResponse) - -# 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). -method ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) -> (image: string) - -# ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also -# a boolean option to force compression. It also takes in a string array of tags to be able to save multiple -# 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). -method ExportImage(name: string, destination: string, compress: bool, tags: []string) -> (image: string) - -# PullImage pulls an image from a repository to local storage. After a successful pull, the image id and logs -# are returned as a [MoreResponse](#MoreResponse). This connection also will handle a WantsMores request to send -# status as it occurs. -method PullImage(name: string, creds: AuthConfig) -> (reply: MoreResponse) - -# CreatePod creates a new empty pod. It uses a [PodCreate](#PodCreate) type for input. -# On success, the ID of the newly created pod will be returned. -# #### Example -# ~~~ -# $ varlink call unix:/run/podman/io.podman/io.podman.CreatePod '{"create": {"name": "test"}}' -# { -# "pod": "b05dee7bd4ccfee688099fe1588a7a898d6ddd6897de9251d4671c9b0feacb2a" -# } -# -# $ varlink call unix:/run/podman/io.podman/io.podman.CreatePod '{"create": {"infra": true, "share": ["ipc", "net", "uts"]}}' -# { -# "pod": "d7697449a8035f613c1a8891286502aca68fff7d5d49a85279b3bda229af3b28" -# } -# ~~~ -method CreatePod(create: PodCreate) -> (pod: string) - -# ListPods returns a list of pods in no particular order. They are -# returned as an array of ListPodData structs. See also [GetPod](#GetPod). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ListPods -# { -# "pods": [ -# { -# "cgroup": "machine.slice", -# "containersinfo": [ -# { -# "id": "00c130a45de0411f109f1a0cfea2e298df71db20fa939de5cab8b2160a36be45", -# "name": "1840835294cf-infra", -# "status": "running" -# }, -# { -# "id": "49a5cce72093a5ca47c6de86f10ad7bb36391e2d89cef765f807e460865a0ec6", -# "name": "upbeat_murdock", -# "status": "running" -# } -# ], -# "createdat": "2018-12-07 13:10:15.014139258 -0600 CST", -# "id": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f", -# "name": "foobar", -# "numberofcontainers": "2", -# "status": "Running" -# }, -# { -# "cgroup": "machine.slice", -# "containersinfo": [ -# { -# "id": "1ca4b7bbba14a75ba00072d4b705c77f3df87db0109afaa44d50cb37c04a477e", -# "name": "784306f655c6-infra", -# "status": "running" -# } -# ], -# "createdat": "2018-12-07 13:09:57.105112457 -0600 CST", -# "id": "784306f655c6200aea321dd430ba685e9b2cc1f7d7528a72f3ff74ffb29485a2", -# "name": "nostalgic_pike", -# "numberofcontainers": "1", -# "status": "Running" -# } -# ] -# } -# ~~~ -method ListPods() -> (pods: []ListPodData) - -# GetPod takes a name or ID of a pod and returns single [ListPodData](#ListPodData) -# structure. A [PodNotFound](#PodNotFound) error will be returned if the pod cannot be found. -# See also [ListPods](ListPods). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.GetPod '{"name": "foobar"}' -# { -# "pod": { -# "cgroup": "machine.slice", -# "containersinfo": [ -# { -# "id": "00c130a45de0411f109f1a0cfea2e298df71db20fa939de5cab8b2160a36be45", -# "name": "1840835294cf-infra", -# "status": "running" -# }, -# { -# "id": "49a5cce72093a5ca47c6de86f10ad7bb36391e2d89cef765f807e460865a0ec6", -# "name": "upbeat_murdock", -# "status": "running" -# } -# ], -# "createdat": "2018-12-07 13:10:15.014139258 -0600 CST", -# "id": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f", -# "name": "foobar", -# "numberofcontainers": "2", -# "status": "Running" -# } -# } -# ~~~ -method GetPod(name: string) -> (pod: ListPodData) - -# InspectPod takes the name or ID of an image and returns a string representation of data associated with the -# pod. You must serialize the string into JSON to use it further. A [PodNotFound](#PodNotFound) error will -# be returned if the pod cannot be found. -method InspectPod(name: string) -> (pod: string) - -# StartPod starts containers in a pod. It takes the name or ID of pod. If the pod cannot be found, a [PodNotFound](#PodNotFound) -# error will be returned. Containers in a pod are started independently. If there is an error starting one container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was started with no errors, the pod ID is returned. -# See also [CreatePod](#CreatePod). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.StartPod '{"name": "135d71b9495f"}' -# { -# "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6", -# } -# ~~~ -method StartPod(name: string) -> (pod: string) - -# StopPod stops containers in a pod. It takes the name or ID of a pod and a timeout. -# If the pod cannot be found, a [PodNotFound](#PodNotFound) error will be returned instead. -# Containers in a pod are stopped independently. If there is an error stopping one container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was stopped with no errors, the pod ID is returned. -# See also [KillPod](KillPod). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.StopPod '{"name": "135d71b9495f"}' -# { -# "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6" -# } -# ~~~ -method StopPod(name: string, timeout: int) -> (pod: string) - -# RestartPod will restart containers in a pod given a pod name or ID. Containers in -# the pod that are running will be stopped, then all stopped containers will be run. -# If the pod cannot be found by name or ID, a [PodNotFound](#PodNotFound) error will be returned. -# Containers in a pod are restarted independently. If there is an error restarting one container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was restarted with no errors, the pod ID is returned. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.RestartPod '{"name": "135d71b9495f"}' -# { -# "pod": "135d71b9495f7c3967f536edad57750bfdb569336cd107d8aabab45565ffcfb6" -# } -# ~~~ -method RestartPod(name: string) -> (pod: string) - -# KillPod takes the name or ID of a pod as well as a signal to be applied to the pod. If the pod cannot be found, a -# [PodNotFound](#PodNotFound) error is returned. -# Containers in a pod are killed independently. If there is an error killing one container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was killed with no errors, the pod ID is returned. -# See also [StopPod](StopPod). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.KillPod '{"name": "foobar", "signal": 15}' -# { -# "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" -# } -# ~~~ -method KillPod(name: string, signal: int) -> (pod: string) - -# PausePod takes the name or ID of a pod and pauses the running containers associated with it. If the pod cannot be found, -# a [PodNotFound](#PodNotFound) error will be returned. -# Containers in a pod are paused independently. If there is an error pausing one container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was paused with no errors, the pod ID is returned. -# See also [UnpausePod](#UnpausePod). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.PausePod '{"name": "foobar"}' -# { -# "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" -# } -# ~~~ -method PausePod(name: string) -> (pod: string) - -# UnpausePod takes the name or ID of a pod and unpauses the paused containers associated with it. If the pod cannot be -# found, a [PodNotFound](#PodNotFound) error will be returned. -# Containers in a pod are unpaused independently. If there is an error unpausing one container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was unpaused with no errors, the pod ID is returned. -# See also [PausePod](#PausePod). -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.UnpausePod '{"name": "foobar"}' -# { -# "pod": "1840835294cf076a822e4e12ba4152411f131bd869e7f6a4e8b16df9b0ea5c7f" -# } -# ~~~ -method UnpausePod(name: string) -> (pod: string) - -# RemovePod takes the name or ID of a pod as well a boolean representing whether a running -# container in the pod can be stopped and removed. If a pod has containers associated with it, and force is not true, -# an error will occur. -# If the pod cannot be found by name or ID, a [PodNotFound](#PodNotFound) error will be returned. -# Containers in a pod are removed independently. If there is an error removing any container, the ID of those containers -# will be returned in a list, along with the ID of the pod in a [PodContainerError](#PodContainerError). -# If the pod was removed with no errors, the pod ID is returned. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.RemovePod '{"name": "62f4fd98cb57", "force": "true"}' -# { -# "pod": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20" -# } -# ~~~ -method RemovePod(name: string, force: bool) -> (pod: string) - -# This method has not be implemented yet. -# method WaitPod() -> (notimplemented: NotImplemented) - -method TopPod(pod: string, latest: bool, descriptors: []string) -> (stats: []string) - -# 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) -# error will be returned. If the pod has no running containers associated with it, a [NoContainerRunning](#NoContainerRunning) -# error will be returned. -# #### Example -# ~~~ -# $ varlink call unix:/run/podman/io.podman/io.podman.GetPodStats '{"name": "7f62b508b6f12b11d8fe02e"}' -# { -# "containers": [ -# { -# "block_input": 0, -# "block_output": 0, -# "cpu": 2.833470544016107524276e-08, -# "cpu_nano": 54363072, -# "id": "a64b51f805121fe2c5a3dc5112eb61d6ed139e3d1c99110360d08b58d48e4a93", -# "mem_limit": 12276146176, -# "mem_perc": 7.974359265237864966003e-03, -# "mem_usage": 978944, -# "name": "quirky_heisenberg", -# "net_input": 866, -# "net_output": 7388, -# "pids": 1, -# "system_nano": 20000000 -# } -# ], -# "pod": "7f62b508b6f12b11d8fe02e0db4de6b9e43a7d7699b33a4fc0d574f6e82b4ebd" -# } -# ~~~ -method GetPodStats(name: string) -> (pod: string, containers: []ContainerStats) - -# GetPodsByStatus searches for pods whose status is included in statuses -method GetPodsByStatus(statuses: []string) -> (pods: []string) - -# ImageExists talks a full or partial image ID or name and returns an int as to whether -# the image exists in local storage. An int result of 0 means the image does exist in -# local storage; whereas 1 indicates the image does not exists in local storage. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "imageddoesntexist"}' -# { -# "exists": 1 -# } -# ~~~ -method ImageExists(name: string) -> (exists: int) - -# ImageTree returns the image tree for the provided image name or ID -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}' -# { -# "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n" -# } -# ~~~ -method ImageTree(name: string, whatRequires: bool) -> (tree: string) - -# ContainerExists takes a full or partial container ID or name and returns an int as to -# whether the container exists in local storage. A result of 0 means the container does -# exists; whereas a result of 1 means it could not be found. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ContainerExists '{"name": "flamboyant_payne"}'{ -# "exists": 0 -# } -# ~~~ -method ContainerExists(name: string) -> (exists: int) - -# ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container -# ID. On successful checkpoint, the id of the checkpointed container is returned. -method ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) -> (id: string) - -# ContainerRestore restores a container that has been checkpointed. The container to be restored can -# be identified by its name or full/partial container ID. A successful restore will result in the return -# of the container's ID. -method ContainerRestore(name: string, keep: bool, tcpEstablished: bool) -> (id: string) - -# ContainerRunlabel runs executes a command as described by a given container image label. -method ContainerRunlabel(runlabel: Runlabel) -> () - -# ExecContainer executes a command in the given container. -method ExecContainer(opts: ExecOpts) -> () - -# ListContainerMounts gathers all the mounted container mount points and returns them as an array -# of strings -# #### Example -# ~~~ -# $ varlink call unix:/run/podman/io.podman/io.podman.ListContainerMounts -# { -# "mounts": { -# "04e4c255269ed2545e7f8bd1395a75f7949c50c223415c00c1d54bfa20f3b3d9": "/var/lib/containers/storage/overlay/a078925828f57e20467ca31cfca8a849210d21ec7e5757332b72b6924f441c17/merged", -# "1d58c319f9e881a644a5122ff84419dccf6d138f744469281446ab243ef38924": "/var/lib/containers/storage/overlay/948fcf93f8cb932f0f03fd52e3180a58627d547192ffe3b88e0013b98ddcd0d2/merged" -# } -# } -# ~~~ -method ListContainerMounts() -> (mounts: [string]string) - -# MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination -# mount is returned as a string. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.MountContainer '{"name": "jolly_shannon"}'{ -# "path": "/var/lib/containers/storage/overlay/419eeb04e783ea159149ced67d9fcfc15211084d65e894792a96bedfae0470ca/merged" -# } -# ~~~ -method MountContainer(name: string) -> (path: string) - -# UnmountContainer umounts a container by its name or full/partial container ID. -# #### Example -# ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.UnmountContainer '{"name": "jolly_shannon", "force": false}' -# {} -# ~~~ -method UnmountContainer(name: string, force: bool) -> () - -# ImagesPrune removes all unused images from the local store. Upon successful pruning, -# the IDs of the removed images are returned. -method ImagesPrune(all: bool, filter: []string) -> (pruned: []string) - -# This function is not implemented yet. -# 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(name: string, service: bool) -> (pod: KubePodService) - -# 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) - -# ContainerConfig returns a container's config in string form. This call is for -# development of Podman only and generally should not be used. -method ContainerConfig(name: string) -> (config: string) - -# ContainerArtifacts returns a container's artifacts in string form. This call is for -# development of Podman only and generally should not be used. -method ContainerArtifacts(name: string, artifactName: string) -> (config: string) - -# ContainerInspectData returns a container's inspect data in string form. This call is for -# development of Podman only and generally should not be used. -method ContainerInspectData(name: string, size: bool) -> (config: string) - -# ContainerStateData returns a container's state config in string form. This call is for -# development of Podman only and generally should not be used. -method ContainerStateData(name: string) -> (config: string) - -# PodStateData returns inspectr level information of a given pod in string form. This call is for -# development of Podman only and generally should not be used. -method PodStateData(name: string) -> (config: string) - -# This call is for the development of Podman only and should not be used. -method CreateFromCC(in: []string) -> (id: string) - -# Spec returns the oci spec for a container. This call is for development of Podman only and generally should not be used. -method Spec(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) -> (successes: []string, failures: [string]string) - -# GetVolumes gets slice of the volumes on a remote host -method GetVolumes(args: []string, all: bool) -> (volumes: []Volume) - -# InspectVolume inspects a single volume. Returns inspect JSON in the form of a -# string. -method InspectVolume(name: string) -> (volume: string) - -# VolumesPrune removes unused volumes on the host -method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string) - -# ImageSave allows you to save an image from the local image storage to a tarball -method ImageSave(options: ImageSaveOptions) -> (reply: MoreResponse) - -# GetPodsByContext allows you to get a list pod ids depending on all, latest, or a list of -# pod names. The definition of latest pod means the latest by creation date. In a multi- -# user environment, results might differ from what you expect. -method GetPodsByContext(all: bool, latest: bool, args: []string) -> (pods: []string) - -# LoadImage allows you to load an image into local storage from a tarball. -method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) -> (reply: MoreResponse) - -# GetEvents returns known libpod events filtered by the options provided. -method GetEvents(filter: []string, since: string, until: string) -> (events: Event) - -# Diff returns a diff between libpod objects -method Diff(name: string) -> (diffs: []DiffInfo) - -# GetLayersMapWithImageInfo is for the development of Podman and should not be used. -method GetLayersMapWithImageInfo() -> (layerMap: string) - -# BuildImageHierarchyMap is for the development of Podman and should not be used. -method BuildImageHierarchyMap(name: string) -> (imageInfo: string) - -# ImageNotFound means the image could not be found by the provided name or ID in local storage. -error ImageNotFound (id: string, reason: string) - -# ContainerNotFound means the container could not be found by the provided name or ID in local storage. -error ContainerNotFound (id: string, reason: string) - -# NoContainerRunning means none of the containers requested are running in a command that requires a running container. -error NoContainerRunning () - -# PodNotFound means the pod could not be found by the provided name or ID in local storage. -error PodNotFound (name: string, reason: string) - -# VolumeNotFound means the volume could not be found by the name or ID in local storage. -error VolumeNotFound (id: string, reason: string) - -# PodContainerError means a container associated with a pod failed to perform an operation. It contains -# a container ID of the container that failed. -error PodContainerError (podname: string, errors: []PodContainerErrorData) - -# NoContainersInPod means a pod has no containers on which to perform the operation. It contains -# the pod ID. -error NoContainersInPod (name: string) - -# InvalidState indicates that a container or pod was in an improper state for the requested operation -error InvalidState (id: string, reason: string) - -# ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message -# is includes as part of the error's text. -error ErrorOccurred (reason: string) - -# RuntimeErrors generally means a runtime could not be found or gotten. -error RuntimeError (reason: string) - -# The Podman endpoint requires that you use a streaming connection. -error WantsMoreRequired (reason: string) - -# Container is already stopped -error ErrCtrStopped (id: string) - -# This function requires CGroupsV2 to run in rootless mode. -error ErrRequiresCgroupsV2ForRootless(reason: string) diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go deleted file mode 100644 index 38354a790..000000000 --- a/pkg/varlinkapi/attach.go +++ /dev/null @@ -1,144 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "bufio" - "context" - "io" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/libpod/events" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/containers/podman/v2/pkg/varlinkapi/virtwriter" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "k8s.io/client-go/tools/remotecommand" -) - -func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *define.AttachStreams) { - - // These are the varlink sockets - reader := call.Call.Reader - writer := call.Call.Writer - - // This pipe is used to pass stdin from the client to the input stream - // once the msg has been "decoded" - pr, pw := io.Pipe() - - stdoutWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdout) - // TODO if runc ever starts passing stderr, we can too - // stderrWriter := NewVirtWriteCloser(writer, ToStderr) - - streams := define.AttachStreams{ - OutputStream: stdoutWriter, - InputStream: bufio.NewReader(pr), - // Runc eats the error stream - ErrorStream: stdoutWriter, - AttachInput: true, - AttachOutput: true, - // Runc eats the error stream - AttachError: true, - } - return reader, writer, pr, pw, &streams -} - -// Attach connects to a containers console -func (i *VarlinkAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys string, start bool) error { - var finalErr error - resize := make(chan remotecommand.TerminalSize) - errChan := make(chan error) - - if !call.WantsUpgrade() { - return call.ReplyErrorOccurred("client must use upgraded connection to attach") - } - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - state, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if !start && state != define.ContainerStateRunning { - return call.ReplyErrorOccurred("container must be running to attach") - } - - // ACK the client upgrade request - if err := call.ReplyAttach(); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - reader, writer, _, pw, streams := setupStreams(call) - go func() { - if err := virtwriter.Reader(reader, nil, nil, pw, resize, nil); err != nil { - errChan <- err - } - }() - - if state == define.ContainerStateRunning { - finalErr = attach(ctr, streams, detachKeys, resize, errChan) - } else { - finalErr = startAndAttach(ctr, streams, detachKeys, resize, errChan) - } - - exitCode := define.ExitCode(finalErr) - if finalErr != define.ErrDetach && finalErr != nil { - logrus.Error(finalErr) - } else { - if ecode, err := ctr.Wait(); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - // Check events - event, err := i.Runtime.GetLastContainerEvent(context.Background(), ctr.ID(), events.Exited) - if err != nil { - logrus.Errorf("Cannot get exit code: %v", err) - exitCode = define.ExecErrorCodeNotFound - } else { - exitCode = event.ContainerExitCode - } - } else { - exitCode = define.ExitCode(err) - } - } else { - exitCode = int(ecode) - } - } - - if ctr.AutoRemove() { - err := i.Runtime.RemoveContainer(getContext(), ctr, false, false) - if err != nil { - logrus.Errorf("Failed to remove container %s: %s", ctr.ID(), err.Error()) - } - } - - if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil { - logrus.Errorf("Failed to HANG-UP attach to %s: %s", ctr.ID(), err.Error()) - } - return call.Writer.Flush() -} - -func attach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { - go func() { - if err := ctr.Attach(streams, detachKeys, resize); err != nil { - errChan <- err - } - }() - attachError := <-errChan - return attachError -} - -func startAndAttach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error { - var finalErr error - attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false) - if err != nil { - return err - } - select { - case attachChanErr := <-attachChan: - finalErr = attachChanErr - case chanError := <-errChan: - finalErr = chanError - } - return finalErr -} diff --git a/pkg/varlinkapi/config.go b/pkg/varlinkapi/config.go deleted file mode 100644 index 50e6fb833..000000000 --- a/pkg/varlinkapi/config.go +++ /dev/null @@ -1,22 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "github.com/containers/podman/v2/libpod" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/spf13/cobra" -) - -// VarlinkAPI is the basic varlink struct for libpod -type VarlinkAPI struct { - Cli *cobra.Command - iopodman.VarlinkInterface - Runtime *libpod.Runtime -} - -// New creates a new varlink client -func New(cli *cobra.Command, runtime *libpod.Runtime) *iopodman.VarlinkInterface { - lp := VarlinkAPI{Cli: cli, Runtime: runtime} - return iopodman.VarlinkNew(&lp) -} diff --git a/pkg/varlinkapi/container.go b/pkg/varlinkapi/container.go deleted file mode 100644 index c4e8c1feb..000000000 --- a/pkg/varlinkapi/container.go +++ /dev/null @@ -1,928 +0,0 @@ -package varlinkapi - -import ( - "context" - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/containers/image/v5/types" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/libpod/image" - "github.com/containers/podman/v2/pkg/timetype" - "github.com/containers/podman/v2/pkg/util" - "github.com/cri-o/ocicni/pkg/ocicni" - "github.com/docker/go-units" - "github.com/google/shlex" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - v1 "k8s.io/api/core/v1" -) - -const ( - cidTruncLength = 12 - podTruncLength = 12 - iidTruncLength = 12 - cmdTruncLength = 17 -) - -// PsOptions describes the struct being formed for ps. -type PsOptions struct { - All bool - Format string - Last int - Latest bool - NoTrunc bool - Pod bool - Quiet bool - Size bool - Sort string - Namespace bool - Sync bool -} - -// BatchContainerStruct is the return object from BatchContainer and contains -// container related information. -type BatchContainerStruct struct { - ConConfig *libpod.ContainerConfig - ConState define.ContainerStatus - ExitCode int32 - Exited bool - Pid int - StartedTime time.Time - ExitedTime time.Time - Size *ContainerSize -} - -// PsContainerOutput is the struct being returned from a parallel -// batch operation. -type PsContainerOutput struct { - ID string - Image string - ImageID string - Command string - Created string - Ports string - Names string - IsInfra bool - Status string - State define.ContainerStatus - Pid int - Size *ContainerSize - Pod string - PodName string - CreatedAt time.Time - ExitedAt time.Time - StartedAt time.Time - Labels map[string]string - PID string - Cgroup string - IPC string - MNT string - NET string - PIDNS string - User string - UTS string - Mounts string -} - -// Namespace describes output for ps namespace. -type Namespace struct { - PID string `json:"pid,omitempty"` - Cgroup string `json:"cgroup,omitempty"` - IPC string `json:"ipc,omitempty"` - MNT string `json:"mnt,omitempty"` - NET string `json:"net,omitempty"` - PIDNS string `json:"pidns,omitempty"` - User string `json:"user,omitempty"` - UTS string `json:"uts,omitempty"` -} - -// ContainerSize holds the size of the container's root filesystem and top -// read-write layer. -type ContainerSize struct { - RootFsSize int64 `json:"rootFsSize"` - RwSize int64 `json:"rwSize"` -} - -// NewBatchContainer runs a batch process under one lock to get container information and only -// be called in PBatch. -func NewBatchContainer(r *libpod.Runtime, ctr *libpod.Container, opts PsOptions) (PsContainerOutput, error) { - var ( - conState define.ContainerStatus - command string - created string - status string - exitedAt time.Time - startedAt time.Time - exitCode int32 - err error - pid int - size *ContainerSize - ns *Namespace - pso PsContainerOutput - ) - batchErr := ctr.Batch(func(c *libpod.Container) error { - if opts.Sync { - if err := c.Sync(); err != nil { - return err - } - } - - conState, err = c.State() - if err != nil { - return errors.Wrapf(err, "unable to obtain container state") - } - command = strings.Join(c.Command(), " ") - created = units.HumanDuration(time.Since(c.CreatedTime())) + " ago" - - exitCode, _, err = c.ExitCode() - if err != nil { - return errors.Wrapf(err, "unable to obtain container exit code") - } - startedAt, err = c.StartedTime() - if err != nil { - logrus.Errorf("error getting started time for %q: %v", c.ID(), err) - } - exitedAt, err = c.FinishedTime() - if err != nil { - logrus.Errorf("error getting exited time for %q: %v", c.ID(), err) - } - if opts.Namespace { - pid, err = c.PID() - if err != nil { - return errors.Wrapf(err, "unable to obtain container pid") - } - ns = GetNamespaces(pid) - } - if opts.Size { - size = new(ContainerSize) - - rootFsSize, err := c.RootFsSize() - if err != nil { - logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) - } - - rwSize, err := c.RWSize() - if err != nil { - logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) - } - - size.RootFsSize = rootFsSize - size.RwSize = rwSize - } - - return nil - }) - - if batchErr != nil { - return pso, batchErr - } - - switch conState.String() { - case define.ContainerStateExited.String(): - fallthrough - case define.ContainerStateStopped.String(): - exitedSince := units.HumanDuration(time.Since(exitedAt)) - status = fmt.Sprintf("Exited (%d) %s ago", exitCode, exitedSince) - case define.ContainerStateRunning.String(): - status = "Up " + units.HumanDuration(time.Since(startedAt)) + " ago" - case define.ContainerStatePaused.String(): - status = "Paused" - case define.ContainerStateCreated.String(), define.ContainerStateConfigured.String(): - status = "Created" - case define.ContainerStateRemoving.String(): - status = "Removing" - default: - status = "Error" - } - - imageID, imageName := ctr.Image() - cid := ctr.ID() - podID := ctr.PodID() - if !opts.NoTrunc { - cid = cid[0:cidTruncLength] - if len(podID) > podTruncLength { - podID = podID[0:podTruncLength] - } - if len(command) > cmdTruncLength { - command = command[0:cmdTruncLength] + "..." - } - if len(imageID) > iidTruncLength { - imageID = imageID[0:iidTruncLength] - } - } - - 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.ImageID = imageID - pso.Command = command - pso.Created = created - pso.Ports = portsToString(ports) - pso.Names = ctr.Name() - pso.IsInfra = ctr.IsInfra() - pso.Status = status - pso.State = conState - pso.Pid = pid - pso.Size = size - pso.ExitedAt = exitedAt - pso.CreatedAt = ctr.CreatedTime() - pso.StartedAt = startedAt - pso.Labels = ctr.Labels() - pso.Mounts = strings.Join(ctr.UserVolumes(), " ") - - // Add pod name and pod ID if requested by user. - // No need to look up the pod if its ID is empty. - if opts.Pod && len(podID) > 0 { - // The pod name is not in the container definition - // so we need to retrieve it using the pod ID. - var podName string - pod, err := r.LookupPod(podID) - if err != nil { - logrus.Errorf("unable to lookup pod for container %s", ctr.ID()) - } else { - podName = pod.Name() - } - - pso.Pod = podID - pso.PodName = podName - } - - if opts.Namespace { - pso.Cgroup = ns.Cgroup - pso.IPC = ns.IPC - pso.MNT = ns.MNT - pso.NET = ns.NET - pso.User = ns.User - pso.UTS = ns.UTS - pso.PIDNS = ns.PIDNS - } - - return pso, nil -} - -type batchFunc func() (PsContainerOutput, error) - -type workerInput struct { - parallelFunc batchFunc - opts PsOptions - cid string - job int -} - -// worker is a "threaded" worker that takes jobs from the channel "queue". -func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContainerOutput, errors chan<- error) { - for j := range jobs { - r, err := j.parallelFunc() - // If we find an error, we return just the error. - if err != nil { - errors <- err - } else { - // Return the result. - results <- r - } - wg.Done() - } -} - -// GenerateContainerFilterFuncs return ContainerFilter functions based of filter. -func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { - switch filter { - case "id": - return func(c *libpod.Container) bool { - return strings.Contains(c.ID(), filterValue) - }, nil - case "label": - var filterArray = strings.SplitN(filterValue, "=", 2) - var filterKey = filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } - return func(c *libpod.Container) bool { - for labelKey, labelValue := range c.Labels() { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { - return true - } - } - return false - }, nil - case "name": - return func(c *libpod.Container) bool { - match, err := regexp.MatchString(filterValue, c.Name()) - if err != nil { - return false - } - return match - }, nil - case "exited": - exitCode, err := strconv.ParseInt(filterValue, 10, 32) - if err != nil { - return nil, errors.Wrapf(err, "exited code out of range %q", filterValue) - } - return func(c *libpod.Container) bool { - ec, exited, err := c.ExitCode() - if ec == int32(exitCode) && err == nil && exited { - return true - } - return false - }, nil - case "status": - if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { - return nil, errors.Errorf("%s is not a valid status", filterValue) - } - return func(c *libpod.Container) bool { - status, err := c.State() - if err != nil { - return false - } - if filterValue == "stopped" { - filterValue = "exited" - } - state := status.String() - if status == define.ContainerStateConfigured { - state = "created" - } else if status == define.ContainerStateStopped { - state = "exited" - } - return state == filterValue - }, nil - case "ancestor": - // This needs to refine to match docker - // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant. - return func(c *libpod.Container) bool { - containerConfig := c.Config() - if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) { - return true - } - return false - }, nil - case "before": - ctr, err := r.LookupContainer(filterValue) - if err != nil { - return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) - } - containerConfig := ctr.Config() - createTime := containerConfig.CreatedTime - return func(c *libpod.Container) bool { - cc := c.Config() - return createTime.After(cc.CreatedTime) - }, nil - case "since": - ctr, err := r.LookupContainer(filterValue) - if err != nil { - return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) - } - containerConfig := ctr.Config() - createTime := containerConfig.CreatedTime - return func(c *libpod.Container) bool { - cc := c.Config() - return createTime.Before(cc.CreatedTime) - }, nil - case "volume": - //- volume=(<volume-name>|<mount-point-destination>) - return func(c *libpod.Container) bool { - containerConfig := c.Config() - var dest string - arr := strings.Split(filterValue, ":") - source := arr[0] - if len(arr) == 2 { - dest = arr[1] - } - for _, mount := range containerConfig.Spec.Mounts { - if dest != "" && (mount.Source == source && mount.Destination == dest) { - return true - } - if dest == "" && mount.Source == source { - return true - } - } - return false - }, nil - case "health": - return func(c *libpod.Container) bool { - hcStatus, err := c.HealthCheckStatus() - if err != nil { - return false - } - return hcStatus == filterValue - }, nil - case "until": - ts, err := timetype.GetTimestamp(filterValue, time.Now()) - if err != nil { - return nil, err - } - seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) - if err != nil { - return nil, err - } - until := time.Unix(seconds, nanoseconds) - return func(c *libpod.Container) bool { - if !until.IsZero() && c.CreatedTime().After((until)) { - return true - } - return false - }, nil - } - return nil, errors.Errorf("%s is an invalid filter", filter) -} - -// GetPsContainerOutput returns a slice of containers specifically for ps output. -func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, maxWorkers int) ([]PsContainerOutput, error) { - var ( - filterFuncs []libpod.ContainerFilter - outputContainers []*libpod.Container - ) - - if len(filters) > 0 { - for _, f := range filters { - filterSplit := strings.SplitN(f, "=", 2) - if len(filterSplit) < 2 { - return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - generatedFunc, err := GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) - if err != nil { - return nil, errors.Wrapf(err, "invalid filter") - } - filterFuncs = append(filterFuncs, generatedFunc) - } - } - if !opts.Latest { - // Get all containers. - containers, err := r.GetContainers(filterFuncs...) - if err != nil { - return nil, err - } - - // We only want the last few containers. - if opts.Last > 0 && opts.Last <= len(containers) { - return nil, errors.Errorf("--last not yet supported") - } else { - outputContainers = containers - } - } else { - // Get just the latest container. - // Ignore filters. - latestCtr, err := r.GetLatestContainer() - if err != nil { - return nil, err - } - - outputContainers = []*libpod.Container{latestCtr} - } - - pss := PBatch(r, outputContainers, maxWorkers, opts) - return pss, nil -} - -// PBatch performs batch operations on a container in parallel. It spawns the -// number of workers relative to the number of parallel operations desired. -func PBatch(r *libpod.Runtime, containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput { - var wg sync.WaitGroup - psResults := []PsContainerOutput{} - - // If the number of containers in question is less than the number of - // proposed parallel operations, we shouldn't spawn so many workers. - if workers > len(containers) { - workers = len(containers) - } - - jobs := make(chan workerInput, len(containers)) - results := make(chan PsContainerOutput, len(containers)) - batchErrors := make(chan error, len(containers)) - - // Create the workers. - for w := 1; w <= workers; w++ { - go worker(&wg, jobs, results, batchErrors) - } - - // Add jobs to the workers. - for i, j := range containers { - j := j - wg.Add(1) - f := func() (PsContainerOutput, error) { - return NewBatchContainer(r, j, opts) - } - jobs <- workerInput{ - parallelFunc: f, - opts: opts, - cid: j.ID(), - job: i, - } - } - close(jobs) - wg.Wait() - close(results) - close(batchErrors) - for err := range batchErrors { - logrus.Errorf("unable to get container info: %q", err) - } - for res := range results { - // We sort out running vs non-running here to save lots of copying - // later. - if !opts.All && !opts.Latest && opts.Last < 1 { - if !res.IsInfra && res.State == define.ContainerStateRunning { - psResults = append(psResults, res) - } - } else { - psResults = append(psResults, res) - } - } - return psResults -} - -// BatchContainerOp is used in ps to reduce performance hits by "batching" -// locks. -func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) { - var ( - conConfig *libpod.ContainerConfig - conState define.ContainerStatus - err error - exitCode int32 - exited bool - pid int - size *ContainerSize - startedTime time.Time - exitedTime time.Time - ) - - batchErr := ctr.Batch(func(c *libpod.Container) error { - conConfig = c.Config() - conState, err = c.State() - if err != nil { - return errors.Wrapf(err, "unable to obtain container state") - } - - exitCode, exited, err = c.ExitCode() - if err != nil { - return errors.Wrapf(err, "unable to obtain container exit code") - } - startedTime, err = c.StartedTime() - if err != nil { - logrus.Errorf("error getting started time for %q: %v", c.ID(), err) - } - exitedTime, err = c.FinishedTime() - if err != nil { - logrus.Errorf("error getting exited time for %q: %v", c.ID(), err) - } - - if !opts.Size && !opts.Namespace { - return nil - } - - if opts.Namespace { - pid, err = c.PID() - if err != nil { - return errors.Wrapf(err, "unable to obtain container pid") - } - } - if opts.Size { - size = new(ContainerSize) - - rootFsSize, err := c.RootFsSize() - if err != nil { - logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) - } - - rwSize, err := c.RWSize() - if err != nil { - logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) - } - - size.RootFsSize = rootFsSize - size.RwSize = rwSize - } - return nil - }) - if batchErr != nil { - return BatchContainerStruct{}, batchErr - } - return BatchContainerStruct{ - ConConfig: conConfig, - ConState: conState, - ExitCode: exitCode, - Exited: exited, - Pid: pid, - StartedTime: startedTime, - ExitedTime: exitedTime, - Size: size, - }, nil -} - -// GetNamespaces returns a populated namespace struct. -func GetNamespaces(pid int) *Namespace { - ctrPID := strconv.Itoa(pid) - cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup")) - ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc")) - mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt")) - net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net")) - pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid")) - user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user")) - uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts")) - - return &Namespace{ - PID: ctrPID, - Cgroup: cgroup, - IPC: ipc, - MNT: mnt, - NET: net, - PIDNS: pidns, - User: user, - UTS: uts, - } -} - -// GetNamespaceInfo is an exported wrapper for getNamespaceInfo -func GetNamespaceInfo(path string) (string, error) { - return getNamespaceInfo(path) -} - -func getNamespaceInfo(path string) (string, error) { - val, err := os.Readlink(path) - if err != nil { - return "", errors.Wrapf(err, "error getting info from %q", path) - } - return getStrFromSquareBrackets(val), nil -} - -// getStrFromSquareBrackets gets the string inside [] from a string. -func getStrFromSquareBrackets(cmd string) string { - reg := regexp.MustCompile(`.*\[|\].*`) - arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",") - return strings.Join(arr, ",") -} - -func comparePorts(i, j ocicni.PortMapping) bool { - if i.ContainerPort != j.ContainerPort { - return i.ContainerPort < j.ContainerPort - } - - if i.HostIP != j.HostIP { - return i.HostIP < j.HostIP - } - - if i.HostPort != j.HostPort { - return i.HostPort < j.HostPort - } - - return i.Protocol < j.Protocol -} - -// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> -// e.g 0.0.0.0:1000-1006->1000-1006/tcp. -func formatGroup(key string, start, last int32) string { - parts := strings.Split(key, "/") - groupType := parts[0] - var ip string - if len(parts) > 1 { - ip = parts[0] - groupType = parts[1] - } - group := strconv.Itoa(int(start)) - if start != last { - group = fmt.Sprintf("%s-%d", group, last) - } - if ip != "" { - group = fmt.Sprintf("%s:%s->%s", ip, group, group) - } - return fmt.Sprintf("%s/%s", group, groupType) -} - -// portsToString converts the ports used to a string of the from "port1, port2" -// and also groups a continuous list of ports into a readable format. -func portsToString(ports []ocicni.PortMapping) string { - type portGroup struct { - first int32 - last int32 - } - var portDisplay []string - if len(ports) == 0 { - return "" - } - //Sort the ports, so grouping continuous ports become easy. - sort.Slice(ports, func(i, j int) bool { - return comparePorts(ports[i], ports[j]) - }) - - // portGroupMap is used for grouping continuous ports. - portGroupMap := make(map[string]*portGroup) - var groupKeyList []string - - for _, v := range ports { - - hostIP := v.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - // If hostPort and containerPort are not same, consider as individual port. - if v.ContainerPort != v.HostPort { - portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) - continue - } - - portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) - - portgroup, ok := portGroupMap[portMapKey] - if !ok { - portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} - // This list is required to traverse portGroupMap. - groupKeyList = append(groupKeyList, portMapKey) - continue - } - - if portgroup.last == (v.ContainerPort - 1) { - portgroup.last = v.ContainerPort - continue - } - } - // For each portMapKey, format group list and append to output string. - for _, portKey := range groupKeyList { - group := portGroupMap[portKey] - portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) - } - return strings.Join(portDisplay, ", ") -} - -// GetRunlabel is a helper function for runlabel; it gets the image if needed and begins the -// construction of the runlabel output and environment variables. -func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtime *libpod.Runtime, pull bool, inputCreds string, dockerRegistryOptions image.DockerRegistryOptions, authfile string, signaturePolicyPath string, output io.Writer) (string, string, error) { - var ( - newImage *image.Image - err error - imageName string - ) - if pull { - var registryCreds *types.DockerAuthConfig - if inputCreds != "" { - creds, err := util.ParseRegistryCreds(inputCreds) - if err != nil { - return "", "", err - } - registryCreds = creds - } - dockerRegistryOptions.DockerRegistryCreds = registryCreds - newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, &label, util.PullImageMissing) - } else { - newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage) - } - if err != nil { - return "", "", errors.Wrapf(err, "unable to find image") - } - - if len(newImage.Names()) < 1 { - imageName = newImage.ID() - } else { - imageName = newImage.Names()[0] - } - - runLabel, err := newImage.GetLabel(ctx, label) - return runLabel, imageName, err -} - -// GenerateRunlabelCommand generates the command that will eventually be executed by Podman. -func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string, globalOpts string) ([]string, []string, error) { - // If no name is provided, we use the image's basename instead. - if name == "" { - baseName, err := image.GetImageBaseName(imageName) - if err != nil { - return nil, nil, err - } - name = baseName - } - // The user provided extra arguments that need to be tacked onto the label's command. - if len(extraArgs) > 0 { - runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " ")) - } - cmd, err := GenerateCommand(runLabel, imageName, name, globalOpts) - if err != nil { - return nil, nil, errors.Wrapf(err, "unable to generate command") - } - env := GenerateRunEnvironment(name, imageName, opts) - env = append(env, "PODMAN_RUNLABEL_NESTED=1") - - envmap := envSliceToMap(env) - - envmapper := func(k string) string { - switch k { - case "OPT1": - return envmap["OPT1"] - case "OPT2": - return envmap["OPT2"] - case "OPT3": - return envmap["OPT3"] - case "PWD": - // I would prefer to use os.getenv but it appears PWD is not in the os env list. - d, err := os.Getwd() - if err != nil { - logrus.Error("unable to determine current working directory") - return "" - } - return d - } - return "" - } - newS := os.Expand(strings.Join(cmd, " "), envmapper) - cmd, err = shlex.Split(newS) - if err != nil { - return nil, nil, err - } - return cmd, env, nil -} - -func envSliceToMap(env []string) map[string]string { - m := make(map[string]string) - for _, i := range env { - split := strings.Split(i, "=") - m[split[0]] = strings.Join(split[1:], " ") - } - return m -} - -// GenerateKube generates kubernetes yaml based on a pod or container. -func GenerateKube(name string, service bool, r *libpod.Runtime) (*v1.Pod, *v1.Service, error) { - var ( - pod *libpod.Pod - podYAML *v1.Pod - err error - container *libpod.Container - servicePorts []v1.ServicePort - serviceYAML v1.Service - ) - // Get the container in question. - container, err = r.LookupContainer(name) - if err != nil { - pod, err = r.LookupPod(name) - if err != nil { - return nil, nil, err - } - podYAML, servicePorts, err = pod.GenerateForKube() - } else { - if len(container.Dependencies()) > 0 { - return nil, nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies") - } - podYAML, err = container.GenerateForKube() - } - if err != nil { - return nil, nil, err - } - - if service { - serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts) - } - return podYAML, &serviceYAML, nil -} - -// Parallelize provides the maximum number of parallel workers (int) as calculated by a basic -// heuristic. This can be overridden by the --max-workers primary switch to podman. -func Parallelize(job string) int { - numCpus := runtime.NumCPU() - switch job { - case "kill": - if numCpus <= 3 { - return numCpus * 3 - } - return numCpus * 4 - case "pause": - if numCpus <= 3 { - return numCpus * 3 - } - return numCpus * 4 - case "ps": - return 8 - case "restart": - return numCpus * 2 - case "rm": - if numCpus <= 3 { - return numCpus * 3 - } else { - return numCpus * 4 - } - case "stop": - if numCpus <= 2 { - return 4 - } else { - return numCpus * 3 - } - case "unpause": - if numCpus <= 3 { - return numCpus * 3 - } - return numCpus * 4 - } - return 3 -} diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go deleted file mode 100644 index fef3b6476..000000000 --- a/pkg/varlinkapi/containers.go +++ /dev/null @@ -1,912 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "sync" - "syscall" - "time" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/libpod/logs" - "github.com/containers/podman/v2/pkg/cgroups" - "github.com/containers/podman/v2/pkg/rootless" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/containers/podman/v2/pkg/varlinkapi/virtwriter" - "github.com/containers/storage/pkg/archive" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "k8s.io/client-go/tools/remotecommand" -) - -// ListContainers ... -func (i *VarlinkAPI) ListContainers(call iopodman.VarlinkCall) error { - var ( - listContainers []iopodman.Container - ) - - containers, err := i.Runtime.GetAllContainers() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - opts := PsOptions{ - Namespace: true, - Size: true, - } - for _, ctr := range containers { - batchInfo, err := BatchContainerOp(ctr, opts) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - listContainers = append(listContainers, makeListContainer(ctr.ID(), batchInfo)) - } - return call.ReplyListContainers(listContainers) -} - -func (i *VarlinkAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error { - var ( - containers []iopodman.PsContainer - ) - maxWorkers := Parallelize("ps") - psOpts := makePsOpts(opts) - filters := []string{} - if opts.Filters != nil { - filters = *opts.Filters - } - psContainerOutputs, err := GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - for _, ctr := range psContainerOutputs { - container := iopodman.PsContainer{ - Id: ctr.ID, - Image: ctr.Image, - Command: ctr.Command, - Created: ctr.Created, - Ports: ctr.Ports, - Names: ctr.Names, - IsInfra: ctr.IsInfra, - Status: ctr.Status, - State: ctr.State.String(), - PidNum: int64(ctr.Pid), - Pod: ctr.Pod, - CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano), - ExitedAt: ctr.ExitedAt.Format(time.RFC3339Nano), - StartedAt: ctr.StartedAt.Format(time.RFC3339Nano), - Labels: ctr.Labels, - NsPid: ctr.PID, - Cgroup: ctr.Cgroup, - Ipc: ctr.Cgroup, - Mnt: ctr.MNT, - Net: ctr.NET, - PidNs: ctr.PIDNS, - User: ctr.User, - Uts: ctr.UTS, - Mounts: ctr.Mounts, - } - if ctr.Size != nil { - container.RootFsSize = ctr.Size.RootFsSize - container.RwSize = ctr.Size.RwSize - } - containers = append(containers, container) - } - return call.ReplyPs(containers) -} - -// GetContainer ... -func (i *VarlinkAPI) GetContainer(call iopodman.VarlinkCall, id string) error { - ctr, err := i.Runtime.LookupContainer(id) - if err != nil { - return call.ReplyContainerNotFound(id, err.Error()) - } - opts := PsOptions{ - Namespace: true, - Size: true, - } - batchInfo, err := BatchContainerOp(ctr, opts) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo)) -} - -// getContainersByContext returns a slice of container ids based on all, latest, or a list -func (i *VarlinkAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { - var ids []string - - ctrs, err := getContainersByContext(all, latest, input, i.Runtime) - if err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - return call.ReplyContainerNotFound("", err.Error()) - } - return call.ReplyErrorOccurred(err.Error()) - } - - for _, c := range ctrs { - ids = append(ids, c.ID()) - } - return call.ReplyGetContainersByContext(ids) -} - -// GetContainersByStatus returns a slice of containers filtered by a libpod status -func (i *VarlinkAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses []string) error { - var ( - filterFuncs []libpod.ContainerFilter - containers []iopodman.Container - ) - for _, status := range statuses { - lpstatus, err := define.StringToContainerStatus(status) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { - state, _ := c.State() - return state == lpstatus - }) - } - filteredContainers, err := i.Runtime.GetContainers(filterFuncs...) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - opts := PsOptions{Size: true, Namespace: true} - for _, ctr := range filteredContainers { - batchInfo, err := BatchContainerOp(ctr, opts) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - containers = append(containers, makeListContainer(ctr.ID(), batchInfo)) - } - return call.ReplyGetContainersByStatus(containers) -} - -// InspectContainer ... -func (i *VarlinkAPI) InspectContainer(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - data, err := ctr.Inspect(true) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - b, err := json.Marshal(data) - if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize")) - } - return call.ReplyInspectContainer(string(b)) -} - -// ListContainerProcesses ... -func (i *VarlinkAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - containerState, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if containerState != define.ContainerStateRunning { - return call.ReplyErrorOccurred(fmt.Sprintf("container %s is not running", name)) - } - var psArgs []string - psOpts := []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "comm"} - if len(opts) > 1 { - psOpts = opts - } - psArgs = append(psArgs, psOpts...) - psOutput, err := ctr.GetContainerPidInformation(psArgs) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - return call.ReplyListContainerProcesses(psOutput) -} - -// GetContainerLogs ... -func (i *VarlinkAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) error { - var logs []string - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - logPath := ctr.LogPath() - - containerState, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if _, err := os.Stat(logPath); err != nil { - if containerState == define.ContainerStateConfigured { - return call.ReplyGetContainerLogs(logs) - } - } - file, err := os.Open(logPath) - if err != nil { - return errors.Wrapf(err, "unable to read container log file") - } - defer file.Close() - reader := bufio.NewReader(file) - if call.WantsMore() { - call.Continues = true - } - for { - line, err := reader.ReadString('\n') - // We've read the entire file - if err == io.EOF { - if !call.WantsMore() { - // If this is a non-following log request, we return what we have - break - } else { - // If we want to follow, return what we have, wipe the slice, and make - // sure the container is still running before iterating. - call.ReplyGetContainerLogs(logs) - logs = []string{} - time.Sleep(1 * time.Second) - state, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if state != define.ContainerStateRunning && state != define.ContainerStatePaused { - return call.ReplyErrorOccurred(fmt.Sprintf("%s is no longer running", ctr.ID())) - } - - } - } else if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } else { - logs = append(logs, line) - } - } - - call.Continues = false - - return call.ReplyGetContainerLogs(logs) -} - -// ListContainerChanges ... -func (i *VarlinkAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) error { - changes, err := i.Runtime.GetDiff("", name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - result := iopodman.ContainerChanges{} - for _, change := range changes { - switch change.Kind { - case archive.ChangeModify: - result.Changed = append(result.Changed, change.Path) - case archive.ChangeDelete: - result.Deleted = append(result.Deleted, change.Path) - case archive.ChangeAdd: - result.Added = append(result.Added, change.Path) - } - } - return call.ReplyListContainerChanges(result) -} - -// ExportContainer ... -func (i *VarlinkAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - outputFile, err := ioutil.TempFile("", "varlink_recv") - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - defer outputFile.Close() - if outPath == "" { - outPath = outputFile.Name() - } - if err := ctr.Export(outPath); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyExportContainer(outPath) - -} - -// GetContainerStats ... -func (i *VarlinkAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error { - if rootless.IsRootless() { - cgroupv2, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if !cgroupv2 { - return call.ReplyErrRequiresCgroupsV2ForRootless("rootless containers cannot report container stats") - } - } - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - containerStats, err := ctr.GetContainerStats(&define.ContainerStats{}) - if err != nil { - if errors.Cause(err) == define.ErrCtrStateInvalid { - return call.ReplyNoContainerRunning() - } - return call.ReplyErrorOccurred(err.Error()) - } - cs := iopodman.ContainerStats{ - Id: ctr.ID(), - Name: ctr.Name(), - Cpu: containerStats.CPU, - Cpu_nano: int64(containerStats.CPUNano), - System_nano: int64(containerStats.SystemNano), - Mem_usage: int64(containerStats.MemUsage), - Mem_limit: int64(containerStats.MemLimit), - Mem_perc: containerStats.MemPerc, - Net_input: int64(containerStats.NetInput), - Net_output: int64(containerStats.NetOutput), - Block_input: int64(containerStats.BlockInput), - Block_output: int64(containerStats.BlockOutput), - Pids: int64(containerStats.PIDs), - } - return call.ReplyGetContainerStats(cs) -} - -// StartContainer ... -func (i *VarlinkAPI) StartContainer(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - state, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if state == define.ContainerStateRunning || state == define.ContainerStatePaused { - return call.ReplyErrorOccurred("container is already running or paused") - } - recursive := false - if ctr.PodID() != "" { - recursive = true - } - if err := ctr.Start(getContext(), recursive); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyStartContainer(ctr.ID()) -} - -// InitContainer initializes the container given by Varlink. -func (i *VarlinkAPI) InitContainer(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - if err := ctr.Init(getContext(), false); err != nil { - if errors.Cause(err) == define.ErrCtrStateInvalid { - return call.ReplyInvalidState(ctr.ID(), err.Error()) - } - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyInitContainer(ctr.ID()) -} - -// StopContainer ... -func (i *VarlinkAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - if err := ctr.StopWithTimeout(uint(timeout)); err != nil { - if errors.Cause(err) == define.ErrCtrStopped { - return call.ReplyErrCtrStopped(ctr.ID()) - } - if errors.Cause(err) == define.ErrCtrStateInvalid { - return call.ReplyInvalidState(ctr.ID(), err.Error()) - } - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyStopContainer(ctr.ID()) -} - -// RestartContainer ... -func (i *VarlinkAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - if err := ctr.RestartWithTimeout(getContext(), uint(timeout)); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyRestartContainer(ctr.ID()) -} - -// ContainerExists looks in local storage for the existence of a container -func (i *VarlinkAPI) ContainerExists(call iopodman.VarlinkCall, name string) error { - _, err := i.Runtime.LookupContainer(name) - if errors.Cause(err) == define.ErrNoSuchCtr { - return call.ReplyContainerExists(1) - } - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyContainerExists(0) -} - -// KillContainer kills a running container. If you want to use the default SIGTERM signal, just send a -1 -// for the signal arg. -func (i *VarlinkAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error { - killSignal := uint(syscall.SIGTERM) - if signal != -1 { - killSignal = uint(signal) - } - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - if err := ctr.Kill(killSignal); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyKillContainer(ctr.ID()) -} - -// PauseContainer ... -func (i *VarlinkAPI) PauseContainer(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - if err := ctr.Pause(); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyPauseContainer(ctr.ID()) -} - -// UnpauseContainer ... -func (i *VarlinkAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - if err := ctr.Unpause(); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyUnpauseContainer(ctr.ID()) -} - -// WaitContainer ... -func (i *VarlinkAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - exitCode, err := ctr.WaitWithInterval(time.Duration(interval)) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyWaitContainer(int64(exitCode)) -} - -// RemoveContainer ... -func (i *VarlinkAPI) 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, err.Error()) - } - if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - return call.ReplyContainerExists(1) - } - if errors.Cause(err) == define.ErrCtrStateInvalid { - return call.ReplyInvalidState(ctr.ID(), err.Error()) - } - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyRemoveContainer(ctr.ID()) -} - -// EvictContainer ... -func (i *VarlinkAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error { - ctx := getContext() - id, err := i.Runtime.EvictContainer(ctx, name, removeVolumes) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyEvictContainer(id) -} - -// DeleteStoppedContainers ... -func (i *VarlinkAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { - ctx := getContext() - var deletedContainers []string - containers, err := i.Runtime.GetAllContainers() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - for _, ctr := range containers { - state, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if state != define.ContainerStateRunning { - if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - deletedContainers = append(deletedContainers, ctr.ID()) - } - } - return call.ReplyDeleteStoppedContainers(deletedContainers) -} - -// GetAttachSockets ... -func (i *VarlinkAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - - status, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - // If the container hasn't been run, we need to run init - // so the conmon sockets get created. - if status == define.ContainerStateConfigured || status == define.ContainerStateStopped { - if err := ctr.Init(getContext(), false); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - } - - sockPath, err := ctr.AttachSocketPath() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - s := iopodman.Sockets{ - Container_id: ctr.ID(), - Io_socket: sockPath, - Control_socket: ctr.ControlSocketPath(), - } - return call.ReplyGetAttachSockets(s) -} - -// ContainerCheckpoint ... -func (i *VarlinkAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error { - ctx := getContext() - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - - options := libpod.ContainerCheckpointOptions{ - Keep: keep, - TCPEstablished: tcpEstablished, - KeepRunning: leaveRunning, - } - if err := ctr.Checkpoint(ctx, options); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyContainerCheckpoint(ctr.ID()) -} - -// ContainerRestore ... -func (i *VarlinkAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error { - ctx := getContext() - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - - options := libpod.ContainerCheckpointOptions{ - Keep: keep, - TCPEstablished: tcpEstablished, - } - if err := ctr.Restore(ctx, options); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyContainerRestore(ctr.ID()) -} - -// ContainerConfig returns just the container.config struct -func (i *VarlinkAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - config := ctr.Config() - b, err := json.Marshal(config) - if err != nil { - return call.ReplyErrorOccurred("unable to serialize container config") - } - return call.ReplyContainerConfig(string(b)) -} - -// ContainerArtifacts returns an untouched container's artifact in string format -func (i *VarlinkAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - artifacts, err := ctr.GetArtifact(artifactName) - if err != nil { - return call.ReplyErrorOccurred("unable to get container artifacts") - } - b, err := json.Marshal(artifacts) - if err != nil { - return call.ReplyErrorOccurred("unable to serialize container artifacts") - } - return call.ReplyContainerArtifacts(string(b)) -} - -// ContainerInspectData returns the inspect data of a container in string format -func (i *VarlinkAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, size bool) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - data, err := ctr.Inspect(size) - if err != nil { - return call.ReplyErrorOccurred("unable to inspect container") - } - b, err := json.Marshal(data) - if err != nil { - return call.ReplyErrorOccurred("unable to serialize container inspect data") - } - return call.ReplyContainerInspectData(string(b)) - -} - -// ContainerStateData returns a container's state data in string format -func (i *VarlinkAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - data, err := ctr.ContainerState() - if err != nil { - return call.ReplyErrorOccurred("unable to obtain container state") - } - b, err := json.Marshal(data) - if err != nil { - return call.ReplyErrorOccurred("unable to serialize container inspect data") - } - return call.ReplyContainerStateData(string(b)) -} - -// GetContainerStatsWithHistory is a varlink endpoint that returns container stats based on current and -// previous statistics -func (i *VarlinkAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prevStats iopodman.ContainerStats) error { - con, err := i.Runtime.LookupContainer(prevStats.Id) - if err != nil { - return call.ReplyContainerNotFound(prevStats.Id, err.Error()) - } - previousStats := ContainerStatsToLibpodContainerStats(prevStats) - stats, err := con.GetContainerStats(&previousStats) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - cStats := iopodman.ContainerStats{ - Id: stats.ContainerID, - Name: stats.Name, - Cpu: stats.CPU, - Cpu_nano: int64(stats.CPUNano), - System_nano: int64(stats.SystemNano), - Mem_usage: int64(stats.MemUsage), - Mem_limit: int64(stats.MemLimit), - Mem_perc: stats.MemPerc, - Net_input: int64(stats.NetInput), - Net_output: int64(stats.NetOutput), - Block_input: int64(stats.BlockInput), - Block_output: int64(stats.BlockOutput), - Pids: int64(stats.PIDs), - } - return call.ReplyGetContainerStatsWithHistory(cStats) -} - -// Spec ... -func (i *VarlinkAPI) Spec(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - spec := ctr.Spec() - b, err := json.Marshal(spec) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - return call.ReplySpec(string(b)) -} - -// GetContainersLogs is the varlink endpoint to obtain one or more container logs -func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error { - var wg sync.WaitGroup - if call.WantsMore() { - call.Continues = true - } - sinceTime, err := time.Parse(time.RFC3339Nano, since) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - options := logs.LogOptions{ - Follow: follow, - Since: sinceTime, - Tail: tail, - Timestamps: timestamps, - } - - options.WaitGroup = &wg - if len(names) > 1 { - options.Multi = true - } - tailLen := int(tail) - if tailLen < 0 { - tailLen = 0 - } - logChannel := make(chan *logs.LogLine, tailLen*len(names)+1) - containers, err := getContainersByContext(false, latest, names, i.Runtime) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if err := i.Runtime.Log(getContext(), containers, &options, logChannel); err != nil { - return err - } - go func() { - wg.Wait() - close(logChannel) - }() - for line := range logChannel { - call.ReplyGetContainersLogs(newPodmanLogLine(line)) - if !call.Continues { - break - } - - } - return call.ReplyGetContainersLogs(iopodman.LogLine{}) -} - -func newPodmanLogLine(line *logs.LogLine) iopodman.LogLine { - return iopodman.LogLine{ - Device: line.Device, - ParseLogType: line.ParseLogType, - Time: line.Time.Format(time.RFC3339Nano), - Msg: line.Msg, - Cid: line.CID, - } -} - -// Top displays information about a container's running processes -func (i *VarlinkAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors []string) error { - ctr, err := i.Runtime.LookupContainer(nameOrID) - if err != nil { - return call.ReplyContainerNotFound(ctr.ID(), err.Error()) - } - topInfo, err := ctr.Top(descriptors) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyTop(topInfo) -} - -// ExecContainer is the varlink endpoint to execute a command in a container -func (i *VarlinkAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecOpts) error { - if !call.WantsUpgrade() { - return call.ReplyErrorOccurred("client must use upgraded connection to exec") - } - - ctr, err := i.Runtime.LookupContainer(opts.Name) - if err != nil { - return call.ReplyContainerNotFound(opts.Name, err.Error()) - } - - state, err := ctr.State() - if err != nil { - return call.ReplyErrorOccurred( - fmt.Sprintf("exec failed to obtain container %s state: %s", ctr.ID(), err.Error())) - } - - if state != define.ContainerStateRunning { - return call.ReplyErrorOccurred( - fmt.Sprintf("exec requires a running container, %s is %s", ctr.ID(), state.String())) - } - - // ACK the client upgrade request - call.ReplyExecContainer() - - envs := make(map[string]string) - if opts.Env != nil { - // HACK: The Varlink API uses the old []string format for env, - // storage as "k=v". Split on the = and turn into the new map - // format. - for _, env := range *opts.Env { - splitEnv := strings.SplitN(env, "=", 2) - if len(splitEnv) == 1 { - logrus.Errorf("Got badly-formatted environment variable %q in exec", env) - continue - } - envs[splitEnv[0]] = splitEnv[1] - } - } - - var user string - if opts.User != nil { - user = *opts.User - } - - var workDir string - if opts.Workdir != nil { - workDir = *opts.Workdir - } - - resizeChan := make(chan remotecommand.TerminalSize) - - reader, writer, _, pipeWriter, streams := setupStreams(call) - - type ExitCodeError struct { - ExitCode uint32 - Error error - } - ecErrChan := make(chan ExitCodeError, 1) - - go func() { - if err := virtwriter.Reader(reader, nil, nil, pipeWriter, resizeChan, nil); err != nil { - ecErrChan <- ExitCodeError{ - define.ExecErrorCodeGeneric, - err, - } - } - }() - - execConfig := new(libpod.ExecConfig) - execConfig.Command = opts.Cmd - execConfig.Terminal = opts.Tty - execConfig.Privileged = opts.Privileged - execConfig.Environment = envs - execConfig.User = user - execConfig.WorkDir = workDir - execConfig.DetachKeys = opts.DetachKeys - - go func() { - ec, err := ctr.Exec(execConfig, streams, resizeChan) - if err != nil { - logrus.Errorf(err.Error()) - } - ecErrChan <- ExitCodeError{ - uint32(ec), - err, - } - }() - - ecErr := <-ecErrChan - - exitCode := define.TranslateExecErrorToExitCode(int(ecErr.ExitCode), ecErr.Error) - - if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil { - logrus.Errorf("ExecContainer failed to HANG-UP on %s: %s", ctr.ID(), err.Error()) - } - - if err := call.Writer.Flush(); err != nil { - logrus.Errorf("Exec Container err: %s", err.Error()) - } - - return ecErr.Error -} - -// HealthCheckRun executes defined container's healthcheck command and returns the container's health status. -func (i *VarlinkAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { - hcStatus, err := i.Runtime.HealthCheck(nameOrID) - if err != nil && hcStatus != define.HealthCheckFailure { - return call.ReplyErrorOccurred(err.Error()) - } - status := define.HealthCheckUnhealthy - if hcStatus == define.HealthCheckSuccess { - status = define.HealthCheckHealthy - } - return call.ReplyHealthCheckRun(status) -} diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go deleted file mode 100644 index 771e58089..000000000 --- a/pkg/varlinkapi/containers_create.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - iopodman "github.com/containers/podman/v2/pkg/varlink" -) - -// CreateContainer ... -func (i *VarlinkAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.Create) error { - generic := VarlinkCreateToGeneric(config) - ctr, _, err := CreateContainer(getContext(), &generic, i.Runtime) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyCreateContainer(ctr.ID()) -} diff --git a/pkg/varlinkapi/create.go b/pkg/varlinkapi/create.go deleted file mode 100644 index e9309a2d4..000000000 --- a/pkg/varlinkapi/create.go +++ /dev/null @@ -1,1155 +0,0 @@ -package varlinkapi - -import ( - "context" - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - goruntime "runtime" - "strconv" - "strings" - "syscall" - "time" - - "github.com/containers/common/pkg/sysinfo" - "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v2/cmd/podman/parse" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/libpod/image" - ann "github.com/containers/podman/v2/pkg/annotations" - "github.com/containers/podman/v2/pkg/autoupdate" - "github.com/containers/podman/v2/pkg/cgroups" - envLib "github.com/containers/podman/v2/pkg/env" - "github.com/containers/podman/v2/pkg/errorhandling" - "github.com/containers/podman/v2/pkg/inspect" - ns "github.com/containers/podman/v2/pkg/namespaces" - "github.com/containers/podman/v2/pkg/rootless" - "github.com/containers/podman/v2/pkg/seccomp" - cc "github.com/containers/podman/v2/pkg/spec" - systemdGen "github.com/containers/podman/v2/pkg/systemd/generate" - "github.com/containers/podman/v2/pkg/util" - "github.com/docker/go-connections/nat" - "github.com/docker/go-units" - "github.com/opentracing/opentracing-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -var DefaultKernelNamespaces = "cgroup,ipc,net,uts" - -func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) { - var ( - healthCheck *manifest.Schema2HealthConfig - err error - cidFile *os.File - ) - if c.Bool("trace") { - span, _ := opentracing.StartSpanFromContext(ctx, "createContainer") - defer span.Finish() - } - if c.Bool("rm") && c.String("restart") != "" && c.String("restart") != "no" { - return nil, nil, errors.Errorf("the --rm option conflicts with --restart") - } - - rtc, err := runtime.GetConfig() - if err != nil { - return nil, nil, err - } - rootfs := "" - if c.Bool("rootfs") { - rootfs = c.InputArgs[0] - } - - if c.IsSet("cidfile") { - cidFile, err = util.OpenExclusiveFile(c.String("cidfile")) - if err != nil && os.IsExist(err) { - return nil, nil, errors.Errorf("container id file exists. Ensure another container is not using it or delete %s", c.String("cidfile")) - } - if err != nil { - return nil, nil, errors.Errorf("error opening cidfile %s", c.String("cidfile")) - } - defer errorhandling.CloseQuiet(cidFile) - defer errorhandling.SyncQuiet(cidFile) - } - - imageName := "" - rawImageName := "" - var imageData *inspect.ImageData = nil - - // Set the storage if there is no rootfs specified - if rootfs == "" { - var writer io.Writer - if !c.Bool("quiet") { - writer = os.Stderr - } - - if len(c.InputArgs) != 0 { - rawImageName = c.InputArgs[0] - } else { - return nil, nil, errors.Errorf("error, image name not provided") - } - - pullType, err := util.ValidatePullType(c.String("pull")) - if err != nil { - return nil, nil, err - } - - overrideOS := c.String("override-os") - overrideArch := c.String("override-arch") - dockerRegistryOptions := image.DockerRegistryOptions{ - OSChoice: overrideOS, - ArchitectureChoice: overrideArch, - } - - newImage, err := runtime.ImageRuntime().New(ctx, rawImageName, rtc.Engine.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType) - if err != nil { - return nil, nil, err - } - imageData, err = newImage.InspectNoSize(ctx) - if err != nil { - return nil, nil, err - } - - if overrideOS == "" && imageData.Os != goruntime.GOOS { - logrus.Infof("Using %q (OS) image on %q host", imageData.Os, goruntime.GOOS) - } - - if overrideArch == "" && imageData.Architecture != goruntime.GOARCH { - logrus.Infof("Using %q (architecture) on %q host", imageData.Architecture, goruntime.GOARCH) - } - - names := newImage.Names() - if len(names) > 0 { - imageName = names[0] - } else { - imageName = newImage.ID() - } - - // if the user disabled the healthcheck with "none" or the no-healthcheck - // options is provided, we skip adding it - healthCheckCommandInput := c.String("healthcheck-command") - - // the user didn't disable the healthcheck but did pass in a healthcheck command - // now we need to make a healthcheck from the commandline input - if healthCheckCommandInput != "none" && !c.Bool("no-healthcheck") { - if len(healthCheckCommandInput) > 0 { - healthCheck, err = makeHealthCheckFromCli(c) - if err != nil { - return nil, nil, errors.Wrapf(err, "unable to create healthcheck") - } - } else { - // the user did not disable the health check and did not pass in a healthcheck - // command as input. so now we add healthcheck if it exists AND is correct mediatype - _, mediaType, err := newImage.Manifest(ctx) - if err != nil { - return nil, nil, errors.Wrapf(err, "unable to determine mediatype of image %s", newImage.ID()) - } - if mediaType == manifest.DockerV2Schema2MediaType { - healthCheck, err = newImage.GetHealthCheck(ctx) - if err != nil { - return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0]) - } - - if healthCheck != nil { - hcCommand := healthCheck.Test - if len(hcCommand) < 1 || hcCommand[0] == "" || hcCommand[0] == "NONE" { - // disable health check - healthCheck = nil - } else { - // apply defaults if image doesn't override them - if healthCheck.Interval == 0 { - healthCheck.Interval = 30 * time.Second - } - if healthCheck.Timeout == 0 { - healthCheck.Timeout = 30 * time.Second - } - /* Docker default is 0s, so the following would be a no-op - if healthCheck.StartPeriod == 0 { - healthCheck.StartPeriod = 0 * time.Second - } - */ - if healthCheck.Retries == 0 { - healthCheck.Retries = 3 - } - } - } - } - } - } - } - - createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, rawImageName, imageData) - if err != nil { - return nil, nil, err - } - - // (VR): Ideally we perform the checks _before_ pulling the image but that - // would require some bigger code refactoring of `ParseCreateOpts` and the - // logic here. But as the creation code will be consolidated in the future - // and given auto updates are experimental, we can live with that for now. - // In the end, the user may only need to correct the policy or the raw image - // name. - autoUpdatePolicy, autoUpdatePolicySpecified := createConfig.Labels[autoupdate.Label] - if autoUpdatePolicySpecified { - if _, err := autoupdate.LookupPolicy(autoUpdatePolicy); err != nil { - return nil, nil, err - } - // Now we need to make sure we're having a fully-qualified image reference. - if rootfs != "" { - return nil, nil, errors.Errorf("auto updates do not work with --rootfs") - } - // Make sure the input image is a docker. - if err := autoupdate.ValidateImageReference(rawImageName); err != nil { - return nil, nil, err - } - } - - // Because parseCreateOpts does derive anything from the image, we add health check - // at this point. The rest is done by WithOptions. - createConfig.HealthCheck = healthCheck - - // TODO: Should be able to return this from ParseCreateOpts - var pod *libpod.Pod - if createConfig.Pod != "" { - pod, err = runtime.LookupPod(createConfig.Pod) - if err != nil { - return nil, nil, errors.Wrapf(err, "error looking up pod to join") - } - } - - ctr, err := CreateContainerFromCreateConfig(ctx, runtime, createConfig, pod) - if err != nil { - return nil, nil, err - } - if cidFile != nil { - _, err = cidFile.WriteString(ctr.ID()) - if err != nil { - logrus.Error(err) - } - - } - - logrus.Debugf("New container created %q", ctr.ID()) - return ctr, createConfig, nil -} - -func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string { - entrypoint := []string{} - if c.IsSet("entrypoint") { - // Force entrypoint to "" - if c.String("entrypoint") == "" { - return entrypoint - } - // Check if entrypoint specified is json - if err := json.Unmarshal([]byte(c.String("entrypoint")), &entrypoint); err == nil { - return entrypoint - } - // Return entrypoint as a single command - return []string{c.String("entrypoint")} - } - if data != nil { - return data.Config.Entrypoint - } - return entrypoint -} - -func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, string, error) { - pod, err := runtime.LookupPod(podName) - if err != nil { - return namespaces, "", err - } - podInfraID, err := pod.InfraContainerID() - if err != nil { - return namespaces, "", err - } - hasUserns := false - if podInfraID != "" { - podCtr, err := runtime.GetContainer(podInfraID) - if err != nil { - return namespaces, "", err - } - mappings, err := podCtr.IDMappings() - if err != nil { - return namespaces, "", err - } - hasUserns = len(mappings.UIDMap) > 0 - } - - if (namespaces["pid"] == cc.Pod) || (!c.IsSet("pid") && pod.SharesPID()) { - namespaces["pid"] = fmt.Sprintf("container:%s", podInfraID) - } - if (namespaces["net"] == cc.Pod) || (!c.IsSet("net") && !c.IsSet("network") && pod.SharesNet()) { - namespaces["net"] = fmt.Sprintf("container:%s", podInfraID) - } - if hasUserns && (namespaces["user"] == cc.Pod) || (!c.IsSet("user") && pod.SharesUser()) { - namespaces["user"] = fmt.Sprintf("container:%s", podInfraID) - } - if (namespaces["ipc"] == cc.Pod) || (!c.IsSet("ipc") && pod.SharesIPC()) { - namespaces["ipc"] = fmt.Sprintf("container:%s", podInfraID) - } - if (namespaces["uts"] == cc.Pod) || (!c.IsSet("uts") && pod.SharesUTS()) { - namespaces["uts"] = fmt.Sprintf("container:%s", podInfraID) - } - return namespaces, podInfraID, nil -} - -// Parses CLI options related to container creation into a config which can be -// parsed into an OCI runtime spec -func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime, imageName string, rawImageName string, data *inspect.ImageData) (*cc.CreateConfig, error) { - var ( - inputCommand, command []string - memoryLimit, memoryReservation, memorySwap, memoryKernel int64 - blkioWeight uint16 - namespaces map[string]string - ) - - idmappings, err := util.ParseIDMapping(ns.UsernsMode(c.String("userns")), c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname")) - if err != nil { - return nil, err - } - - imageID := "" - - inputCommand = c.InputArgs[1:] - if data != nil { - imageID = data.ID - } - - rootfs := "" - if c.Bool("rootfs") { - rootfs = c.InputArgs[0] - } - - if c.String("memory") != "" { - memoryLimit, err = units.RAMInBytes(c.String("memory")) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - } - if c.String("memory-reservation") != "" { - memoryReservation, err = units.RAMInBytes(c.String("memory-reservation")) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory-reservation") - } - } - if c.String("memory-swap") != "" { - if c.String("memory-swap") == "-1" { - memorySwap = -1 - } else { - memorySwap, err = units.RAMInBytes(c.String("memory-swap")) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory-swap") - } - } - } - if c.String("kernel-memory") != "" { - memoryKernel, err = units.RAMInBytes(c.String("kernel-memory")) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for kernel-memory") - } - } - if c.String("blkio-weight") != "" { - u, err := strconv.ParseUint(c.String("blkio-weight"), 10, 16) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for blkio-weight") - } - blkioWeight = uint16(u) - } - - tty := c.Bool("tty") - - if c.Changed("cpu-period") && c.Changed("cpus") { - return nil, errors.Errorf("--cpu-period and --cpus cannot be set together") - } - if c.Changed("cpu-quota") && c.Changed("cpus") { - return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together") - } - - if c.Bool("no-hosts") && c.Changed("add-host") { - return nil, errors.Errorf("--no-hosts and --add-host cannot be set together") - } - - // EXPOSED PORTS - var portBindings map[nat.Port][]nat.PortBinding - if data != nil { - portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.Config.ExposedPorts) - if err != nil { - return nil, err - } - } - - // Kernel Namespaces - // TODO Fix handling of namespace from pod - // Instead of integrating here, should be done in libpod - // However, that also involves setting up security opts - // when the pod's namespace is integrated - namespaces = map[string]string{ - "cgroup": c.String("cgroupns"), - "pid": c.String("pid"), - "net": c.String("network"), - "ipc": c.String("ipc"), - "user": c.String("userns"), - "uts": c.String("uts"), - } - - originalPodName := c.String("pod") - podName := strings.Replace(originalPodName, "new:", "", 1) - // after we strip out :new, make sure there is something left for a pod name - if len(podName) < 1 && c.IsSet("pod") { - return nil, errors.Errorf("new pod name must be at least one character") - } - - // If we are adding a container to a pod, we would like to add an annotation for the infra ID - // so kata containers can share VMs inside the pod - var podInfraID string - if c.IsSet("pod") { - if strings.HasPrefix(originalPodName, "new:") { - // pod does not exist; lets make it - var podOptions []libpod.PodCreateOption - podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups()) - if len(portBindings) > 0 { - ociPortBindings, err := cc.NatToOCIPortBindings(portBindings) - if err != nil { - return nil, err - } - podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings)) - } - - podNsOptions, err := GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ",")) - if err != nil { - return nil, err - } - podOptions = append(podOptions, podNsOptions...) - // make pod - pod, err := runtime.NewPod(ctx, podOptions...) - if err != nil { - return nil, err - } - logrus.Debugf("pod %s created by new container request", pod.ID()) - - // The container now cannot have port bindings; so we reset the map - portBindings = make(map[nat.Port][]nat.PortBinding) - } - namespaces, podInfraID, err = configurePod(c, runtime, namespaces, podName) - if err != nil { - return nil, err - } - } - - pidMode := ns.PidMode(namespaces["pid"]) - if !cc.Valid(string(pidMode), pidMode) { - return nil, errors.Errorf("--pid %q is not valid", c.String("pid")) - } - - usernsMode := ns.UsernsMode(namespaces["user"]) - if !cc.Valid(string(usernsMode), usernsMode) { - return nil, errors.Errorf("--userns %q is not valid", namespaces["user"]) - } - - utsMode := ns.UTSMode(namespaces["uts"]) - if !cc.Valid(string(utsMode), utsMode) { - return nil, errors.Errorf("--uts %q is not valid", namespaces["uts"]) - } - - cgroupMode := ns.CgroupMode(namespaces["cgroup"]) - if !cgroupMode.Valid() { - return nil, errors.Errorf("--cgroup %q is not valid", namespaces["cgroup"]) - } - - ipcMode := ns.IpcMode(namespaces["ipc"]) - if !cc.Valid(string(ipcMode), ipcMode) { - return nil, errors.Errorf("--ipc %q is not valid", ipcMode) - } - - // Make sure if network is set to container namespace, port binding is not also being asked for - netMode := ns.NetworkMode(namespaces["net"]) - if netMode.IsContainer() { - if len(portBindings) > 0 { - return nil, errors.Errorf("cannot set port bindings on an existing container network namespace") - } - } - - // USER - user := c.String("user") - if user == "" { - switch { - case usernsMode.IsKeepID(): - user = fmt.Sprintf("%d:%d", rootless.GetRootlessUID(), rootless.GetRootlessGID()) - case data == nil: - user = "0" - default: - user = data.Config.User - } - } - - // STOP SIGNAL - stopSignal := syscall.SIGTERM - signalString := "" - if data != nil { - signalString = data.Config.StopSignal - } - if c.IsSet("stop-signal") { - signalString = c.String("stop-signal") - } - if signalString != "" { - stopSignal, err = util.ParseSignal(signalString) - if err != nil { - return nil, err - } - } - - // ENVIRONMENT VARIABLES - // - // Precedence order (higher index wins): - // 1) env-host, 2) image data, 3) env-file, 4) env - env := map[string]string{ - "container": "podman", - } - - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return nil, errors.Wrap(err, "error parsing host environment variables") - } - - // Start with env-host - - if c.Bool("env-host") { - env = envLib.Join(env, osEnv) - } - - // Image data overrides any previous variables - if data != nil { - configEnv, err := envLib.ParseSlice(data.Config.Env) - if err != nil { - return nil, errors.Wrap(err, "error passing image environment variables") - } - env = envLib.Join(env, configEnv) - } - - // env-file overrides any previous variables - if c.IsSet("env-file") { - for _, f := range c.StringSlice("env-file") { - fileEnv, err := envLib.ParseFile(f) - if err != nil { - return nil, err - } - // File env is overridden by env. - env = envLib.Join(env, fileEnv) - } - } - - if c.IsSet("env") { - // env overrides any previous variables - cmdlineEnv := c.StringSlice("env") - if len(cmdlineEnv) > 0 { - parsedEnv, err := envLib.ParseSlice(cmdlineEnv) - if err != nil { - return nil, err - } - env = envLib.Join(env, parsedEnv) - } - } - - // LABEL VARIABLES - labels, err := parse.GetAllLabels(c.StringSlice("label-file"), c.StringArray("label")) - if err != nil { - return nil, errors.Wrapf(err, "unable to process labels") - } - if data != nil { - for key, val := range data.Config.Labels { - if _, ok := labels[key]; !ok { - labels[key] = val - } - } - } - - if systemdUnit, exists := osEnv[systemdGen.EnvVariable]; exists { - labels[systemdGen.EnvVariable] = systemdUnit - } - - // ANNOTATIONS - annotations := make(map[string]string) - - // First, add our default annotations - annotations[ann.TTY] = "false" - if tty { - annotations[ann.TTY] = "true" - } - - // in the event this container is in a pod, and the pod has an infra container - // we will want to configure it as a type "container" instead defaulting to - // the behavior of a "sandbox" container - // In Kata containers: - // - "sandbox" is the annotation that denotes the container should use its own - // VM, which is the default behavior - // - "container" denotes the container should join the VM of the SandboxID - // (the infra container) - if podInfraID != "" { - annotations[ann.SandboxID] = podInfraID - annotations[ann.ContainerType] = ann.ContainerTypeContainer - } - - if data != nil { - // Next, add annotations from the image - for key, value := range data.Annotations { - annotations[key] = value - } - } - // Last, add user annotations - for _, annotation := range c.StringSlice("annotation") { - splitAnnotation := strings.SplitN(annotation, "=", 2) - if len(splitAnnotation) < 2 { - return nil, errors.Errorf("Annotations must be formatted KEY=VALUE") - } - annotations[splitAnnotation[0]] = splitAnnotation[1] - } - - // WORKING DIRECTORY - workDir := "/" - if c.IsSet("workdir") { - workDir = c.String("workdir") - } else if data != nil && data.Config.WorkingDir != "" { - workDir = data.Config.WorkingDir - } - - userCommand := []string{} - entrypoint := configureEntrypoint(c, data) - // Build the command - // If we have an entry point, it goes first - if len(entrypoint) > 0 { - command = entrypoint - } - if len(inputCommand) > 0 { - // User command overrides data CMD - command = append(command, inputCommand...) - userCommand = append(userCommand, inputCommand...) - } else if data != nil && len(data.Config.Cmd) > 0 && !c.IsSet("entrypoint") { - // If not user command, add CMD - command = append(command, data.Config.Cmd...) - userCommand = append(userCommand, data.Config.Cmd...) - } - - if data != nil && len(command) == 0 { - return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image") - } - - // SHM Size - shmSize, err := units.FromHumanSize(c.String("shm-size")) - if err != nil { - return nil, errors.Wrapf(err, "unable to translate --shm-size") - } - - if c.IsSet("add-host") { - // Verify the additional hosts are in correct format - for _, host := range c.StringSlice("add-host") { - if _, err := parse.ValidateExtraHost(host); err != nil { - return nil, err - } - } - } - - var ( - dnsSearches []string - dnsServers []string - dnsOptions []string - ) - if c.Changed("dns-search") { - dnsSearches = c.StringSlice("dns-search") - // Check for explicit dns-search domain of '' - if len(dnsSearches) == 0 { - return nil, errors.Errorf("'' is not a valid domain") - } - // Validate domains are good - for _, dom := range dnsSearches { - if dom == "." { - if len(dnsSearches) > 1 { - return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") - } - continue - } - if _, err := parse.ValidateDomain(dom); err != nil { - return nil, err - } - } - } - if c.IsSet("dns") { - dnsServers = append(dnsServers, c.StringSlice("dns")...) - } - if c.IsSet("dns-opt") { - dnsOptions = c.StringSlice("dns-opt") - } - - var ImageVolumes map[string]struct{} - if data != nil && c.String("image-volume") != "ignore" { - ImageVolumes = data.Config.Volumes - } - - var imageVolType = map[string]string{ - "bind": "", - "tmpfs": "", - "ignore": "", - } - if _, ok := imageVolType[c.String("image-volume")]; !ok { - return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume")) - } - - systemd := c.String("systemd") == "always" - if !systemd && command != nil { - x, err := strconv.ParseBool(c.String("systemd")) - if err != nil { - return nil, errors.Wrapf(err, "cannot parse bool %s", c.String("systemd")) - } - useSystemdCommands := map[string]bool{ - "/sbin/init": true, - "/usr/sbin/init": true, - "/usr/local/sbin/init": true, - } - if x && (useSystemdCommands[command[0]] || (filepath.Base(command[0]) == "systemd")) { - systemd = true - } - } - if systemd { - if signalString == "" { - stopSignal, err = util.ParseSignal("RTMIN+3") - if err != nil { - return nil, errors.Wrapf(err, "error parsing systemd signal") - } - } - } - // This is done because cobra cannot have two aliased flags. So we have to check - // both - memorySwappiness := c.Int64("memory-swappiness") - - logDriver := define.KubernetesLogging - if c.Changed("log-driver") { - logDriver = c.String("log-driver") - } - - pidsLimit := c.Int64("pids-limit") - if c.String("cgroups") == "disabled" && !c.Changed("pids-limit") { - pidsLimit = -1 - } - - pid := &cc.PidConfig{ - PidMode: pidMode, - } - ipc := &cc.IpcConfig{ - IpcMode: ipcMode, - } - - cgroup := &cc.CgroupConfig{ - Cgroups: c.String("cgroups"), - Cgroupns: c.String("cgroupns"), - CgroupParent: c.String("cgroup-parent"), - CgroupMode: cgroupMode, - } - - userns := &cc.UserConfig{ - GroupAdd: c.StringSlice("group-add"), - IDMappings: idmappings, - UsernsMode: usernsMode, - User: user, - } - - uts := &cc.UtsConfig{ - UtsMode: utsMode, - NoHosts: c.Bool("no-hosts"), - HostAdd: c.StringSlice("add-host"), - Hostname: c.String("hostname"), - } - net := &cc.NetworkConfig{ - DNSOpt: dnsOptions, - DNSSearch: dnsSearches, - DNSServers: dnsServers, - HTTPProxy: c.Bool("http-proxy"), - MacAddress: c.String("mac-address"), - Network: c.String("network"), - NetMode: netMode, - IPAddress: c.String("ip"), - Publish: c.StringSlice("publish"), - PublishAll: c.Bool("publish-all"), - PortBindings: portBindings, - } - - sysctl := map[string]string{} - if c.Changed("sysctl") { - sysctl, err = util.ValidateSysctls(c.StringSlice("sysctl")) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for sysctl") - } - } - - secConfig := &cc.SecurityConfig{ - CapAdd: c.StringSlice("cap-add"), - CapDrop: c.StringSlice("cap-drop"), - Privileged: c.Bool("privileged"), - ReadOnlyRootfs: c.Bool("read-only"), - ReadOnlyTmpfs: c.Bool("read-only-tmpfs"), - Sysctl: sysctl, - } - - var securityOpt []string - if c.Changed("security-opt") { - securityOpt = c.StringArray("security-opt") - } - if err := secConfig.SetSecurityOpts(runtime, securityOpt); err != nil { - return nil, err - } - - // SECCOMP - if data != nil { - if value, exists := labels[seccomp.ContainerImageLabel]; exists { - secConfig.SeccompProfileFromImage = value - } - } - if policy, err := seccomp.LookupPolicy(c.String("seccomp-policy")); err != nil { - return nil, err - } else { - secConfig.SeccompPolicy = policy - } - rtc, err := runtime.GetConfig() - if err != nil { - return nil, err - } - volumes := rtc.Containers.Volumes - if c.Changed("volume") { - volumes = append(volumes, c.StringSlice("volume")...) - } - - devices := rtc.Containers.Devices - if c.Changed("device") { - devices = append(devices, c.StringSlice("device")...) - } - - config := &cc.CreateConfig{ - Annotations: annotations, - BuiltinImgVolumes: ImageVolumes, - ConmonPidFile: c.String("conmon-pidfile"), - ImageVolumeType: c.String("image-volume"), - CidFile: c.String("cidfile"), - Command: command, - UserCommand: userCommand, - Detach: c.Bool("detach"), - Devices: devices, - Entrypoint: entrypoint, - Env: env, - // ExposedPorts: ports, - Init: c.Bool("init"), - InitPath: c.String("init-path"), - Image: imageName, - RawImageName: rawImageName, - ImageID: imageID, - Interactive: c.Bool("interactive"), - // IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6 - Labels: labels, - // LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet - LogDriver: logDriver, - LogDriverOpt: c.StringSlice("log-opt"), - Name: c.String("name"), - // NetworkAlias: c.StringSlice("network-alias"), // Not implemented - does this make sense in Podman? - Pod: podName, - Quiet: c.Bool("quiet"), - Resources: cc.CreateResourceConfig{ - BlkioWeight: blkioWeight, - BlkioWeightDevice: c.StringSlice("blkio-weight-device"), - CPUShares: c.Uint64("cpu-shares"), - CPUPeriod: c.Uint64("cpu-period"), - CPUsetCPUs: c.String("cpuset-cpus"), - CPUsetMems: c.String("cpuset-mems"), - CPUQuota: c.Int64("cpu-quota"), - CPURtPeriod: c.Uint64("cpu-rt-period"), - CPURtRuntime: c.Int64("cpu-rt-runtime"), - CPUs: c.Float64("cpus"), - DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), - DeviceReadBps: c.StringSlice("device-read-bps"), - DeviceReadIOps: c.StringSlice("device-read-iops"), - DeviceWriteBps: c.StringSlice("device-write-bps"), - DeviceWriteIOps: c.StringSlice("device-write-iops"), - DisableOomKiller: c.Bool("oom-kill-disable"), - ShmSize: shmSize, - Memory: memoryLimit, - MemoryReservation: memoryReservation, - MemorySwap: memorySwap, - MemorySwappiness: int(memorySwappiness), - KernelMemory: memoryKernel, - OomScoreAdj: c.Int("oom-score-adj"), - PidsLimit: pidsLimit, - Ulimit: c.StringSlice("ulimit"), - }, - RestartPolicy: c.String("restart"), - Rm: c.Bool("rm"), - Security: *secConfig, - StopSignal: stopSignal, - StopTimeout: c.Uint("stop-timeout"), - Systemd: systemd, - Tmpfs: c.StringArray("tmpfs"), - Tty: tty, - MountsFlag: c.StringArray("mount"), - Volumes: volumes, - WorkDir: workDir, - Rootfs: rootfs, - VolumesFrom: c.StringSlice("volumes-from"), - Syslog: c.Bool("syslog"), - - Pid: *pid, - Ipc: *ipc, - Cgroup: *cgroup, - User: *userns, - Uts: *uts, - Network: *net, - } - - warnings, err := verifyContainerResources(config, false) - if err != nil { - return nil, err - } - for _, warning := range warnings { - fmt.Fprintln(os.Stderr, warning) - } - return config, nil -} - -func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *cc.CreateConfig, pod *libpod.Pod) (*libpod.Container, error) { - runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod) - if err != nil { - return nil, err - } - - ctr, err := r.NewContainer(ctx, runtimeSpec, options...) - if err != nil { - return nil, err - } - return ctr, nil -} - -func makeHealthCheckFromCli(c *GenericCLIResults) (*manifest.Schema2HealthConfig, error) { - inCommand := c.String("healthcheck-command") - inInterval := c.String("healthcheck-interval") - inRetries := c.Uint("healthcheck-retries") - inTimeout := c.String("healthcheck-timeout") - inStartPeriod := c.String("healthcheck-start-period") - - // Every healthcheck requires a command - if len(inCommand) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") - } - - // first try to parse option value as JSON array of strings... - cmd := []string{} - err := json.Unmarshal([]byte(inCommand), &cmd) - if err != nil { - // ...otherwise pass it to "/bin/sh -c" inside the container - cmd = []string{"CMD-SHELL", inCommand} - } - hc := manifest.Schema2HealthConfig{ - Test: cmd, - } - - if inInterval == "disable" { - inInterval = "0" - } - intervalDuration, err := time.ParseDuration(inInterval) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-interval %s ", inInterval) - } - - hc.Interval = intervalDuration - - if inRetries < 1 { - return nil, errors.New("healthcheck-retries must be greater than 0.") - } - hc.Retries = int(inRetries) - timeoutDuration, err := time.ParseDuration(inTimeout) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-timeout %s", inTimeout) - } - if timeoutDuration < time.Duration(1) { - return nil, errors.New("healthcheck-timeout must be at least 1 second") - } - hc.Timeout = timeoutDuration - - startPeriodDuration, err := time.ParseDuration(inStartPeriod) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-start-period %s", inStartPeriod) - } - if startPeriodDuration < time.Duration(0) { - return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") - } - hc.StartPeriod = startPeriodDuration - - return &hc, nil -} - -// GetNamespaceOptions transforms a slice of kernel namespaces -// into a slice of pod create options. Currently, not all -// kernel namespaces are supported, and they will be returned in an error -func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { - var options []libpod.PodCreateOption - var erroredOptions []libpod.PodCreateOption - for _, toShare := range ns { - switch toShare { - case "cgroup": - options = append(options, libpod.WithPodCgroups()) - case "net": - options = append(options, libpod.WithPodNet()) - case "mnt": - return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") - case "pid": - options = append(options, libpod.WithPodPID()) - case "user": - return erroredOptions, errors.Errorf("User sharing functionality not supported on pod level") - case "ipc": - options = append(options, libpod.WithPodIPC()) - case "uts": - options = append(options, libpod.WithPodUTS()) - case "": - case "none": - return erroredOptions, nil - default: - return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: net, pid, ipc, uts or none", toShare) - } - } - return options, nil -} - -func addWarning(warnings []string, msg string) []string { - logrus.Warn(msg) - return append(warnings, msg) -} - -func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) { - warnings := []string{} - - cgroup2, err := cgroups.IsCgroup2UnifiedMode() - if err != nil || cgroup2 { - return warnings, err - } - - sysInfo := sysinfo.New(true) - - // memory subsystem checks and adjustments - if config.Resources.Memory > 0 && !sysInfo.MemoryLimit { - warnings = addWarning(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.") - config.Resources.Memory = 0 - config.Resources.MemorySwap = -1 - } - if config.Resources.Memory > 0 && config.Resources.MemorySwap != -1 && !sysInfo.SwapLimit { - warnings = addWarning(warnings, "Your kernel does not support swap limit capabilities,or the cgroup is not mounted. Memory limited without swap.") - config.Resources.MemorySwap = -1 - } - if config.Resources.Memory > 0 && config.Resources.MemorySwap > 0 && config.Resources.MemorySwap < config.Resources.Memory { - return warnings, fmt.Errorf("minimum memoryswap limit should be larger than memory limit, see usage") - } - if config.Resources.Memory == 0 && config.Resources.MemorySwap > 0 && !update { - return warnings, fmt.Errorf("you should always set the memory limit when using memoryswap limit, see usage") - } - if config.Resources.MemorySwappiness != -1 { - if !sysInfo.MemorySwappiness { - msg := "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded." - warnings = addWarning(warnings, msg) - config.Resources.MemorySwappiness = -1 - } else { - swappiness := config.Resources.MemorySwappiness - if swappiness < -1 || swappiness > 100 { - return warnings, fmt.Errorf("invalid value: %v, valid memory swappiness range is 0-100", swappiness) - } - } - } - if config.Resources.MemoryReservation > 0 && !sysInfo.MemoryReservation { - warnings = addWarning(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.") - config.Resources.MemoryReservation = 0 - } - if config.Resources.Memory > 0 && config.Resources.MemoryReservation > 0 && config.Resources.Memory < config.Resources.MemoryReservation { - return warnings, fmt.Errorf("minimum memory limit cannot be less than memory reservation limit, see usage") - } - if config.Resources.KernelMemory > 0 && !sysInfo.KernelMemory { - warnings = addWarning(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.") - config.Resources.KernelMemory = 0 - } - if config.Resources.DisableOomKiller && !sysInfo.OomKillDisable { - // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point - // warning the caller if they already wanted the feature to be off - warnings = addWarning(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.") - config.Resources.DisableOomKiller = false - } - - if config.Resources.PidsLimit != 0 && !sysInfo.PidsLimit { - warnings = addWarning(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") - config.Resources.PidsLimit = 0 - } - - if config.Resources.CPUShares > 0 && !sysInfo.CPUShares { - warnings = addWarning(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.") - config.Resources.CPUShares = 0 - } - if config.Resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod { - warnings = addWarning(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.") - config.Resources.CPUPeriod = 0 - } - if config.Resources.CPUPeriod != 0 && (config.Resources.CPUPeriod < 1000 || config.Resources.CPUPeriod > 1000000) { - return warnings, fmt.Errorf("CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)") - } - if config.Resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota { - warnings = addWarning(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.") - config.Resources.CPUQuota = 0 - } - if config.Resources.CPUQuota > 0 && config.Resources.CPUQuota < 1000 { - return warnings, fmt.Errorf("CPU cfs quota cannot be less than 1ms (i.e. 1000)") - } - // cpuset subsystem checks and adjustments - if (config.Resources.CPUsetCPUs != "" || config.Resources.CPUsetMems != "") && !sysInfo.Cpuset { - warnings = addWarning(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. CPUset discarded.") - config.Resources.CPUsetCPUs = "" - config.Resources.CPUsetMems = "" - } - cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(config.Resources.CPUsetCPUs) - if err != nil { - return warnings, fmt.Errorf("invalid value %s for cpuset cpus", config.Resources.CPUsetCPUs) - } - if !cpusAvailable { - return warnings, fmt.Errorf("requested CPUs are not available - requested %s, available: %s", config.Resources.CPUsetCPUs, sysInfo.Cpus) - } - memsAvailable, err := sysInfo.IsCpusetMemsAvailable(config.Resources.CPUsetMems) - if err != nil { - return warnings, fmt.Errorf("invalid value %s for cpuset mems", config.Resources.CPUsetMems) - } - if !memsAvailable { - return warnings, fmt.Errorf("requested memory nodes are not available - requested %s, available: %s", config.Resources.CPUsetMems, sysInfo.Mems) - } - - // blkio subsystem checks and adjustments - if config.Resources.BlkioWeight > 0 && !sysInfo.BlkioWeight { - warnings = addWarning(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.") - config.Resources.BlkioWeight = 0 - } - if config.Resources.BlkioWeight > 0 && (config.Resources.BlkioWeight < 10 || config.Resources.BlkioWeight > 1000) { - return warnings, fmt.Errorf("range of blkio weight is from 10 to 1000") - } - if len(config.Resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice { - warnings = addWarning(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.") - config.Resources.BlkioWeightDevice = []string{} - } - if len(config.Resources.DeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice { - warnings = addWarning(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded") - config.Resources.DeviceReadBps = []string{} - } - if len(config.Resources.DeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice { - warnings = addWarning(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.") - config.Resources.DeviceWriteBps = []string{} - } - if len(config.Resources.DeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice { - warnings = addWarning(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.") - config.Resources.DeviceReadIOps = []string{} - } - if len(config.Resources.DeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice { - warnings = addWarning(warnings, "Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.") - config.Resources.DeviceWriteIOps = []string{} - } - - return warnings, nil -} diff --git a/pkg/varlinkapi/events.go b/pkg/varlinkapi/events.go deleted file mode 100644 index 8628b1ce6..000000000 --- a/pkg/varlinkapi/events.go +++ /dev/null @@ -1,56 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "context" - "time" - - "github.com/containers/podman/v2/libpod/events" - iopodman "github.com/containers/podman/v2/pkg/varlink" -) - -// GetEvents is a remote endpoint to get events from the event log -func (i *VarlinkAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since string, until string) error { - var ( - fromStart bool - eventsError error - event *events.Event - stream bool - ) - if call.WantsMore() { - stream = true - call.Continues = true - } - if len(since) > 0 || len(until) > 0 { - fromStart = true - } - eventChannel := make(chan *events.Event) - go func() { - readOpts := events.ReadOptions{FromStart: fromStart, Stream: stream, Filters: filter, EventChannel: eventChannel} - eventsError = i.Runtime.Events(context.Background(), readOpts) - }() - if eventsError != nil { - return call.ReplyErrorOccurred(eventsError.Error()) - } - for { - event = <-eventChannel - if event == nil { - call.Continues = false - break - } - call.ReplyGetEvents(iopodman.Event{ - Id: event.ID, - Image: event.Image, - Name: event.Name, - Status: string(event.Status), - Time: event.Time.Format(time.RFC3339Nano), - Type: string(event.Type), - }) - if !call.Continues { - // For a one-shot on events, we break out here - break - } - } - return nil -} diff --git a/pkg/varlinkapi/funcs.go b/pkg/varlinkapi/funcs.go deleted file mode 100644 index 8fb8a7ea0..000000000 --- a/pkg/varlinkapi/funcs.go +++ /dev/null @@ -1,121 +0,0 @@ -package varlinkapi - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/containers/image/v5/types" - "github.com/containers/podman/v2/libpod/image" - "github.com/google/shlex" - "github.com/pkg/errors" -) - -func GetSystemContext(authfile string) (*types.SystemContext, error) { - if authfile != "" { - if _, err := os.Stat(authfile); err != nil { - return nil, errors.Wrapf(err, "error checking authfile path %s", authfile) - } - } - return image.GetSystemContext("", authfile, false), nil -} - -func substituteCommand(cmd string) (string, error) { - var ( - newCommand string - ) - - // Replace cmd with "/proc/self/exe" if "podman" or "docker" is being - // used. If "/usr/bin/docker" is provided, we also sub in podman. - // Otherwise, leave the command unchanged. - if cmd == "podman" || filepath.Base(cmd) == "docker" { - newCommand = "/proc/self/exe" - } else { - newCommand = cmd - } - - // If cmd is an absolute or relative path, check if the file exists. - // Throw an error if it doesn't exist. - if strings.Contains(newCommand, "/") || strings.HasPrefix(newCommand, ".") { - res, err := filepath.Abs(newCommand) - if err != nil { - return "", err - } - if _, err := os.Stat(res); !os.IsNotExist(err) { - return res, nil - } else if err != nil { - return "", err - } - } - - return newCommand, nil -} - -// GenerateCommand takes a label (string) and converts it to an executable command -func GenerateCommand(command, imageName, name, globalOpts string) ([]string, error) { - var ( - newCommand []string - ) - if name == "" { - name = imageName - } - - cmd, err := shlex.Split(command) - if err != nil { - return nil, err - } - - prog, err := substituteCommand(cmd[0]) - if err != nil { - return nil, err - } - newCommand = append(newCommand, prog) - - for _, arg := range cmd[1:] { - var newArg string - switch arg { - case "IMAGE": - newArg = imageName - case "$IMAGE": - newArg = imageName - case "IMAGE=IMAGE": - newArg = fmt.Sprintf("IMAGE=%s", imageName) - case "IMAGE=$IMAGE": - newArg = fmt.Sprintf("IMAGE=%s", imageName) - case "NAME": - newArg = name - case "NAME=NAME": - newArg = fmt.Sprintf("NAME=%s", name) - case "NAME=$NAME": - newArg = fmt.Sprintf("NAME=%s", name) - case "$NAME": - newArg = name - case "$GLOBAL_OPTS": - newArg = globalOpts - default: - newArg = arg - } - newCommand = append(newCommand, newArg) - } - return newCommand, nil -} - -// GenerateRunEnvironment merges the current environment variables with optional -// environment variables provided by the user -func GenerateRunEnvironment(name, imageName string, opts map[string]string) []string { - newEnv := os.Environ() - newEnv = append(newEnv, fmt.Sprintf("NAME=%s", name)) - newEnv = append(newEnv, fmt.Sprintf("IMAGE=%s", imageName)) - - if opts["opt1"] != "" { - newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", opts["opt1"])) - } - if opts["opt2"] != "" { - newEnv = append(newEnv, fmt.Sprintf("OPT2=%s", opts["opt2"])) - } - if opts["opt3"] != "" { - newEnv = append(newEnv, fmt.Sprintf("OPT3=%s", opts["opt3"])) - } - return newEnv -} diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go deleted file mode 100644 index bbc16dae5..000000000 --- a/pkg/varlinkapi/generate.go +++ /dev/null @@ -1,30 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "encoding/json" - - iopodman "github.com/containers/podman/v2/pkg/varlink" -) - -// GenerateKube ... -func (i *VarlinkAPI) GenerateKube(call iopodman.VarlinkCall, name string, service bool) error { - pod, serv, err := GenerateKube(name, service, i.Runtime) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - podB, err := json.Marshal(pod) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - servB, err := json.Marshal(serv) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - return call.ReplyGenerateKube(iopodman.KubePodService{ - Pod: string(podB), - Service: string(servB), - }) -} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go deleted file mode 100644 index af6c43fec..000000000 --- a/pkg/varlinkapi/images.go +++ /dev/null @@ -1,1037 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - "github.com/containers/buildah" - "github.com/containers/buildah/imagebuildah" - dockerarchive "github.com/containers/image/v5/docker/archive" - "github.com/containers/image/v5/manifest" - "github.com/containers/image/v5/transports/alltransports" - "github.com/containers/image/v5/types" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/libpod/image" - "github.com/containers/podman/v2/pkg/channel" - "github.com/containers/podman/v2/pkg/util" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/containers/podman/v2/utils" - "github.com/containers/storage/pkg/archive" - v1 "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ListImagesWithFilters returns a list of images that have been filtered -func (i *VarlinkAPI) ListImagesWithFilters(call iopodman.VarlinkCall, filters []string) error { - images, err := i.Runtime.ImageRuntime().GetImagesWithFilters(filters) - if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to get list of images %q", err)) - } - imageList, err := imagesToImageList(images) - if err != nil { - return call.ReplyErrorOccurred("unable to parse response " + err.Error()) - } - return call.ReplyListImagesWithFilters(imageList) -} - -// imagesToImageList converts a slice of Images to an imagelist for varlink responses -func imagesToImageList(images []*image.Image) ([]iopodman.Image, error) { - var imageList []iopodman.Image - for _, img := range images { - labels, _ := img.Labels(getContext()) - containers, _ := img.Containers() - repoDigests, err := img.RepoDigests() - if err != nil { - return nil, err - } - - size, _ := img.Size(getContext()) - isParent, err := img.IsParent(context.TODO()) - if err != nil { - return nil, err - } - - i := iopodman.Image{ - Id: img.ID(), - Digest: string(img.Digest()), - ParentId: img.Parent, - RepoTags: img.Names(), - RepoDigests: repoDigests, - Created: img.Created().Format(time.RFC3339), - Size: int64(*size), - VirtualSize: img.VirtualSize, - Containers: int64(len(containers)), - Labels: labels, - IsParent: isParent, - ReadOnly: img.IsReadOnly(), - History: img.NamesHistory(), - } - imageList = append(imageList, i) - } - return imageList, nil -} - -// ListImages lists all the images in the store -// It requires no inputs. -func (i *VarlinkAPI) ListImages(call iopodman.VarlinkCall) error { - images, err := i.Runtime.ImageRuntime().GetImages() - if err != nil { - return call.ReplyErrorOccurred("unable to get list of images " + err.Error()) - } - imageList, err := imagesToImageList(images) - if err != nil { - return call.ReplyErrorOccurred("unable to parse response " + err.Error()) - } - return call.ReplyListImages(imageList) -} - -// GetImage returns a single image in the form of a Image -func (i *VarlinkAPI) GetImage(call iopodman.VarlinkCall, id string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(id) - if err != nil { - return call.ReplyImageNotFound(id, err.Error()) - } - labels, err := newImage.Labels(getContext()) - if err != nil { - return err - } - containers, err := newImage.Containers() - if err != nil { - return err - } - repoDigests, err := newImage.RepoDigests() - if err != nil { - return err - } - size, err := newImage.Size(getContext()) - if err != nil { - return err - } - - il := iopodman.Image{ - Id: newImage.ID(), - ParentId: newImage.Parent, - RepoTags: newImage.Names(), - RepoDigests: repoDigests, - Created: newImage.Created().Format(time.RFC3339), - Size: int64(*size), - VirtualSize: newImage.VirtualSize, - Containers: int64(len(containers)), - Labels: labels, - TopLayer: newImage.TopLayer(), - ReadOnly: newImage.IsReadOnly(), - History: newImage.NamesHistory(), - } - return call.ReplyGetImage(il) -} - -// BuildImage ... -func (i *VarlinkAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildInfo) error { - var ( - namespace []buildah.NamespaceOption - imageID string - err error - ) - - contextDir := config.ContextDir - - newContextDir, err := ioutil.TempDir("", "buildTarball") - if err != nil { - call.ReplyErrorOccurred("unable to create tempdir") - } - logrus.Debugf("created new context dir at %s", newContextDir) - - reader, err := os.Open(contextDir) - if err != nil { - logrus.Errorf("failed to open the context dir tar file") - return call.ReplyErrorOccurred(fmt.Sprintf("unable to open context dir tar file %s", contextDir)) - } - 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) - defer func() { - if err := os.Remove(contextDir); err != nil { - logrus.Error(err) - } - if err := os.RemoveAll(newContextDir); err != nil { - logrus.Errorf("unable to delete directory '%s': %q", newContextDir, err) - } - }() - - systemContext := types.SystemContext{} - // All output (stdout, stderr) is captured in output as well - var output bytes.Buffer - - commonOpts := &buildah.CommonBuildOptions{ - 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, - } - - options := imagebuildah.BuildOptions{ - AddCapabilities: config.AddCapabilities, - AdditionalTags: config.AdditionalTags, - Annotations: config.Annotations, - Architecture: config.Architecture, - Args: config.BuildArgs, - CNIConfigDir: config.CniConfigDir, - CNIPluginPath: config.CniPluginDir, - CommonBuildOpts: commonOpts, - Compression: stringCompressionToArchiveType(config.Compression), - ContextDirectory: newContextDir, - DefaultMountsFilePath: config.DefaultsMountFilePath, - Devices: config.Devices, - Err: &output, - ForceRmIntermediateCtrs: config.ForceRmIntermediateCtrs, - IIDFile: config.Iidfile, - Labels: config.Label, - Layers: config.Layers, - NamespaceOptions: namespace, - NoCache: config.Nocache, - OS: config.Os, - Out: &output, - Output: config.Output, - OutputFormat: config.OutputFormat, - PullPolicy: stringPullPolicyToType(config.PullPolicy), - Quiet: config.Quiet, - RemoveIntermediateCtrs: config.RemoteIntermediateCtrs, - ReportWriter: &output, - RuntimeArgs: config.RuntimeArgs, - SignBy: config.SignBy, - Squash: config.Squash, - SystemContext: &systemContext, - Target: config.Target, - TransientMounts: config.TransientMounts, - } - - if call.WantsMore() { - call.Continues = true - } else { - return call.ReplyErrorOccurred("endpoint requires a more connection") - } - - 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 := make(chan error) - go func() { - iid, _, err := i.Runtime.Build(getContext(), options, newPathDockerFiles...) - imageID = iid - c <- err - close(c) - }() - - var log []string - done := false - for { - outputLine, err := output.ReadString('\n') - if err == nil { - 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 { - case err := <-c: - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - done = true - default: - if call.WantsMore() { - time.Sleep(1 * time.Second) - break - } - } - } else { - return call.ReplyErrorOccurred(err.Error()) - } - if done { - break - } - } - call.Continues = false - - br := iopodman.MoreResponse{ - Logs: log, - Id: imageID, - } - return call.ReplyBuildImage(br) -} - -// InspectImage returns an image's inspect information as a string that can be serialized. -// Requires an image ID or name -func (i *VarlinkAPI) InspectImage(call iopodman.VarlinkCall, name string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - inspectInfo, err := newImage.Inspect(getContext()) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - b, err := json.Marshal(inspectInfo) - if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize")) - } - return call.ReplyInspectImage(string(b)) -} - -// HistoryImage returns the history of the image's layers -// Requires an image or name -func (i *VarlinkAPI) HistoryImage(call iopodman.VarlinkCall, name string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - history, err := newImage.History(getContext()) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - var histories []iopodman.ImageHistory - for _, hist := range history { - imageHistory := iopodman.ImageHistory{ - Id: hist.ID, - Created: hist.Created.Format(time.RFC3339), - CreatedBy: hist.CreatedBy, - Tags: newImage.Names(), - Size: hist.Size, - Comment: hist.Comment, - } - histories = append(histories, imageHistory) - } - return call.ReplyHistoryImage(histories) -} - -// PushImage pushes an local image to registry -func (i *VarlinkAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compress bool, format string, removeSignatures bool, signBy string) error { - var ( - manifestType string - ) - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - destname := name - if tag != "" { - destname = tag - } - dockerRegistryOptions := image.DockerRegistryOptions{} - if format != "" { - switch format { - case "oci": // nolint - manifestType = v1.MediaTypeImageManifest - case "v2s1": - manifestType = manifest.DockerV2Schema1SignedMediaType - case "v2s2", "docker": - manifestType = manifest.DockerV2Schema2MediaType - default: - return call.ReplyErrorOccurred(fmt.Sprintf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", format)) - } - } - so := image.SigningOptions{ - RemoveSignatures: removeSignatures, - SignBy: signBy, - } - - if call.WantsMore() { - call.Continues = true - } - - output := bytes.NewBuffer([]byte{}) - c := make(chan error) - go func() { - writer := bytes.NewBuffer([]byte{}) - err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", "", writer, compress, so, &dockerRegistryOptions, nil) - if err != nil { - c <- err - } - _, err = io.CopyBuffer(output, writer, 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() { - break - } - br := iopodman.MoreResponse{ - Logs: log, - Id: newImage.ID(), - } - call.ReplyPushImage(br) - log = []string{} - } - } else { - return call.ReplyErrorOccurred(err.Error()) - } - if done { - break - } - } - call.Continues = false - - br := iopodman.MoreResponse{ - Logs: log, - Id: newImage.ID(), - } - return call.ReplyPushImage(br) -} - -// TagImage accepts an image name and tag as strings and tags an image in the local store. -func (i *VarlinkAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - if err := newImage.TagImage(tag); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyTagImage(newImage.ID()) -} - -// UntagImage accepts an image name and tag as strings and removes the tag from the local store. -func (i *VarlinkAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - if err := newImage.UntagImage(tag); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyUntagImage(newImage.ID()) -} - -// RemoveImage accepts a image name or ID as a string and force bool to determine if it should -// remove the image even if being used by stopped containers -func (i *VarlinkAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bool) error { - ctx := getContext() - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - _, err = i.Runtime.RemoveImage(ctx, newImage, force) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyRemoveImage(newImage.ID()) -} - -// RemoveImageWithResponse accepts an image name and force bool. It returns details about what -// was done in removeimageresponse struct. -func (i *VarlinkAPI) RemoveImageWithResponse(call iopodman.VarlinkCall, name string, force bool) error { - ir := iopodman.RemoveImageResponse{} - ctx := getContext() - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - response, err := i.Runtime.RemoveImage(ctx, newImage, force) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - ir.Untagged = append(ir.Untagged, response.Untagged...) - ir.Deleted = response.Deleted - return call.ReplyRemoveImageWithResponse(ir) -} - -// 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 *VarlinkAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, filter iopodman.ImageSearchFilter) error { - // Transform all arguments to proper types first - argLimit := 0 - argIsOfficial := types.OptionalBoolUndefined - argIsAutomated := types.OptionalBoolUndefined - if limit != nil { - argLimit = int(*limit) - } - if filter.Is_official != nil { - argIsOfficial = types.NewOptionalBool(*filter.Is_official) - } - if filter.Is_automated != nil { - argIsAutomated = types.NewOptionalBool(*filter.Is_automated) - } - - // Transform a SearchFilter the backend can deal with - sFilter := image.SearchFilter{ - IsOfficial: argIsOfficial, - IsAutomated: argIsAutomated, - Stars: int(filter.Star_count), - } - - searchOptions := image.SearchOptions{ - Limit: argLimit, - Filter: sFilter, - } - results, err := image.SearchImages(query, searchOptions) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - var imageResults []iopodman.ImageSearchResult - for _, result := range results { - i := iopodman.ImageSearchResult{ - Registry: result.Index, - Description: result.Description, - Is_official: result.Official == "[OK]", - Is_automated: result.Automated == "[OK]", - Name: result.Name, - Star_count: int64(result.Stars), - } - imageResults = append(imageResults, i) - } - return call.ReplySearchImages(imageResults) -} - -// DeleteUnusedImages deletes any images that do not have containers associated with it. -// TODO Filters are not implemented -func (i *VarlinkAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error { - images, err := i.Runtime.ImageRuntime().GetImages() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - var deletedImages []string - for _, img := range images { - containers, err := img.Containers() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if len(containers) == 0 { - if err := img.Remove(context.TODO(), false); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - deletedImages = append(deletedImages, img.ID()) - } - } - return call.ReplyDeleteUnusedImages(deletedImages) -} - -// Commit ... -func (i *VarlinkAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error { - var ( - newImage *image.Image - log []string - mimeType string - ) - output := channel.NewWriter(make(chan []byte)) - channelClose := func() { - if err := output.Close(); err != nil { - logrus.Errorf("failed to close channel writer: %q", err) - } - } - defer channelClose() - - ctr, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyContainerNotFound(name, err.Error()) - } - rtc, err := i.Runtime.GetConfig() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) - switch manifestType { - case "oci", "": // nolint - mimeType = buildah.OCIv1ImageManifest - case "docker": - mimeType = manifest.DockerV2Schema2MediaType - default: - return call.ReplyErrorOccurred(fmt.Sprintf("unrecognized image format %q", manifestType)) - } - coptions := buildah.CommitOptions{ - SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, - ReportWriter: output, - SystemContext: sc, - PreferredManifestType: mimeType, - } - options := libpod.ContainerCommitOptions{ - CommitOptions: coptions, - Pause: pause, - Message: message, - Changes: changes, - Author: author, - } - - if call.WantsMore() { - call.Continues = true - } - - c := make(chan error) - - go func() { - newImage, err = ctr.Commit(getContext(), imageName, options) - if err != nil { - c <- err - } - c <- nil - close(c) - }() - - // reply is the func being sent to the output forwarder. in this case it is replying - // with a more response struct - reply := func(br iopodman.MoreResponse) error { - return call.ReplyCommit(br) - } - log, err = forwardOutput(log, c, call.WantsMore(), output, reply) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - call.Continues = false - br := iopodman.MoreResponse{ - Logs: log, - Id: newImage.ID(), - } - return call.ReplyCommit(br) -} - -// ImportImage imports an image from a tarball to the image store -func (i *VarlinkAPI) ImportImage(call iopodman.VarlinkCall, source, reference, message string, changes []string, delete bool) error { - configChanges, err := util.GetImageConfig(changes) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - history := []v1.History{ - {Comment: message}, - } - config := v1.Image{ - Config: configChanges.ImageConfig, - History: history, - } - newImage, err := i.Runtime.ImageRuntime().Import(getContext(), source, reference, nil, image.SigningOptions{}, config) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if delete { - if err := os.Remove(source); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - } - - return call.ReplyImportImage(newImage.ID()) -} - -// ExportImage exports an image to the provided destination -// destination must have the transport type!! -func (i *VarlinkAPI) ExportImage(call iopodman.VarlinkCall, name, destination string, compress bool, tags []string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyImageNotFound(name, err.Error()) - } - - additionalTags, err := image.GetAdditionalTags(tags) - if err != nil { - return err - } - - if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyExportImage(newImage.ID()) -} - -// PullImage pulls an image from a registry to the image store. -func (i *VarlinkAPI) PullImage(call iopodman.VarlinkCall, name string, creds iopodman.AuthConfig) error { - var ( - imageID string - err error - ) - dockerRegistryOptions := image.DockerRegistryOptions{ - DockerRegistryCreds: &types.DockerAuthConfig{ - Username: creds.Username, - Password: creds.Password, - }, - } - - so := image.SigningOptions{} - - if call.WantsMore() { - call.Continues = true - } - output := channel.NewWriter(make(chan []byte)) - channelClose := func() { - if err := output.Close(); err != nil { - logrus.Errorf("failed to close channel writer: %q", err) - } - } - defer channelClose() - c := make(chan error) - defer close(c) - - go func() { - var foundError bool - if strings.HasPrefix(name, dockerarchive.Transport.Name()+":") { - srcRef, err := alltransports.ParseImageName(name) - if err != nil { - c <- errors.Wrapf(err, "error parsing %q", name) - } - newImage, err := i.Runtime.ImageRuntime().LoadFromArchiveReference(getContext(), srcRef, "", output) - if err != nil { - foundError = true - c <- errors.Wrapf(err, "error pulling image from %q", name) - } else { - imageID = newImage[0].ID() - } - } else { - newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, "", "", output, &dockerRegistryOptions, so, nil, util.PullImageMissing) - if err != nil { - foundError = true - c <- errors.Wrapf(err, "unable to pull %s", name) - } else { - imageID = newImage.ID() - } - } - if !foundError { - c <- nil - } - }() - - var log []string - reply := func(br iopodman.MoreResponse) error { - return call.ReplyPullImage(br) - } - log, err = forwardOutput(log, c, call.WantsMore(), output, reply) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - call.Continues = false - br := iopodman.MoreResponse{ - Logs: log, - Id: imageID, - } - return call.ReplyPullImage(br) -} - -// ImageExists returns bool as to whether the input image exists in local storage -func (i *VarlinkAPI) ImageExists(call iopodman.VarlinkCall, name string) error { - _, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if errors.Cause(err) == image.ErrNoSuchImage { - return call.ReplyImageExists(1) - } - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyImageExists(0) -} - -// ContainerRunlabel ... -func (i *VarlinkAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.Runlabel) error { - ctx := getContext() - dockerRegistryOptions := image.DockerRegistryOptions{} - stdErr := os.Stderr - stdOut := os.Stdout - stdIn := os.Stdin - - runLabel, imageName, err := GetRunlabel(input.Label, input.Image, ctx, i.Runtime, input.Pull, "", dockerRegistryOptions, input.Authfile, "", nil) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if runLabel == "" { - return call.ReplyErrorOccurred(fmt.Sprintf("%s does not contain the label %s", input.Image, input.Label)) - } - - cmd, env, err := GenerateRunlabelCommand(runLabel, imageName, input.Name, input.Opts, input.ExtraArgs, "") - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if err := utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyContainerRunlabel() -} - -// ImagesPrune .... -func (i *VarlinkAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []string) error { - prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all, []string{}) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyImagesPrune(prunedImages) -} - -// ImageSave .... -func (i *VarlinkAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageSaveOptions) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(options.Name) - if err != nil { - if errors.Cause(err) == define.ErrNoSuchImage { - return call.ReplyImageNotFound(options.Name, err.Error()) - } - return call.ReplyErrorOccurred(err.Error()) - } - - // Determine if we are dealing with a tarball or dir - var output string - outputToDir := false - if options.Format == "oci-archive" || options.Format == "docker-archive" { - tempfile, err := ioutil.TempFile("", "varlink_send") - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - output = tempfile.Name() - tempfile.Close() - } else { - var err error - outputToDir = true - output, err = ioutil.TempDir("", "varlink_send") - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - } - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if call.WantsMore() { - call.Continues = true - } - - saveOutput := bytes.NewBuffer([]byte{}) - c := make(chan error) - go func() { - err := newImage.Save(getContext(), options.Name, options.Format, output, options.MoreTags, options.Quiet, options.Compress, true) - c <- err - close(c) - }() - var log []string - done := false - for { - line, err := saveOutput.ReadString('\n') - if err == nil { - log = append(log, line) - continue - } else if err == io.EOF { - select { - case err := <-c: - if err != nil { - logrus.Errorf("reading of output during save failed for %s", newImage.ID()) - return call.ReplyErrorOccurred(err.Error()) - } - done = true - default: - if !call.WantsMore() { - break - } - br := iopodman.MoreResponse{ - Logs: log, - } - call.ReplyImageSave(br) - log = []string{} - } - } else { - return call.ReplyErrorOccurred(err.Error()) - } - if done { - break - } - } - call.Continues = false - - sendfile := output - // Image has been saved to `output` - if outputToDir { - // If the output is a directory, we need to tar up the directory to send it back - // Create a tempfile for the directory tarball - outputFile, err := ioutil.TempFile("", "varlink_save_dir") - if err != nil { - return err - } - defer outputFile.Close() - if err := utils.TarToFilesystem(output, outputFile); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - sendfile = outputFile.Name() - } - br := iopodman.MoreResponse{ - Logs: log, - Id: sendfile, - } - return call.ReplyPushImage(br) -} - -// LoadImage ... -func (i *VarlinkAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, deleteInputFile, quiet bool) error { - var ( - names string - writer io.Writer - err error - ) - if !quiet { - writer = os.Stderr - } - - if call.WantsMore() { - call.Continues = true - } - output := bytes.NewBuffer([]byte{}) - - c := make(chan error) - go func() { - names, err = i.Runtime.LoadImage(getContext(), name, inputFile, writer, "") - c <- err - close(c) - }() - - var log []string - done := false - for { - line, err := output.ReadString('\n') - if err == nil { - log = append(log, line) - continue - } else if err == io.EOF { - select { - case err := <-c: - if err != nil { - logrus.Error(err) - return call.ReplyErrorOccurred(err.Error()) - } - done = true - default: - if !call.WantsMore() { - break - } - br := iopodman.MoreResponse{ - Logs: log, - } - call.ReplyLoadImage(br) - log = []string{} - } - } else { - return call.ReplyErrorOccurred(err.Error()) - } - if done { - break - } - } - call.Continues = false - - br := iopodman.MoreResponse{ - Logs: log, - Id: names, - } - if deleteInputFile { - if err := os.Remove(inputFile); err != nil { - logrus.Errorf("unable to delete input file %s", inputFile) - } - } - return call.ReplyLoadImage(br) -} - -// Diff ... -func (i *VarlinkAPI) Diff(call iopodman.VarlinkCall, name string) error { - var response []iopodman.DiffInfo - changes, err := i.Runtime.GetDiff("", name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - for _, change := range changes { - response = append(response, iopodman.DiffInfo{Path: change.Path, ChangeType: change.Kind.String()}) - } - return call.ReplyDiff(response) -} - -// GetLayersMapWithImageInfo is a development only endpoint to obtain layer information for an image. -func (i *VarlinkAPI) GetLayersMapWithImageInfo(call iopodman.VarlinkCall) error { - layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime()) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - b, err := json.Marshal(layerInfo) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyGetLayersMapWithImageInfo(string(b)) -} - -// BuildImageHierarchyMap ... -func (i *VarlinkAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name string) error { - img, err := i.Runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - imageInfo := &image.InfoImage{ - ID: img.ID(), - Tags: img.Names(), - } - layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime()) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if err := image.BuildImageHierarchyMap(imageInfo, layerInfo, img.TopLayer()); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - b, err := json.Marshal(imageInfo) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyBuildImageHierarchyMap(string(b)) -} - -// ImageTree returns the image tree string for the provided image name or ID -func (i *VarlinkAPI) ImageTree(call iopodman.VarlinkCall, nameOrID string, whatRequires bool) error { - img, err := i.Runtime.ImageRuntime().NewFromLocal(nameOrID) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - tree, err := img.GenerateTree(whatRequires) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyImageTree(tree) -} diff --git a/pkg/varlinkapi/intermediate.go b/pkg/varlinkapi/intermediate.go deleted file mode 100644 index f04665a86..000000000 --- a/pkg/varlinkapi/intermediate.go +++ /dev/null @@ -1,289 +0,0 @@ -package varlinkapi - -import ( - "github.com/sirupsen/logrus" -) - -/* -attention - -in this file you will see a lot of struct duplication. this was done because people wanted a strongly typed -varlink mechanism. this resulted in us creating this intermediate layer that allows us to take the input -from the cli and make an intermediate layer which can be transferred as strongly typed structures over a varlink -interface. - -we intentionally avoided heavy use of reflection here because we were concerned about performance impacts to the -non-varlink intermediate layer generation. -*/ - -// GenericCLIResult describes the overall interface for dealing with -// the create command cli in both local and remote uses -type GenericCLIResult interface { - IsSet() bool - Name() string - Value() interface{} -} - -// CRStringSlice describes a string slice cli struct -type CRStringSlice struct { - Val []string - createResult -} - -// CRString describes a string cli struct -type CRString struct { - Val string - createResult -} - -// CRUint64 describes a uint64 cli struct -type CRUint64 struct { - Val uint64 - createResult -} - -// CRFloat64 describes a float64 cli struct -type CRFloat64 struct { - Val float64 - createResult -} - -//CRBool describes a bool cli struct -type CRBool struct { - Val bool - createResult -} - -// CRInt64 describes an int64 cli struct -type CRInt64 struct { - Val int64 - createResult -} - -// CRUint describes a uint cli struct -type CRUint struct { - Val uint - createResult -} - -// CRInt describes an int cli struct -type CRInt struct { - Val int - createResult -} - -// CRStringArray describes a stringarray cli struct -type CRStringArray struct { - Val []string - createResult -} - -type createResult struct { - Flag string - Changed bool -} - -// GenericCLIResults in the intermediate object between the cobra cli -// and createconfig -type GenericCLIResults struct { - results map[string]GenericCLIResult - InputArgs []string -} - -// IsSet returns a bool if the flag was changed -func (f GenericCLIResults) IsSet(flag string) bool { - r := f.findResult(flag) - if r == nil { - return false - } - return r.IsSet() -} - -// Value returns the value of the cli flag -func (f GenericCLIResults) Value(flag string) interface{} { - r := f.findResult(flag) - if r == nil { - return "" - } - return r.Value() -} - -func (f GenericCLIResults) findResult(flag string) GenericCLIResult { - val, ok := f.results[flag] - if ok { - return val - } - logrus.Debugf("unable to find flag %s", flag) - return nil -} - -// Bool is a wrapper to get a bool value from GenericCLIResults -func (f GenericCLIResults) Bool(flag string) bool { - r := f.findResult(flag) - if r == nil { - return false - } - return r.Value().(bool) -} - -// String is a wrapper to get a string value from GenericCLIResults -func (f GenericCLIResults) String(flag string) string { - r := f.findResult(flag) - if r == nil { - return "" - } - return r.Value().(string) -} - -// Uint is a wrapper to get an uint value from GenericCLIResults -func (f GenericCLIResults) Uint(flag string) uint { - r := f.findResult(flag) - if r == nil { - return 0 - } - return r.Value().(uint) -} - -// StringSlice is a wrapper to get a stringslice value from GenericCLIResults -func (f GenericCLIResults) StringSlice(flag string) []string { - r := f.findResult(flag) - if r == nil { - return []string{} - } - return r.Value().([]string) -} - -// StringArray is a wrapper to get a stringslice value from GenericCLIResults -func (f GenericCLIResults) StringArray(flag string) []string { - r := f.findResult(flag) - if r == nil { - return []string{} - } - return r.Value().([]string) -} - -// Uint64 is a wrapper to get an uint64 value from GenericCLIResults -func (f GenericCLIResults) Uint64(flag string) uint64 { - r := f.findResult(flag) - if r == nil { - return 0 - } - return r.Value().(uint64) -} - -// Int64 is a wrapper to get an int64 value from GenericCLIResults -func (f GenericCLIResults) Int64(flag string) int64 { - r := f.findResult(flag) - if r == nil { - return 0 - } - return r.Value().(int64) -} - -// Int is a wrapper to get an int value from GenericCLIResults -func (f GenericCLIResults) Int(flag string) int { - r := f.findResult(flag) - if r == nil { - return 0 - } - return r.Value().(int) -} - -// Float64 is a wrapper to get an float64 value from GenericCLIResults -func (f GenericCLIResults) Float64(flag string) float64 { - r := f.findResult(flag) - if r == nil { - return 0 - } - return r.Value().(float64) -} - -// Float64 is a wrapper to get an float64 value from GenericCLIResults -func (f GenericCLIResults) Changed(flag string) bool { - r := f.findResult(flag) - if r == nil { - return false - } - return r.IsSet() -} - -// IsSet ... -func (c CRStringSlice) IsSet() bool { return c.Changed } - -// Name ... -func (c CRStringSlice) Name() string { return c.Flag } - -// Value ... -func (c CRStringSlice) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRString) IsSet() bool { return c.Changed } - -// Name ... -func (c CRString) Name() string { return c.Flag } - -// Value ... -func (c CRString) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRUint64) IsSet() bool { return c.Changed } - -// Name ... -func (c CRUint64) Name() string { return c.Flag } - -// Value ... -func (c CRUint64) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRFloat64) IsSet() bool { return c.Changed } - -// Name ... -func (c CRFloat64) Name() string { return c.Flag } - -// Value ... -func (c CRFloat64) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRBool) IsSet() bool { return c.Changed } - -// Name ... -func (c CRBool) Name() string { return c.Flag } - -// Value ... -func (c CRBool) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRInt64) IsSet() bool { return c.Changed } - -// Name ... -func (c CRInt64) Name() string { return c.Flag } - -// Value ... -func (c CRInt64) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRUint) IsSet() bool { return c.Changed } - -// Name ... -func (c CRUint) Name() string { return c.Flag } - -// Value ... -func (c CRUint) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRInt) IsSet() bool { return c.Changed } - -// Name ... -func (c CRInt) Name() string { return c.Flag } - -// Value ... -func (c CRInt) Value() interface{} { return c.Val } - -// IsSet ... -func (c CRStringArray) IsSet() bool { return c.Changed } - -// Name ... -func (c CRStringArray) Name() string { return c.Flag } - -// Value ... -func (c CRStringArray) Value() interface{} { return c.Val } diff --git a/pkg/varlinkapi/intermediate_varlink.go b/pkg/varlinkapi/intermediate_varlink.go deleted file mode 100644 index 0d74f1a95..000000000 --- a/pkg/varlinkapi/intermediate_varlink.go +++ /dev/null @@ -1,457 +0,0 @@ -// +build varlink remoteclient - -package varlinkapi - -import ( - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v2/pkg/rootless" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/pkg/errors" -) - -//FIXME these are duplicated here to resolve a circular -//import with cmd/podman/common. -var ( - // DefaultHealthCheckInterval default value - DefaultHealthCheckInterval = "30s" - // DefaultHealthCheckRetries default value - DefaultHealthCheckRetries uint = 3 - // DefaultHealthCheckStartPeriod default value - DefaultHealthCheckStartPeriod = "0s" - // DefaultHealthCheckTimeout default value - DefaultHealthCheckTimeout = "30s" - // DefaultImageVolume default value - DefaultImageVolume = "bind" -) - -// StringSliceToPtr converts a genericcliresult value into a *[]string -func StringSliceToPtr(g GenericCLIResult) *[]string { - if !g.IsSet() { - return nil - } - newT := g.Value().([]string) - return &newT -} - -// StringToPtr converts a genericcliresult value into a *string -func StringToPtr(g GenericCLIResult) *string { - if !g.IsSet() { - return nil - } - newT := g.Value().(string) - return &newT -} - -// BoolToPtr converts a genericcliresult value into a *bool -func BoolToPtr(g GenericCLIResult) *bool { - if !g.IsSet() { - return nil - } - newT := g.Value().(bool) - return &newT -} - -// AnyIntToInt64Ptr converts a genericcliresult value into an *int64 -func AnyIntToInt64Ptr(g GenericCLIResult) *int64 { - if !g.IsSet() { - return nil - } - var newT int64 - switch g.Value().(type) { - case int: - newT = int64(g.Value().(int)) - case int64: - newT = g.Value().(int64) - case uint64: - newT = int64(g.Value().(uint64)) - case uint: - newT = int64(g.Value().(uint)) - default: - panic(errors.Errorf("invalid int type")) - } - return &newT -} - -// Float64ToPtr converts a genericcliresult into a *float64 -func Float64ToPtr(g GenericCLIResult) *float64 { - if !g.IsSet() { - return nil - } - newT := g.Value().(float64) - return &newT -} - -// MakeVarlink creates a varlink transportable struct from GenericCLIResults -func (g GenericCLIResults) MakeVarlink() iopodman.Create { - v := iopodman.Create{ - Args: g.InputArgs, - AddHost: StringSliceToPtr(g.Find("add-host")), - Annotation: StringSliceToPtr(g.Find("annotation")), - Attach: StringSliceToPtr(g.Find("attach")), - BlkioWeight: StringToPtr(g.Find("blkio-weight")), - BlkioWeightDevice: StringSliceToPtr(g.Find("blkio-weight-device")), - CapAdd: StringSliceToPtr(g.Find("cap-add")), - CapDrop: StringSliceToPtr(g.Find("cap-drop")), - CgroupParent: StringToPtr(g.Find("cgroup-parent")), - CidFile: StringToPtr(g.Find("cidfile")), - ConmonPidfile: StringToPtr(g.Find("conmon-pidfile")), - CpuPeriod: AnyIntToInt64Ptr(g.Find("cpu-period")), - CpuQuota: AnyIntToInt64Ptr(g.Find("cpu-quota")), - CpuRtPeriod: AnyIntToInt64Ptr(g.Find("cpu-rt-period")), - CpuRtRuntime: AnyIntToInt64Ptr(g.Find("cpu-rt-runtime")), - CpuShares: AnyIntToInt64Ptr(g.Find("cpu-shares")), - Cpus: Float64ToPtr(g.Find("cpus")), - CpuSetCpus: StringToPtr(g.Find("cpuset-cpus")), - CpuSetMems: StringToPtr(g.Find("cpuset-mems")), - Detach: BoolToPtr(g.Find("detach")), - DetachKeys: StringToPtr(g.Find("detach-keys")), - Device: StringSliceToPtr(g.Find("device")), - DeviceReadBps: StringSliceToPtr(g.Find("device-read-bps")), - DeviceReadIops: StringSliceToPtr(g.Find("device-read-iops")), - DeviceWriteBps: StringSliceToPtr(g.Find("device-write-bps")), - DeviceWriteIops: StringSliceToPtr(g.Find("device-write-iops")), - Dns: StringSliceToPtr(g.Find("dns")), - DnsOpt: StringSliceToPtr(g.Find("dns-opt")), - DnsSearch: StringSliceToPtr(g.Find("dns-search")), - Entrypoint: StringToPtr(g.Find("entrypoint")), - Env: StringSliceToPtr(g.Find("env")), - EnvFile: StringSliceToPtr(g.Find("env-file")), - Expose: StringSliceToPtr(g.Find("expose")), - Gidmap: StringSliceToPtr(g.Find("gidmap")), - Groupadd: StringSliceToPtr(g.Find("group-add")), - HealthcheckCommand: StringToPtr(g.Find("healthcheck-command")), - HealthcheckInterval: StringToPtr(g.Find("healthcheck-interval")), - HealthcheckRetries: AnyIntToInt64Ptr(g.Find("healthcheck-retries")), - HealthcheckStartPeriod: StringToPtr(g.Find("healthcheck-start-period")), - HealthcheckTimeout: StringToPtr(g.Find("healthcheck-timeout")), - Hostname: StringToPtr(g.Find("hostname")), - ImageVolume: StringToPtr(g.Find("image-volume")), - Init: BoolToPtr(g.Find("init")), - InitPath: StringToPtr(g.Find("init-path")), - Interactive: BoolToPtr(g.Find("interactive")), - Ip: StringToPtr(g.Find("ip")), - Ipc: StringToPtr(g.Find("ipc")), - KernelMemory: StringToPtr(g.Find("kernel-memory")), - Label: StringSliceToPtr(g.Find("label")), - LabelFile: StringSliceToPtr(g.Find("label-file")), - LogDriver: StringToPtr(g.Find("log-driver")), - LogOpt: StringSliceToPtr(g.Find("log-opt")), - MacAddress: StringToPtr(g.Find("mac-address")), - Memory: StringToPtr(g.Find("memory")), - MemoryReservation: StringToPtr(g.Find("memory-reservation")), - MemorySwap: StringToPtr(g.Find("memory-swap")), - MemorySwappiness: AnyIntToInt64Ptr(g.Find("memory-swappiness")), - Name: StringToPtr(g.Find("name")), - Network: StringToPtr(g.Find("network")), - OomKillDisable: BoolToPtr(g.Find("oom-kill-disable")), - OomScoreAdj: AnyIntToInt64Ptr(g.Find("oom-score-adj")), - OverrideOS: StringToPtr(g.Find("override-os")), - OverrideArch: StringToPtr(g.Find("override-arch")), - Pid: StringToPtr(g.Find("pid")), - PidsLimit: AnyIntToInt64Ptr(g.Find("pids-limit")), - Pod: StringToPtr(g.Find("pod")), - Privileged: BoolToPtr(g.Find("privileged")), - Publish: StringSliceToPtr(g.Find("publish")), - PublishAll: BoolToPtr(g.Find("publish-all")), - Pull: StringToPtr(g.Find("pull")), - Quiet: BoolToPtr(g.Find("quiet")), - Readonly: BoolToPtr(g.Find("read-only")), - Readonlytmpfs: BoolToPtr(g.Find("read-only-tmpfs")), - Restart: StringToPtr(g.Find("restart")), - Rm: BoolToPtr(g.Find("rm")), - Rootfs: BoolToPtr(g.Find("rootfs")), - SecurityOpt: StringSliceToPtr(g.Find("security-opt")), - ShmSize: StringToPtr(g.Find("shm-size")), - StopSignal: StringToPtr(g.Find("stop-signal")), - StopTimeout: AnyIntToInt64Ptr(g.Find("stop-timeout")), - StorageOpt: StringSliceToPtr(g.Find("storage-opt")), - Subuidname: StringToPtr(g.Find("subuidname")), - Subgidname: StringToPtr(g.Find("subgidname")), - Sysctl: StringSliceToPtr(g.Find("sysctl")), - Systemd: StringToPtr(g.Find("systemd")), - Tmpfs: StringSliceToPtr(g.Find("tmpfs")), - Tty: BoolToPtr(g.Find("tty")), - Uidmap: StringSliceToPtr(g.Find("uidmap")), - Ulimit: StringSliceToPtr(g.Find("ulimit")), - User: StringToPtr(g.Find("user")), - Userns: StringToPtr(g.Find("userns")), - Uts: StringToPtr(g.Find("uts")), - Mount: StringSliceToPtr(g.Find("mount")), - Volume: StringSliceToPtr(g.Find("volume")), - VolumesFrom: StringSliceToPtr(g.Find("volumes-from")), - WorkDir: StringToPtr(g.Find("workdir")), - } - - return v -} - -func stringSliceFromVarlink(v *[]string, flagName string, defaultValue *[]string) CRStringSlice { - cr := CRStringSlice{} - if v == nil { - cr.Val = []string{} - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = *v - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func stringFromVarlink(v *string, flagName string, defaultValue *string) CRString { - cr := CRString{} - if v == nil { - cr.Val = "" - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = *v - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func boolFromVarlink(v *bool, flagName string, defaultValue bool) CRBool { - cr := CRBool{} - if v == nil { - // In case a cli bool default value is true - cr.Val = defaultValue - cr.Changed = false - } else { - cr.Val = *v - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func uint64FromVarlink(v *int64, flagName string, defaultValue *uint64) CRUint64 { - cr := CRUint64{} - if v == nil { - cr.Val = 0 - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = uint64(*v) - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func int64FromVarlink(v *int64, flagName string, defaultValue *int64) CRInt64 { - cr := CRInt64{} - if v == nil { - cr.Val = 0 - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = *v - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func float64FromVarlink(v *float64, flagName string, defaultValue *float64) CRFloat64 { - cr := CRFloat64{} - if v == nil { - cr.Val = 0 - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = *v - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func uintFromVarlink(v *int64, flagName string, defaultValue *uint) CRUint { - cr := CRUint{} - if v == nil { - cr.Val = 0 - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = uint(*v) - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func stringArrayFromVarlink(v *[]string, flagName string, defaultValue *[]string) CRStringArray { - cr := CRStringArray{} - if v == nil { - cr.Val = []string{} - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Changed = false - } else { - cr.Val = *v - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -func intFromVarlink(v *int64, flagName string, defaultValue *int) CRInt { - cr := CRInt{} - if v == nil { - if defaultValue != nil { - cr.Val = *defaultValue - } - cr.Val = 0 - cr.Changed = false - } else { - cr.Val = int(*v) - cr.Changed = true - } - cr.Flag = flagName - return cr -} - -// VarlinkCreateToGeneric creates a GenericCLIResults from the varlink create -// structure. -func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults { - // FIXME this will need to be fixed!!!!! With containers conf - //containerConfig := cliconfig.GetDefaultConfig() - // TODO | WARN - // We do not get a default network over varlink. Unlike the other default values for some cli - // elements, it seems it gets set to the default anyway. - - var memSwapDefault int64 = -1 - netModeDefault := "bridge" - systemdDefault := "true" - if rootless.IsRootless() { - netModeDefault = "slirp4netns" - } - - shmSize := config.DefaultShmSize - - m := make(map[string]GenericCLIResult) - m["add-host"] = stringSliceFromVarlink(opts.AddHost, "add-host", nil) - m["annotation"] = stringSliceFromVarlink(opts.Annotation, "annotation", nil) - m["attach"] = stringSliceFromVarlink(opts.Attach, "attach", nil) - m["blkio-weight"] = stringFromVarlink(opts.BlkioWeight, "blkio-weight", nil) - m["blkio-weight-device"] = stringSliceFromVarlink(opts.BlkioWeightDevice, "blkio-weight-device", nil) - m["cap-add"] = stringSliceFromVarlink(opts.CapAdd, "cap-add", nil) - m["cap-drop"] = stringSliceFromVarlink(opts.CapDrop, "cap-drop", nil) - m["cgroup-parent"] = stringFromVarlink(opts.CgroupParent, "cgroup-parent", nil) - m["cidfile"] = stringFromVarlink(opts.CidFile, "cidfile", nil) - m["conmon-pidfile"] = stringFromVarlink(opts.ConmonPidfile, "conmon-file", nil) - m["cpu-period"] = uint64FromVarlink(opts.CpuPeriod, "cpu-period", nil) - m["cpu-quota"] = int64FromVarlink(opts.CpuQuota, "quota", nil) - m["cpu-rt-period"] = uint64FromVarlink(opts.CpuRtPeriod, "cpu-rt-period", nil) - m["cpu-rt-runtime"] = int64FromVarlink(opts.CpuRtRuntime, "cpu-rt-quota", nil) - m["cpu-shares"] = uint64FromVarlink(opts.CpuShares, "cpu-shares", nil) - m["cpus"] = float64FromVarlink(opts.Cpus, "cpus", nil) - m["cpuset-cpus"] = stringFromVarlink(opts.CpuSetCpus, "cpuset-cpus", nil) - m["cpuset-mems"] = stringFromVarlink(opts.CpuSetMems, "cpuset-mems", nil) - m["detach"] = boolFromVarlink(opts.Detach, "detach", false) - m["detach-keys"] = stringFromVarlink(opts.DetachKeys, "detach-keys", nil) - m["device"] = stringSliceFromVarlink(opts.Device, "device", nil) - m["device-read-bps"] = stringSliceFromVarlink(opts.DeviceReadBps, "device-read-bps", nil) - m["device-read-iops"] = stringSliceFromVarlink(opts.DeviceReadIops, "device-read-iops", nil) - m["device-write-bps"] = stringSliceFromVarlink(opts.DeviceWriteBps, "write-device-bps", nil) - m["device-write-iops"] = stringSliceFromVarlink(opts.DeviceWriteIops, "write-device-iops", nil) - m["dns"] = stringSliceFromVarlink(opts.Dns, "dns", nil) - m["dns-opt"] = stringSliceFromVarlink(opts.DnsOpt, "dns-opt", nil) - m["dns-search"] = stringSliceFromVarlink(opts.DnsSearch, "dns-search", nil) - m["entrypoint"] = stringFromVarlink(opts.Entrypoint, "entrypoint", nil) - m["env"] = stringArrayFromVarlink(opts.Env, "env", nil) - m["env-file"] = stringSliceFromVarlink(opts.EnvFile, "env-file", nil) - m["expose"] = stringSliceFromVarlink(opts.Expose, "expose", nil) - m["gidmap"] = stringSliceFromVarlink(opts.Gidmap, "gidmap", nil) - m["group-add"] = stringSliceFromVarlink(opts.Groupadd, "group-add", nil) - m["healthcheck-command"] = stringFromVarlink(opts.HealthcheckCommand, "healthcheck-command", nil) - m["healthcheck-interval"] = stringFromVarlink(opts.HealthcheckInterval, "healthcheck-interval", &DefaultHealthCheckInterval) - m["healthcheck-retries"] = uintFromVarlink(opts.HealthcheckRetries, "healthcheck-retries", &DefaultHealthCheckRetries) - m["healthcheck-start-period"] = stringFromVarlink(opts.HealthcheckStartPeriod, "healthcheck-start-period", &DefaultHealthCheckStartPeriod) - m["healthcheck-timeout"] = stringFromVarlink(opts.HealthcheckTimeout, "healthcheck-timeout", &DefaultHealthCheckTimeout) - m["hostname"] = stringFromVarlink(opts.Hostname, "hostname", nil) - m["image-volume"] = stringFromVarlink(opts.ImageVolume, "image-volume", &DefaultImageVolume) - m["init"] = boolFromVarlink(opts.Init, "init", false) - m["init-path"] = stringFromVarlink(opts.InitPath, "init-path", nil) - m["interactive"] = boolFromVarlink(opts.Interactive, "interactive", false) - m["ip"] = stringFromVarlink(opts.Ip, "ip", nil) - m["ipc"] = stringFromVarlink(opts.Ipc, "ipc", nil) - m["kernel-memory"] = stringFromVarlink(opts.KernelMemory, "kernel-memory", nil) - m["label"] = stringArrayFromVarlink(opts.Label, "label", nil) - m["label-file"] = stringSliceFromVarlink(opts.LabelFile, "label-file", nil) - m["log-driver"] = stringFromVarlink(opts.LogDriver, "log-driver", nil) - m["log-opt"] = stringSliceFromVarlink(opts.LogOpt, "log-opt", nil) - m["mac-address"] = stringFromVarlink(opts.MacAddress, "mac-address", nil) - m["memory"] = stringFromVarlink(opts.Memory, "memory", nil) - m["memory-reservation"] = stringFromVarlink(opts.MemoryReservation, "memory-reservation", nil) - m["memory-swap"] = stringFromVarlink(opts.MemorySwap, "memory-swap", nil) - m["memory-swappiness"] = int64FromVarlink(opts.MemorySwappiness, "memory-swappiness", &memSwapDefault) - m["name"] = stringFromVarlink(opts.Name, "name", nil) - m["network"] = stringFromVarlink(opts.Network, "network", &netModeDefault) - m["no-hosts"] = boolFromVarlink(opts.NoHosts, "no-hosts", false) - m["oom-kill-disable"] = boolFromVarlink(opts.OomKillDisable, "oon-kill-disable", false) - m["oom-score-adj"] = intFromVarlink(opts.OomScoreAdj, "oom-score-adj", nil) - m["override-os"] = stringFromVarlink(opts.OverrideOS, "override-os", nil) - m["override-arch"] = stringFromVarlink(opts.OverrideArch, "override-arch", nil) - m["pid"] = stringFromVarlink(opts.Pid, "pid", nil) - m["pids-limit"] = int64FromVarlink(opts.PidsLimit, "pids-limit", nil) - m["pod"] = stringFromVarlink(opts.Pod, "pod", nil) - m["privileged"] = boolFromVarlink(opts.Privileged, "privileged", false) - m["publish"] = stringSliceFromVarlink(opts.Publish, "publish", nil) - m["publish-all"] = boolFromVarlink(opts.PublishAll, "publish-all", false) - m["pull"] = stringFromVarlink(opts.Pull, "missing", nil) - m["quiet"] = boolFromVarlink(opts.Quiet, "quiet", false) - m["read-only"] = boolFromVarlink(opts.Readonly, "read-only", false) - m["read-only-tmpfs"] = boolFromVarlink(opts.Readonlytmpfs, "read-only-tmpfs", true) - m["restart"] = stringFromVarlink(opts.Restart, "restart", nil) - m["rm"] = boolFromVarlink(opts.Rm, "rm", false) - m["rootfs"] = boolFromVarlink(opts.Rootfs, "rootfs", false) - m["security-opt"] = stringArrayFromVarlink(opts.SecurityOpt, "security-opt", nil) - m["shm-size"] = stringFromVarlink(opts.ShmSize, "shm-size", &shmSize) - m["stop-signal"] = stringFromVarlink(opts.StopSignal, "stop-signal", nil) - m["stop-timeout"] = uintFromVarlink(opts.StopTimeout, "stop-timeout", nil) - m["storage-opt"] = stringSliceFromVarlink(opts.StorageOpt, "storage-opt", nil) - m["subgidname"] = stringFromVarlink(opts.Subgidname, "subgidname", nil) - m["subuidname"] = stringFromVarlink(opts.Subuidname, "subuidname", nil) - m["sysctl"] = stringSliceFromVarlink(opts.Sysctl, "sysctl", nil) - m["systemd"] = stringFromVarlink(opts.Systemd, "systemd", &systemdDefault) - m["tmpfs"] = stringSliceFromVarlink(opts.Tmpfs, "tmpfs", nil) - m["tty"] = boolFromVarlink(opts.Tty, "tty", false) - m["uidmap"] = stringSliceFromVarlink(opts.Uidmap, "uidmap", nil) - m["ulimit"] = stringSliceFromVarlink(opts.Ulimit, "ulimit", nil) - m["user"] = stringFromVarlink(opts.User, "user", nil) - m["userns"] = stringFromVarlink(opts.Userns, "userns", nil) - m["uts"] = stringFromVarlink(opts.Uts, "uts", nil) - m["mount"] = stringArrayFromVarlink(opts.Mount, "mount", nil) - m["volume"] = stringArrayFromVarlink(opts.Volume, "volume", nil) - m["volumes-from"] = stringSliceFromVarlink(opts.VolumesFrom, "volumes-from", nil) - m["workdir"] = stringFromVarlink(opts.WorkDir, "workdir", nil) - - gcli := GenericCLIResults{m, opts.Args} - return gcli -} - -// Find returns a flag from a GenericCLIResults by name -func (g GenericCLIResults) Find(name string) GenericCLIResult { - result, ok := g.results[name] - if ok { - return result - } - panic(errors.Errorf("unable to find generic flag for varlink %s", name)) -} diff --git a/pkg/varlinkapi/mount.go b/pkg/varlinkapi/mount.go deleted file mode 100644 index 6fd4de709..000000000 --- a/pkg/varlinkapi/mount.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build varlink - -package varlinkapi - -import iopodman "github.com/containers/podman/v2/pkg/varlink" - -// ListContainerMounts ... -func (i *VarlinkAPI) ListContainerMounts(call iopodman.VarlinkCall) error { - mounts := make(map[string]string) - allContainers, err := i.Runtime.GetAllContainers() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - for _, container := range allContainers { - mounted, mountPoint, err := container.Mounted() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if mounted { - mounts[container.ID()] = mountPoint - } - } - return call.ReplyListContainerMounts(mounts) -} - -// MountContainer ... -func (i *VarlinkAPI) MountContainer(call iopodman.VarlinkCall, name string) error { - container, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - path, err := container.Mount() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyMountContainer(path) -} - -// UnmountContainer ... -func (i *VarlinkAPI) UnmountContainer(call iopodman.VarlinkCall, name string, force bool) error { - container, err := i.Runtime.LookupContainer(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if err := container.Unmount(force); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyUnmountContainer() -} diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go deleted file mode 100644 index 6d03afb7a..000000000 --- a/pkg/varlinkapi/pods.go +++ /dev/null @@ -1,389 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "syscall" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/cri-o/ocicni/pkg/ocicni" - "github.com/docker/go-connections/nat" - "github.com/pkg/errors" -) - -// CreatePod ... -func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error { - var options []libpod.PodCreateOption - if create.Infra { - options = append(options, libpod.WithInfraContainer()) - nsOptions, err := GetNamespaceOptions(create.Share) - if err != nil { - return err - } - options = append(options, nsOptions...) - } - if create.CgroupParent != "" { - options = append(options, libpod.WithPodCgroupParent(create.CgroupParent)) - } - if len(create.Labels) > 0 { - options = append(options, libpod.WithPodLabels(create.Labels)) - } - if create.Name != "" { - options = append(options, libpod.WithPodName(create.Name)) - } - if len(create.Share) > 0 && !create.Infra { - return call.ReplyErrorOccurred("You cannot share kernel namespaces on the pod level without an infra container") - } - if len(create.Share) == 0 && create.Infra { - return call.ReplyErrorOccurred("You must share kernel namespaces to run an infra container") - } - - if len(create.Publish) > 0 { - if !create.Infra { - return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host") - } - portBindings, err := CreatePortBindings(create.Publish) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - options = append(options, libpod.WithInfraContainerPorts(portBindings)) - - } - options = append(options, libpod.WithPodCgroups()) - - pod, err := i.Runtime.NewPod(getContext(), options...) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyCreatePod(pod.ID()) -} - -// ListPods ... -func (i *VarlinkAPI) ListPods(call iopodman.VarlinkCall) error { - var ( - listPods []iopodman.ListPodData - ) - - pods, err := i.Runtime.GetAllPods() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - opts := PsOptions{} - for _, pod := range pods { - listPod, err := makeListPod(pod, opts) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - listPods = append(listPods, listPod) - } - return call.ReplyListPods(listPods) -} - -// GetPod ... -func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - opts := PsOptions{} - - listPod, err := makeListPod(pod, opts) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - return call.ReplyGetPod(listPod) -} - -// GetPodsByStatus returns a slice of pods filtered by a libpod status -func (i *VarlinkAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error { - filterFuncs := func(p *libpod.Pod) bool { - state, _ := p.GetPodStatus() - for _, status := range statuses { - if state == status { - return true - } - } - return false - } - filteredPods, err := i.Runtime.Pods(filterFuncs) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - podIDs := make([]string, 0, len(filteredPods)) - for _, p := range filteredPods { - podIDs = append(podIDs, p.ID()) - } - return call.ReplyGetPodsByStatus(podIDs) -} - -// InspectPod ... -func (i *VarlinkAPI) InspectPod(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - inspectData, err := pod.Inspect() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - b, err := json.Marshal(&inspectData) - if err != nil { - return call.ReplyErrorOccurred("unable to serialize") - } - return call.ReplyInspectPod(string(b)) -} - -// StartPod ... -func (i *VarlinkAPI) StartPod(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - ctnrs, err := pod.AllContainers() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if 0 == len(ctnrs) { - return call.ReplyNoContainersInPod(name) - } - ctrErrs, err := pod.Start(getContext()) - callErr := handlePodCall(call, pod, ctrErrs, err) - if callErr != nil { - return err - } - return call.ReplyStartPod(pod.ID()) -} - -// StopPod ... -func (i *VarlinkAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - ctrErrs, err := pod.StopWithTimeout(getContext(), true, int(timeout)) - callErr := handlePodCall(call, pod, ctrErrs, err) - if callErr != nil { - return err - } - return call.ReplyStopPod(pod.ID()) -} - -// RestartPod ... -func (i *VarlinkAPI) RestartPod(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - ctnrs, err := pod.AllContainers() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if 0 == len(ctnrs) { - return call.ReplyNoContainersInPod(name) - } - ctrErrs, err := pod.Restart(getContext()) - callErr := handlePodCall(call, pod, ctrErrs, err) - if callErr != nil { - return err - } - return call.ReplyRestartPod(pod.ID()) -} - -// KillPod kills the running containers in a pod. If you want to use the default SIGTERM signal, -// just send a -1 for the signal arg. -func (i *VarlinkAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64) error { - killSignal := uint(syscall.SIGTERM) - if signal != -1 { - killSignal = uint(signal) - } - - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - ctrErrs, err := pod.Kill(context.TODO(), killSignal) - callErr := handlePodCall(call, pod, ctrErrs, err) - if callErr != nil { - return err - } - return call.ReplyKillPod(pod.ID()) -} - -// PausePod ... -func (i *VarlinkAPI) PausePod(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - ctrErrs, err := pod.Pause(context.TODO()) - callErr := handlePodCall(call, pod, ctrErrs, err) - if callErr != nil { - return err - } - return call.ReplyPausePod(pod.ID()) -} - -// UnpausePod ... -func (i *VarlinkAPI) UnpausePod(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - ctrErrs, err := pod.Unpause(context.TODO()) - callErr := handlePodCall(call, pod, ctrErrs, err) - if callErr != nil { - return err - } - return call.ReplyUnpausePod(pod.ID()) -} - -// RemovePod ... -func (i *VarlinkAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool) error { - ctx := getContext() - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - if err = i.Runtime.RemovePod(ctx, pod, true, force); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - return call.ReplyRemovePod(pod.ID()) -} - -// GetPodStats ... -func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - prevStats := make(map[string]*define.ContainerStats) - podStats, err := pod.GetPodStats(prevStats) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - if len(podStats) == 0 { - return call.ReplyNoContainerRunning() - } - containersStats := make([]iopodman.ContainerStats, 0) - for ctrID, containerStats := range podStats { - cs := iopodman.ContainerStats{ - Id: ctrID, - Name: containerStats.Name, - Cpu: containerStats.CPU, - Cpu_nano: int64(containerStats.CPUNano), - System_nano: int64(containerStats.SystemNano), - Mem_usage: int64(containerStats.MemUsage), - Mem_limit: int64(containerStats.MemLimit), - Mem_perc: containerStats.MemPerc, - Net_input: int64(containerStats.NetInput), - Net_output: int64(containerStats.NetOutput), - Block_input: int64(containerStats.BlockInput), - Block_output: int64(containerStats.BlockOutput), - Pids: int64(containerStats.PIDs), - } - containersStats = append(containersStats, cs) - } - return call.ReplyGetPodStats(pod.ID(), containersStats) -} - -// getPodsByContext returns a slice of pod ids based on all, latest, or a list -func (i *VarlinkAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { - var podids []string - - pods, err := getPodsByContext(all, latest, input, i.Runtime) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - for _, p := range pods { - podids = append(podids, p.ID()) - } - return call.ReplyGetPodsByContext(podids) -} - -// PodStateData returns a container's state data in string format -func (i *VarlinkAPI) PodStateData(call iopodman.VarlinkCall, name string) error { - pod, err := i.Runtime.LookupPod(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - data, err := pod.Inspect() - if err != nil { - return call.ReplyErrorOccurred("unable to obtain pod state") - } - b, err := json.Marshal(data) - if err != nil { - return call.ReplyErrorOccurred("unable to serialize pod inspect data") - } - return call.ReplyPodStateData(string(b)) -} - -// TopPod provides the top stats for a given or latest pod -func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool, descriptors []string) error { - var ( - pod *libpod.Pod - err error - ) - if latest { - name = "latest" - pod, err = i.Runtime.GetLatestPod() - } else { - pod, err = i.Runtime.LookupPod(name) - } - if err != nil { - return call.ReplyPodNotFound(name, err.Error()) - } - - podStatus, err := pod.GetPodStatus() - if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to get status for pod %s", pod.ID())) - } - if podStatus != "Running" { - return call.ReplyErrorOccurred("pod top can only be used on pods with at least one running container") - } - reply, err := pod.GetPodPidInformation(descriptors) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyTopPod(reply) -} - -// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands -func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) { - var portBindings []ocicni.PortMapping - // The conversion from []string to natBindings is temporary while mheon reworks the port - // deduplication code. Eventually that step will not be required. - _, natBindings, err := nat.ParsePortSpecs(ports) - if err != nil { - return nil, err - } - for containerPb, hostPb := range natBindings { - var pm ocicni.PortMapping - pm.ContainerPort = int32(containerPb.Int()) - for _, i := range hostPb { - var hostPort int - var err error - pm.HostIP = i.HostIP - if i.HostPort == "" { - hostPort = containerPb.Int() - } else { - hostPort, err = strconv.Atoi(i.HostPort) - if err != nil { - return nil, errors.Wrapf(err, "unable to convert host port to integer") - } - } - - pm.HostPort = int32(hostPort) - pm.Protocol = containerPb.Proto() - portBindings = append(portBindings, pm) - } - } - return portBindings, nil -} diff --git a/pkg/varlinkapi/remote_client.go b/pkg/varlinkapi/remote_client.go deleted file mode 100644 index 88c11c126..000000000 --- a/pkg/varlinkapi/remote_client.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build varlink remoteclient - -package varlinkapi - -import ( - "github.com/containers/podman/v2/libpod/define" - iopodman "github.com/containers/podman/v2/pkg/varlink" -) - -// ContainerStatsToLibpodContainerStats converts the varlink containerstats to a libpod -// container stats -func ContainerStatsToLibpodContainerStats(stats iopodman.ContainerStats) define.ContainerStats { - cstats := define.ContainerStats{ - ContainerID: stats.Id, - Name: stats.Name, - CPU: stats.Cpu, - CPUNano: uint64(stats.Cpu_nano), - SystemNano: uint64(stats.System_nano), - MemUsage: uint64(stats.Mem_usage), - MemLimit: uint64(stats.Mem_limit), - MemPerc: stats.Mem_perc, - NetInput: uint64(stats.Net_input), - NetOutput: uint64(stats.Net_output), - BlockInput: uint64(stats.Block_input), - BlockOutput: uint64(stats.Block_output), - PIDs: uint64(stats.Pids), - } - return cstats -} diff --git a/pkg/varlinkapi/shortcuts.go b/pkg/varlinkapi/shortcuts.go deleted file mode 100644 index bfb05c0e3..000000000 --- a/pkg/varlinkapi/shortcuts.go +++ /dev/null @@ -1,66 +0,0 @@ -package varlinkapi - -import ( - "github.com/containers/podman/v2/libpod" - "github.com/sirupsen/logrus" -) - -// getPodsByContext returns a slice of pods. Note that all, latest and pods are -// mutually exclusive arguments. -func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) { - var outpods []*libpod.Pod - if all { - return runtime.GetAllPods() - } - if latest { - p, err := runtime.GetLatestPod() - if err != nil { - return nil, err - } - outpods = append(outpods, p) - return outpods, nil - } - var err error - for _, p := range pods { - pod, e := runtime.LookupPod(p) - if e != nil { - // Log all errors here, so callers don't need to. - logrus.Debugf("Error looking up pod %q: %v", p, e) - if err == nil { - err = e - } - } else { - outpods = append(outpods, pod) - } - } - return outpods, err -} - -// getContainersByContext gets pods whether all, latest, or a slice of names/ids -// is specified. -func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { - var ctr *libpod.Container - ctrs = []*libpod.Container{} - - switch { - case all: - ctrs, err = runtime.GetAllContainers() - case latest: - ctr, err = runtime.GetLatestContainer() - ctrs = append(ctrs, ctr) - default: - for _, n := range names { - ctr, e := runtime.LookupContainer(n) - if e != nil { - // Log all errors here, so callers don't need to. - logrus.Debugf("Error looking up container %q: %v", n, e) - if err == nil { - err = e - } - } else { - ctrs = append(ctrs, ctr) - } - } - } - return -} diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go deleted file mode 100644 index e5c766a6d..000000000 --- a/pkg/varlinkapi/system.go +++ /dev/null @@ -1,129 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "context" - "fmt" - "os" - goruntime "runtime" - "strconv" - "time" - - "github.com/containers/image/v5/pkg/sysregistriesv2" - "github.com/containers/podman/v2/libpod/define" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/sirupsen/logrus" -) - -// GetVersion ... -func (i *VarlinkAPI) GetVersion(call iopodman.VarlinkCall) error { - versionInfo, err := define.GetVersion() - if err != nil { - return err - } - - int64APIVersion, err := strconv.ParseInt(versionInfo.APIVersion, 10, 64) - if err != nil { - return err - } - - return call.ReplyGetVersion( - versionInfo.Version, - versionInfo.GoVersion, - versionInfo.GitCommit, - time.Unix(versionInfo.Built, 0).Format(time.RFC3339), - versionInfo.OsArch, - int64APIVersion, - ) -} - -// GetInfo returns details about the podman host and its stores -func (i *VarlinkAPI) GetInfo(call iopodman.VarlinkCall) error { - versionInfo, err := define.GetVersion() - if err != nil { - return err - } - podmanInfo := iopodman.PodmanInfo{} - info, err := i.Runtime.Info() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - distribution := iopodman.InfoDistribution{ - Distribution: info.Host.Distribution.Distribution, - Version: info.Host.Distribution.Version, - } - infoHost := iopodman.InfoHost{ - Buildah_version: info.Host.BuildahVersion, - Distribution: distribution, - Mem_free: info.Host.MemFree, - Mem_total: info.Host.MemTotal, - Swap_free: info.Host.SwapFree, - Swap_total: info.Host.SwapTotal, - Arch: info.Host.Arch, - Cpus: int64(info.Host.CPUs), - Hostname: info.Host.Hostname, - Kernel: info.Host.Kernel, - Os: info.Host.OS, - Uptime: info.Host.Uptime, - Eventlogger: info.Host.EventLogger, - } - podmanInfo.Host = infoHost - pmaninfo := iopodman.InfoPodmanBinary{ - Compiler: goruntime.Compiler, - Go_version: goruntime.Version(), - Podman_version: versionInfo.Version, - Git_commit: versionInfo.GitCommit, - } - - graphStatus := iopodman.InfoGraphStatus{ - Backing_filesystem: info.Store.GraphStatus["Backing Filesystem"], - Native_overlay_diff: info.Store.GraphStatus["Native Overlay Diff"], - Supports_d_type: info.Store.GraphStatus["Supports d_type"], - } - infoStore := iopodman.InfoStore{ - Graph_driver_name: info.Store.GraphDriverName, - Containers: int64(info.Store.ContainerStore.Number), - Images: int64(info.Store.ImageStore.Number), - Run_root: info.Store.RunRoot, - Graph_root: info.Store.GraphRoot, - Graph_driver_options: fmt.Sprintf("%v", info.Store.GraphOptions), - Graph_status: graphStatus, - } - - // Registry information if any is stored as the second list item - for key, val := range info.Registries { - if key == "search" { - podmanInfo.Registries.Search = val.([]string) - continue - } - regData := val.(sysregistriesv2.Registry) - if regData.Insecure { - podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key) - } - if regData.Blocked { - podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key) - } - } - podmanInfo.Store = infoStore - podmanInfo.Podman = pmaninfo - return call.ReplyGetInfo(podmanInfo) -} - -// GetVersion ... -func (i *VarlinkAPI) Reset(call iopodman.VarlinkCall) error { - if err := i.Runtime.Reset(context.TODO()); err != nil { - logrus.Errorf("Reset Failed: %v", err) - if err := call.ReplyErrorOccurred(err.Error()); err != nil { - logrus.Errorf("Failed to send ReplyErrorOccurred: %v", err) - } - os.Exit(define.ExecErrorCodeGeneric) - } - if err := call.ReplyReset(); err != nil { - logrus.Errorf("Failed to send ReplyReset: %v", err) - os.Exit(define.ExecErrorCodeGeneric) - } - os.Exit(0) - return nil -} diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go deleted file mode 100644 index a4550a417..000000000 --- a/pkg/varlinkapi/transfers.go +++ /dev/null @@ -1,80 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "bufio" - "io" - "io/ioutil" - "os" - - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/sirupsen/logrus" -) - -// SendFile allows a client to send a file to the varlink server -func (i *VarlinkAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int64) error { - if !call.WantsUpgrade() { - return call.ReplyErrorOccurred("client must use upgraded connection to send files") - } - - outputFile, err := ioutil.TempFile("", "varlink_send") - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - defer outputFile.Close() - - if err = call.ReplySendFile(outputFile.Name()); err != nil { - // If an error occurs while sending the reply, return the error - return err - } - - writer := bufio.NewWriter(outputFile) - defer writer.Flush() - - reader := call.Call.Reader - if _, err := io.CopyN(writer, reader, length); err != nil { - return err - } - - logrus.Debugf("successfully received %s", outputFile.Name()) - // Send an ACK to the client - call.Call.Writer.WriteString(outputFile.Name() + ":") - call.Call.Writer.Flush() - return nil - -} - -// ReceiveFile allows the varlink server to send a file to a client -func (i *VarlinkAPI) ReceiveFile(call iopodman.VarlinkCall, filepath string, delete bool) error { - if !call.WantsUpgrade() { - return call.ReplyErrorOccurred("client must use upgraded connection to send files") - } - fs, err := os.Open(filepath) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - fileInfo, err := fs.Stat() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - - // Send the file length down to client - // Varlink connection upgraded - if err = call.ReplyReceiveFile(fileInfo.Size()); err != nil { - // If an error occurs while sending the reply, return the error - return err - } - - reader := bufio.NewReader(fs) - _, err = reader.WriteTo(call.Writer) - if err != nil { - return err - } - if delete { - if err := os.Remove(filepath); err != nil { - return err - } - } - return call.Writer.Flush() -} diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go deleted file mode 100644 index 7f96965f0..000000000 --- a/pkg/varlinkapi/util.go +++ /dev/null @@ -1,237 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "context" - "strconv" - "strings" - "time" - - "github.com/containers/buildah" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/channel" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/containers/storage/pkg/archive" -) - -// getContext returns a non-nil, empty context -func getContext() context.Context { - return context.TODO() -} - -func makeListContainer(containerID string, batchInfo BatchContainerStruct) iopodman.Container { - var ( - mounts []iopodman.ContainerMount - ports []iopodman.ContainerPortMappings - ) - ns := GetNamespaces(batchInfo.Pid) - - for _, mount := range batchInfo.ConConfig.Spec.Mounts { - m := iopodman.ContainerMount{ - Destination: mount.Destination, - Type: mount.Type, - Source: mount.Source, - Options: mount.Options, - } - mounts = append(mounts, m) - } - - for _, pm := range batchInfo.ConConfig.PortMappings { - p := iopodman.ContainerPortMappings{ - Host_port: strconv.Itoa(int(pm.HostPort)), - Host_ip: pm.HostIP, - Protocol: pm.Protocol, - Container_port: strconv.Itoa(int(pm.ContainerPort)), - } - ports = append(ports, p) - - } - - // If we find this needs to be done for other container endpoints, we should - // convert this to a separate function or a generic map from struct function. - namespace := iopodman.ContainerNameSpace{ - User: ns.User, - Uts: ns.UTS, - Pidns: ns.PIDNS, - Pid: ns.PID, - Cgroup: ns.Cgroup, - Net: ns.NET, - Mnt: ns.MNT, - Ipc: ns.IPC, - } - - lc := iopodman.Container{ - Id: containerID, - Image: batchInfo.ConConfig.RootfsImageName, - Imageid: batchInfo.ConConfig.RootfsImageID, - Command: batchInfo.ConConfig.Spec.Process.Args, - Createdat: batchInfo.ConConfig.CreatedTime.Format(time.RFC3339), - Runningfor: time.Since(batchInfo.ConConfig.CreatedTime).String(), - Status: batchInfo.ConState.String(), - Ports: ports, - Names: batchInfo.ConConfig.Name, - Labels: batchInfo.ConConfig.Labels, - Mounts: mounts, - Containerrunning: batchInfo.ConState == define.ContainerStateRunning, - Namespaces: namespace, - } - if batchInfo.Size != nil { - lc.Rootfssize = batchInfo.Size.RootFsSize - lc.Rwsize = batchInfo.Size.RwSize - } - return lc -} - -func makeListPodContainers(containerID string, batchInfo BatchContainerStruct) iopodman.ListPodContainerInfo { - lc := iopodman.ListPodContainerInfo{ - Id: containerID, - Status: batchInfo.ConState.String(), - Name: batchInfo.ConConfig.Name, - } - return lc -} - -func makeListPod(pod *libpod.Pod, batchInfo PsOptions) (iopodman.ListPodData, error) { - var listPodsContainers []iopodman.ListPodContainerInfo - var errPodData = iopodman.ListPodData{} - status, err := pod.GetPodStatus() - if err != nil { - return errPodData, err - } - containers, err := pod.AllContainers() - if err != nil { - return errPodData, err - } - for _, ctr := range containers { - batchInfo, err := BatchContainerOp(ctr, batchInfo) - if err != nil { - return errPodData, err - } - - listPodsContainers = append(listPodsContainers, makeListPodContainers(ctr.ID(), batchInfo)) - } - listPod := iopodman.ListPodData{ - Createdat: pod.CreatedTime().Format(time.RFC3339), - Id: pod.ID(), - Name: pod.Name(), - Status: status, - Cgroup: pod.CgroupParent(), - Numberofcontainers: strconv.Itoa(len(listPodsContainers)), - Containersinfo: listPodsContainers, - } - return listPod, nil -} - -func handlePodCall(call iopodman.VarlinkCall, pod *libpod.Pod, ctrErrs map[string]error, err error) error { - if err != nil && ctrErrs == nil { - return call.ReplyErrorOccurred(err.Error()) - } - if ctrErrs != nil { - containerErrs := make([]iopodman.PodContainerErrorData, len(ctrErrs)) - for ctr, reason := range ctrErrs { - ctrErr := iopodman.PodContainerErrorData{Containerid: ctr, Reason: reason.Error()} - containerErrs = append(containerErrs, ctrErr) - } - return call.ReplyPodContainerError(pod.ID(), containerErrs) - } - - 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 -} - -func derefBool(inBool *bool) bool { - if inBool == nil { - return false - } - return *inBool -} - -func derefString(in *string) string { - if in == nil { - return "" - } - return *in -} - -func makePsOpts(inOpts iopodman.PsOpts) PsOptions { - last := 0 - if inOpts.Last != nil { - lastT := *inOpts.Last - last = int(lastT) - } - return PsOptions{ - All: inOpts.All, - Last: last, - Latest: derefBool(inOpts.Latest), - NoTrunc: derefBool(inOpts.NoTrunc), - Pod: derefBool(inOpts.Pod), - Size: derefBool(inOpts.Size), - Sort: derefString(inOpts.Sort), - Namespace: true, - Sync: derefBool(inOpts.Sync), - } -} - -// forwardOutput is a helper method for varlink endpoints that employ both more and without -// more. it is capable of sending updates as the output writer gets them or append them -// all to a log. the chan error is the error from the libpod call so we can honor -// and error event in that case. -func forwardOutput(log []string, c chan error, wantsMore bool, output channel.WriteCloser, reply func(br iopodman.MoreResponse) error) ([]string, error) { - done := false - for { - select { - // We need to check if the libpod func being called has returned an - // error yet - case err := <-c: - if err != nil { - return nil, err - } - done = true - // if no error is found, we pull what we can from the log writer and - // append it to log string slice - case line := <-output.Chan(): - log = append(log, string(line)) - // If the end point is being used in more mode, send what we have - if wantsMore { - br := iopodman.MoreResponse{ - Logs: log, - } - if err := reply(br); err != nil { - return nil, err - } - // "reset" the log to empty because we are sending what we - // get as we get it - log = []string{} - } - } - if done { - break - } - } - return log, nil -} diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go deleted file mode 100644 index d96e82a3f..000000000 --- a/pkg/varlinkapi/virtwriter/virtwriter.go +++ /dev/null @@ -1,204 +0,0 @@ -package virtwriter - -import ( - "bufio" - "encoding/binary" - "encoding/json" - "io" - "time" - - "github.com/pkg/errors" - "k8s.io/client-go/tools/remotecommand" -) - -// SocketDest is the "key" to where IO should go on the varlink -// multiplexed socket -type SocketDest int - -const ( - // ToStdout indicates traffic should go stdout - ToStdout SocketDest = iota - // ToStdin indicates traffic came from stdin - ToStdin SocketDest = iota - // ToStderr indicates traffuc should go to stderr - ToStderr SocketDest = iota - // TerminalResize indicates a terminal resize event has occurred - // and data should be passed to resizer - TerminalResize SocketDest = iota - // Quit and detach - Quit SocketDest = iota - // HangUpFromClient hangs up from the client - HangUpFromClient SocketDest = iota -) - -// ErrClientHangup signifies that the client wants to drop its connection from -// the server. -var ErrClientHangup = errors.New("client hangup") - -// IntToSocketDest returns a socketdest based on integer input -func IntToSocketDest(i int) SocketDest { - switch i { - case ToStdout.Int(): - return ToStdout - case ToStderr.Int(): - return ToStderr - case ToStdin.Int(): - return ToStdin - case TerminalResize.Int(): - return TerminalResize - case Quit.Int(): - return Quit - case HangUpFromClient.Int(): - return HangUpFromClient - default: - return ToStderr - } -} - -// Int returns the integer representation of the socket dest -func (sd SocketDest) Int() int { - return int(sd) -} - -// VirtWriteCloser are writers for attach which include the dest -// of the data -type VirtWriteCloser struct { - writer *bufio.Writer - dest SocketDest -} - -// NewVirtWriteCloser is a constructor -func NewVirtWriteCloser(w *bufio.Writer, dest SocketDest) VirtWriteCloser { - return VirtWriteCloser{w, dest} -} - -// Close is a required method for a writecloser -func (v VirtWriteCloser) Close() error { - return v.writer.Flush() -} - -// Write prepends a header to the input message. The header is -// 8bytes. Position one contains the destination. Positions -// 5,6,7,8 are a big-endian encoded uint32 for len of the message. -func (v VirtWriteCloser) Write(input []byte) (int, error) { - header := []byte{byte(v.dest), 0, 0, 0} - // Go makes us define the byte for big endian - mlen := make([]byte, 4) - binary.BigEndian.PutUint32(mlen, uint32(len(input))) - // append the message len to the header - msg := append(header, mlen...) - // append the message to the header - msg = append(msg, input...) - _, err := v.writer.Write(msg) - if err != nil { - return 0, err - } - err = v.writer.Flush() - return len(input), err -} - -// Reader decodes the content that comes over the wire and directs it to the proper destination. -func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remotecommand.TerminalSize, execEcChan chan int) error { - var messageSize int64 - headerBytes := make([]byte, 8) - - if r == nil { - return errors.Errorf("Reader must not be nil") - } - for { - n, err := io.ReadFull(r, headerBytes) - if err != nil { - return errors.Wrapf(err, "Virtual Read failed, %d", n) - } - if n < 8 { - return errors.New("short read and no full header read") - } - - messageSize = int64(binary.BigEndian.Uint32(headerBytes[4:8])) - switch IntToSocketDest(int(headerBytes[0])) { - case ToStdout: - if output != nil { - _, err := io.CopyN(output, r, messageSize) - if err != nil { - return err - } - } - case ToStderr: - if errput != nil { - _, err := io.CopyN(errput, r, messageSize) - if err != nil { - return err - } - } - case ToStdin: - if input != nil { - _, err := io.CopyN(input, r, messageSize) - if err != nil { - return err - } - } - case TerminalResize: - if resize != nil { - out := make([]byte, messageSize) - if messageSize > 0 { - _, err = io.ReadFull(r, out) - - if err != nil { - return err - } - } - // Resize events come over in bytes, need to be reserialized - resizeEvent := remotecommand.TerminalSize{} - if err := json.Unmarshal(out, &resizeEvent); err != nil { - return err - } - resize <- resizeEvent - } - case Quit: - out := make([]byte, messageSize) - if messageSize > 0 { - _, err = io.ReadFull(r, out) - - if err != nil { - return err - } - } - if execEcChan != nil { - ecInt := binary.BigEndian.Uint32(out) - execEcChan <- int(ecInt) - } - return nil - case HangUpFromClient: - // This sleep allows the pipes to flush themselves before tearing everything down. - // It makes me sick to do it but after a full day I cannot put my finger on the race - // that occurs when closing things up. It would require a significant rewrite of code - // to make the pipes close down properly. Given that we are currently discussing a - // rewrite of all things remote, this hardly seems worth resolving. - // - // reproducer: echo hello | (podman-remote run -i alpine cat) - time.Sleep(1 * time.Second) - return ErrClientHangup - default: - // Something really went wrong - return errors.New("unknown multiplex destination") - } - } -} - -// HangUp sends message to peer to close connection -func HangUp(writer *bufio.Writer, ec uint32) (err error) { - n := 0 - msg := make([]byte, 4) - - binary.BigEndian.PutUint32(msg, ec) - - writeQuit := NewVirtWriteCloser(writer, Quit) - if n, err = writeQuit.Write(msg); err != nil { - return - } - - if n != len(msg) { - return errors.Errorf("Failed to send complete %s message", string(msg)) - } - return -} diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go deleted file mode 100644 index 7cc714ea4..000000000 --- a/pkg/varlinkapi/volumes.go +++ /dev/null @@ -1,165 +0,0 @@ -// +build varlink - -package varlinkapi - -import ( - "context" - "encoding/json" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" - iopodman "github.com/containers/podman/v2/pkg/varlink" -) - -// VolumeCreate creates a libpod volume based on input from a varlink connection -func (i *VarlinkAPI) 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 { - parsedOptions, err := parse.VolumeOptions(options.Options) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - volumeOptions = append(volumeOptions, parsedOptions...) - } - 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 *VarlinkAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error { - success, failed, err := SharedRemoveVolumes(getContext(), i.Runtime, options.Volumes, options.All, options.Force) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - // Convert map[string]string to map[string]error - errStrings := make(map[string]string) - for k, v := range failed { - errStrings[k] = v.Error() - } - return call.ReplyVolumeRemove(success, errStrings) -} - -// GetVolumes returns all the volumes known to the remote system -func (i *VarlinkAPI) 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(), - } - volumes = append(volumes, newVol) - } - return call.ReplyGetVolumes(volumes) -} - -// InspectVolume inspects a single volume, returning its JSON as a string. -func (i *VarlinkAPI) InspectVolume(call iopodman.VarlinkCall, name string) error { - vol, err := i.Runtime.LookupVolume(name) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - inspectOut, err := vol.Inspect() - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - inspectJSON, err := json.Marshal(inspectOut) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyInspectVolume(string(inspectJSON)) -} - -// VolumesPrune removes unused images via a varlink call -func (i *VarlinkAPI) VolumesPrune(call iopodman.VarlinkCall) error { - var ( - prunedErrors []string - prunedNames []string - ) - responses, err := i.Runtime.PruneVolumes(getContext()) - if err != nil { - return call.ReplyVolumesPrune([]string{}, []string{err.Error()}) - } - for k, v := range responses { - if v == nil { - prunedNames = append(prunedNames, k) - } else { - prunedErrors = append(prunedErrors, v.Error()) - } - } - return call.ReplyVolumesPrune(prunedNames, prunedErrors) -} - -// Remove given set of volumes -func SharedRemoveVolumes(ctx context.Context, runtime *libpod.Runtime, vols []string, all, force bool) ([]string, map[string]error, error) { - var ( - toRemove []*libpod.Volume - success []string - failed map[string]error - ) - - failed = make(map[string]error) - - if all { - vols, err := runtime.Volumes() - if err != nil { - return nil, nil, err - } - toRemove = vols - } else { - for _, v := range vols { - vol, err := runtime.LookupVolume(v) - if err != nil { - failed[v] = err - continue - } - toRemove = append(toRemove, vol) - } - } - - // We could parallelize this, but I haven't heard anyone complain about - // performance here yet, so hold off. - for _, vol := range toRemove { - if err := runtime.RemoveVolume(ctx, vol, force); err != nil { - failed[vol.Name()] = err - continue - } - success = append(success, vol.Name()) - } - - return success, failed, nil -} diff --git a/test/apiv2/23-containersArchive.at b/test/apiv2/23-containersArchive.at new file mode 100644 index 000000000..459800196 --- /dev/null +++ b/test/apiv2/23-containersArchive.at @@ -0,0 +1,81 @@ +# -*- sh -*- +# +# test more container-related endpoints +# + +red='\e[31m' +nc='\e[0m' + +podman pull $IMAGE &>/dev/null + +# Ensure clean slate +podman rm -a -f &>/dev/null + +CTR="ArchiveTestingCtr" + +TMPD=$(mktemp -d) +pushd "${TMPD}" +echo "Hello" > "hello.txt" +tar --format=posix -cvf "hello.tar" "hello.txt" &> /dev/null +popd + +HELLO_TAR="${TMPD}/hello.tar" + +podman run -d --name "${CTR}" "${IMAGE}" top + +function cleanUpArchiveTest() { + podman container stop "${CTR}" &> /dev/null + podman container rm "${CTR}" &> /dev/null + rm -fr "${TMPD}" &> /dev/null +} + +t HEAD "containers/nonExistentCtr/archive?path=%2F" 404 +t HEAD "containers/${CTR}/archive?path=%2Fnon%2Fexistent%2Fpath" 404 +t HEAD "containers/${CTR}/archive?path=%2Fetc%2Fpasswd" 200 + +curl "http://$HOST:$PORT/containers/${CTR}/archive?path=%2Ftmp%2F" \ + -X PUT \ + -H "Content-Type: application/x-tar" \ + --upload-file "${HELLO_TAR}" &> /dev/null + +if ! podman exec -it "${CTR}" "grep" "Hello" "/tmp/hello.txt" &> /dev/null ; then + echo -e "${red}NOK: The hello.txt file has not been uploaded.${nc}" 1>&2; + cleanUpArchiveTest + exit 1 +fi + +t HEAD "containers/${CTR}/archive?path=%2Ftmp%2Fhello.txt" 200 + +curl "http://$HOST:$PORT/containers/${CTR}/archive?path=%2Ftmp%2Fhello.txt" \ + --dump-header "${TMPD}/headers.txt" \ + -o "${TMPD}/body.tar" \ + -X GET &> /dev/null + +PATH_STAT="$(grep X-Docker-Container-Path-Stat "${TMPD}/headers.txt" | cut -d " " -f 2 | base64 -d --ignore-garbage)" + +ARCHIVE_TEST_ERROR="" + +if [ "$(echo "${PATH_STAT}" | jq ".name")" != '"hello.txt"' ]; then + echo -e "${red}NOK: Wrong name in X-Docker-Container-Path-Stat header.${nc}" 1>&2; + ARCHIVE_TEST_ERROR="1" +fi + +if [ "$(echo "${PATH_STAT}" | jq ".size")" != "6" ]; then + echo -e "${red}NOK: Wrong size in X-Docker-Container-Path-Stat header.${nc}" 1>&2; + ARCHIVE_TEST_ERROR="1" +fi + +if ! tar -tf "${TMPD}/body.tar" | grep "hello.txt" &> /dev/null; then + echo -e "${red}NOK: Body doesn't contain expected file.${nc}" 1>&2; + ARCHIVE_TEST_ERROR="1" +fi + +if [ "$(tar -xf "${TMPD}/body.tar" hello.txt --to-stdout)" != "Hello" ]; then + echo -e "${red}NOK: Content of file doesn't match.${nc}" 1>&2; + ARCHIVE_TEST_ERROR="1" +fi + +cleanUpArchiveTest +if [[ "${ARCHIVE_TEST_ERROR}" ]] ; then + exit 1; +fi diff --git a/test/apiv2/25-containersMore.at b/test/apiv2/25-containersMore.at index e0e6f7222..4f6b80a5f 100644 --- a/test/apiv2/25-containersMore.at +++ b/test/apiv2/25-containersMore.at @@ -52,4 +52,31 @@ t POST libpod/containers/foo/unmount '' 204 t DELETE libpod/containers/foo?force=true 204 +podman run $IMAGE true + +t GET libpod/containers/json?last=1 200 \ + length=1 \ + .[0].Id~[0-9a-f]\\{64\\} \ + .[0].Image=$IMAGE \ + .[0].Command[0]="true" \ + .[0].State~\\\(exited\\\|stopped\\\) \ + .[0].ExitCode=0 \ + .[0].IsInfra=false + +cid=$(jq -r '.[0].Id' <<<"$output") + +t GET libpod/generate/$cid/kube 200 +like "$output" ".*apiVersion:.*" "Check generated kube yaml - apiVersion" +like "$output" ".*kind:\\sPod.*" "Check generated kube yaml - kind: Pod" +like "$output" ".*metadata:.*" "Check generated kube yaml - metadata" +like "$output" ".*spec:.*" "Check generated kube yaml - spec" + +t GET libpod/generate/$cid/kube?service=true 200 +like "$output" ".*apiVersion:.*" "Check generated kube yaml(service=true) - apiVersion" +like "$output" ".*kind:\\sPod.*" "Check generated kube yaml(service=true) - kind: Pod" +like "$output" ".*metadata:.*" "Check generated kube yaml(service=true) - metadata" +like "$output" ".*spec:.*" "Check generated kube yaml(service=true) - spec" +like "$output" ".*kind:\\sService.*" "Check generated kube yaml(service=true) - kind: Service" + +t DELETE libpod/containers/$cid 204 # vim: filetype=sh diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index facafcb77..16d8bb770 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -661,7 +661,7 @@ func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd return &PodmanSessionIntegration{podmanSession} } -// We don't support running Varlink when local +// RestartRemoteService stop and start API Server, usually to change config func (p *PodmanTestIntegration) RestartRemoteService() { p.StopRemoteService() p.StartRemoteService() @@ -788,3 +788,9 @@ func generateNetworkConfig(p *PodmanTestIntegration) (string, string) { return name, path } + +func (p *PodmanTestIntegration) removeCNINetwork(name string) { + session := p.Podman([]string{"network", "rm", "-f", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeNumerically("<=", 1)) +} diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go index fe8b8fe56..da57bb4c0 100644 --- a/test/e2e/libpod_suite_remote_test.go +++ b/test/e2e/libpod_suite_remote_test.go @@ -107,7 +107,6 @@ func (p *PodmanTestIntegration) StopRemoteService() { } } else { - //p.ResetVarlinkAddress() parentPid := fmt.Sprintf("%d", p.RemoteSession.Pid) pgrep := exec.Command("pgrep", "-P", parentPid) fmt.Printf("running: pgrep %s\n", parentPid) diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index c37b24ab6..0ae30ca10 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -59,13 +59,12 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error { } func (p *PodmanTestIntegration) StopRemoteService() {} -func (p *PodmanTestIntegration) DelayForVarlink() {} // SeedImages is a no-op for localized testing func (p *PodmanTestIntegration) SeedImages() error { return nil } -// We don't support running Varlink when local +// We don't support running API service when local func (p *PodmanTestIntegration) StartRemoteService() { } diff --git a/test/e2e/libpod_suite_varlink_test.go b/test/e2e/libpod_suite_varlink_test.go deleted file mode 100644 index 275a1115e..000000000 --- a/test/e2e/libpod_suite_varlink_test.go +++ /dev/null @@ -1,207 +0,0 @@ -// +build remoteclientvarlink - -package integration - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - "syscall" - "time" - - "github.com/containers/podman/v2/pkg/rootless" - - "github.com/onsi/ginkgo" -) - -func IsRemote() bool { - return true -} - -func SkipIfRemote(reason string) { - ginkgo.Skip("[remote]: " + reason) -} - -// Podman is the exec call to podman on the filesystem -func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { - podmanSession := p.PodmanBase(args, false, false) - return &PodmanSessionIntegration{podmanSession} -} - -// PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files -func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration { - podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, nil, extraFiles) - return &PodmanSessionIntegration{podmanSession} -} - -// PodmanNoCache calls podman with out adding the imagecache -func (p *PodmanTestIntegration) PodmanNoCache(args []string) *PodmanSessionIntegration { - podmanSession := p.PodmanBase(args, false, true) - return &PodmanSessionIntegration{podmanSession} -} - -// PodmanNoEvents calls the Podman command without an imagecache and without an -// events backend. It is used mostly for caching and uncaching images. -func (p *PodmanTestIntegration) PodmanNoEvents(args []string) *PodmanSessionIntegration { - podmanSession := p.PodmanBase(args, true, true) - return &PodmanSessionIntegration{podmanSession} -} - -func (p *PodmanTestIntegration) setDefaultRegistriesConfigEnv() { - defaultFile := filepath.Join(INTEGRATION_ROOT, "test/registries.conf") - os.Setenv("REGISTRIES_CONFIG_PATH", defaultFile) -} - -func (p *PodmanTestIntegration) setRegistriesConfigEnv(b []byte) { - outfile := filepath.Join(p.TempDir, "registries.conf") - os.Setenv("REGISTRIES_CONFIG_PATH", outfile) - ioutil.WriteFile(outfile, b, 0644) -} - -func resetRegistriesConfigEnv() { - os.Setenv("REGISTRIES_CONFIG_PATH", "") -} -func PodmanTestCreate(tempDir string) *PodmanTestIntegration { - pti := PodmanTestCreateUtil(tempDir, true) - pti.StartRemoteService() - return pti -} - -func (p *PodmanTestIntegration) ResetVarlinkAddress() { - //os.Unsetenv("PODMAN_VARLINK_ADDRESS") -} - -func (p *PodmanTestIntegration) SetVarlinkAddress(addr string) { - //os.Setenv("PODMAN_VARLINK_ADDRESS", addr) -} - -func (p *PodmanTestIntegration) StartVarlink() { - if os.Geteuid() == 0 { - os.MkdirAll("/run/podman", 0755) - } - varlinkEndpoint := p.RemoteSocket - p.SetVarlinkAddress(p.RemoteSocket) - - args := []string{"varlink", "--time", "0", varlinkEndpoint} - podmanOptions := getVarlinkOptions(p, args) - command := exec.Command(p.PodmanBinary, podmanOptions...) - fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " ")) - command.Start() - command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - p.RemoteCommand = command - p.RemoteSession = command.Process - p.DelayForService() -} - -func (p *PodmanTestIntegration) StopVarlink() { - var out bytes.Buffer - var pids []int - varlinkSession := p.RemoteSession - - if !rootless.IsRootless() { - if err := varlinkSession.Kill(); err != nil { - fmt.Fprintf(os.Stderr, "error on varlink stop-kill %q", err) - } - if _, err := varlinkSession.Wait(); err != nil { - fmt.Fprintf(os.Stderr, "error on varlink stop-wait %q", err) - } - - } else { - p.ResetVarlinkAddress() - parentPid := fmt.Sprintf("%d", p.RemoteSession.Pid) - pgrep := exec.Command("pgrep", "-P", parentPid) - fmt.Printf("running: pgrep %s\n", parentPid) - pgrep.Stdout = &out - err := pgrep.Run() - if err != nil { - fmt.Fprint(os.Stderr, "unable to find varlink pid") - } - - for _, s := range strings.Split(out.String(), "\n") { - if len(s) == 0 { - continue - } - p, err := strconv.Atoi(s) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to convert %s to int", s) - } - if p != 0 { - pids = append(pids, p) - } - } - - pids = append(pids, p.RemoteSession.Pid) - for _, pid := range pids { - syscall.Kill(pid, syscall.SIGKILL) - } - } - socket := strings.Split(p.RemoteSocket, ":")[1] - if err := os.Remove(socket); err != nil { - fmt.Println(err) - } -} - -//MakeOptions assembles all the podman main options -func (p *PodmanTestIntegration) makeOptions(args []string, noEvents, noCache bool) []string { - return args -} - -//MakeOptions assembles all the podman main options -func getVarlinkOptions(p *PodmanTestIntegration, args []string) []string { - podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s", - p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ") - if os.Getenv("HOOK_OPTION") != "" { - podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION")) - } - podmanOptions = append(podmanOptions, strings.Split(p.StorageOptions, " ")...) - podmanOptions = append(podmanOptions, args...) - return podmanOptions -} - -func (p *PodmanTestIntegration) RestoreArtifactToCache(image string) error { - fmt.Printf("Restoring %s...\n", image) - dest := strings.Split(image, "/") - destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1)) - p.CrioRoot = p.ImageCacheDir - restore := p.PodmanNoEvents([]string{"load", "-q", "-i", destName}) - restore.WaitWithDefaultTimeout() - return nil -} - -// SeedImages restores all the artifacts into the main store for remote tests -func (p *PodmanTestIntegration) SeedImages() error { - return p.RestoreAllArtifacts() -} - -// RestoreArtifact puts the cached image into our test store -func (p *PodmanTestIntegration) RestoreArtifact(image string) error { - fmt.Printf("Restoring %s...\n", image) - dest := strings.Split(image, "/") - destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1)) - args := []string{"load", "-q", "-i", destName} - podmanOptions := getVarlinkOptions(p, args) - command := exec.Command(p.PodmanBinary, podmanOptions...) - fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " ")) - command.Start() - command.Wait() - return nil -} - -func (p *PodmanTestIntegration) DelayForVarlink() { - for i := 0; i < 5; i++ { - session := p.Podman([]string{"info"}) - session.WaitWithDefaultTimeout() - if session.ExitCode() == 0 || i == 4 { - break - } - time.Sleep(1 * time.Second) - } -} - -func populateCache(podman *PodmanTestIntegration) {} -func removeCache() {} diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go new file mode 100644 index 000000000..7cdad9bf2 --- /dev/null +++ b/test/e2e/network_connect_disconnect_test.go @@ -0,0 +1,217 @@ +package integration + +import ( + "os" + + . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman network connect and disconnect", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("bad network name in disconnect should result in error", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + dis := podmanTest.Podman([]string{"network", "disconnect", "foobar", "test"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).ToNot(BeZero()) + + }) + + It("bad container name in network disconnect should result in error", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + netName := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + dis := podmanTest.Podman([]string{"network", "disconnect", netName, "foobar"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).ToNot(BeZero()) + + }) + + It("podman network disconnect", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + netName := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(BeZero()) + + exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).To(BeZero()) + + dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).To(BeZero()) + + exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).ToNot(BeZero()) + }) + + It("bad network name in connect should result in error", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + dis := podmanTest.Podman([]string{"network", "connect", "foobar", "test"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).ToNot(BeZero()) + + }) + + It("bad container name in network connect should result in error", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + netName := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + dis := podmanTest.Podman([]string{"network", "connect", netName, "foobar"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).ToNot(BeZero()) + + }) + + It("podman connect on a container that already is connected to the network should error", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + netName := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(BeZero()) + + con := podmanTest.Podman([]string{"network", "connect", netName, "test"}) + con.WaitWithDefaultTimeout() + Expect(con.ExitCode()).ToNot(BeZero()) + }) + + It("podman network connect", func() { + SkipIfRemote("This requires a pending PR to be merged before it will work") + SkipIfRootless("network connect and disconnect are only rootfull") + netName := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(BeZero()) + + exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).To(BeZero()) + + // Create a second network + newNetName := "aliasTest" + stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", newNetName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(newNetName) + + connect := podmanTest.Podman([]string{"network", "connect", newNetName, "test"}) + connect.WaitWithDefaultTimeout() + Expect(connect.ExitCode()).To(BeZero()) + + exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).To(BeZero()) + }) + + It("podman network connect when not running", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + netName := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + ctr := podmanTest.Podman([]string{"create", "--name", "test", ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(BeZero()) + + dis := podmanTest.Podman([]string{"network", "connect", netName, "test"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).To(BeZero()) + + start := podmanTest.Podman([]string{"start", "test"}) + start.WaitWithDefaultTimeout() + Expect(start.ExitCode()).To(BeZero()) + + exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).To(BeZero()) + + exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).To(BeZero()) + }) + + It("podman network disconnect when not running", func() { + SkipIfRootless("network connect and disconnect are only rootfull") + netName1 := "aliasTest" + stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", netName1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName1) + + netName2 := "aliasTest" + stringid.GenerateNonCryptoID() + session2 := podmanTest.Podman([]string{"network", "create", netName2}) + session2.WaitWithDefaultTimeout() + Expect(session2.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName2) + + ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName1 + "," + netName2, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(BeZero()) + + dis := podmanTest.Podman([]string{"network", "disconnect", netName1, "test"}) + dis.WaitWithDefaultTimeout() + Expect(dis.ExitCode()).To(BeZero()) + + start := podmanTest.Podman([]string{"start", "test"}) + start.WaitWithDefaultTimeout() + Expect(start.ExitCode()).To(BeZero()) + + exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).To(BeZero()) + + exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) + exec.WaitWithDefaultTimeout() + Expect(exec.ExitCode()).ToNot(BeZero()) + }) +}) diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index cb997d10a..043046c33 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -55,12 +55,6 @@ func genericPluginsToPortMap(plugins interface{}, pluginType string) (network.Po return portMap, err } -func (p *PodmanTestIntegration) removeCNINetwork(name string) { - session := p.Podman([]string{"network", "rm", "-f", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeNumerically("<=", 1)) -} - func removeNetworkDevice(name string) { session := SystemExec("ip", []string{"link", "delete", name}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index b7e88b8cd..ad6af61c7 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -406,156 +406,14 @@ var _ = Describe("Podman network", func() { Expect(c3.ExitCode()).To(BeZero()) }) - It("bad network name in disconnect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - dis := podmanTest.Podman([]string{"network", "disconnect", "foobar", "test"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) - - }) - - It("bad container name in network disconnect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - dis := podmanTest.Podman([]string{"network", "disconnect", netName, "foobar"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) - - }) - - It("podman network disconnect with invalid container state should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName, ALPINE, "top"}) - ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) - - dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) - }) - - It("podman network disconnect", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) - ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) - - exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) - exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) - - dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).To(BeZero()) - - exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) - exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).ToNot(BeZero()) - }) - - It("bad network name in connect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - dis := podmanTest.Podman([]string{"network", "connect", "foobar", "test"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) - - }) - - It("bad container name in network connect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - dis := podmanTest.Podman([]string{"network", "connect", netName, "foobar"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) - - }) - - It("podman connect on a container that already is connected to the network should error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName, ALPINE, "top"}) - ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) - - con := podmanTest.Podman([]string{"network", "connect", netName, "test"}) - con.WaitWithDefaultTimeout() - Expect(con.ExitCode()).ToNot(BeZero()) - }) - - It("podman network connect with invalid container state should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName, ALPINE, "top"}) - ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) - - dis := podmanTest.Podman([]string{"network", "connect", netName, "test"}) - dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) - }) - - It("podman network connect", func() { - SkipIfRemote("This requires a pending PR to be merged before it will work") - SkipIfRootless("network connect and disconnect are only rootfull") - netName := "aliasTest" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", netName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(netName) - - ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) - ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) - - exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) - exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) - - // Create a second network - newNetName := "aliasTest" + stringid.GenerateNonCryptoID() - session = podmanTest.Podman([]string{"network", "create", newNetName}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - defer podmanTest.removeCNINetwork(newNetName) - - connect := podmanTest.Podman([]string{"network", "connect", newNetName, "test"}) - connect.WaitWithDefaultTimeout() - Expect(connect.ExitCode()).To(BeZero()) - - exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) - exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + It("podman network create/remove macvlan", func() { + net := "macvlan" + stringid.GenerateNonCryptoID() + nc := podmanTest.Podman([]string{"network", "create", "--macvlan", "lo", net}) + nc.WaitWithDefaultTimeout() + Expect(nc.ExitCode()).To(Equal(0)) + + nc = podmanTest.Podman([]string{"network", "rm", net}) + nc.WaitWithDefaultTimeout() + Expect(nc.ExitCode()).To(Equal(0)) }) }) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 92e4544f9..5ecfdd6b5 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -164,9 +164,15 @@ spec: volumes: {{ range . }} - name: {{ .Name }} + {{- if (eq .VolumeType "HostPath") }} hostPath: - path: {{ .Path }} - type: {{ .Type }} + path: {{ .HostPath.Path }} + type: {{ .HostPath.Type }} + {{- end }} + {{- if (eq .VolumeType "PersistentVolumeClaim") }} + persistentVolumeClaim: + claimName: {{ .PersistentVolumeClaim.ClaimName }} + {{- end }} {{ end }} {{ end }} status: {} @@ -692,19 +698,44 @@ func getCtrNameInPod(pod *Pod) string { return fmt.Sprintf("%s-%s", pod.Name, defaultCtrName) } -type Volume struct { - Name string +type HostPath struct { Path string Type string } -// getVolume takes a type and a location for a volume -// giving it a default name of volName -func getVolume(vType, vPath string) *Volume { +type PersistentVolumeClaim struct { + ClaimName string +} + +type Volume struct { + VolumeType string + Name string + HostPath + PersistentVolumeClaim +} + +// getHostPathVolume takes a type and a location for a HostPath +// volume giving it a default name of volName +func getHostPathVolume(vType, vPath string) *Volume { + return &Volume{ + VolumeType: "HostPath", + Name: defaultVolName, + HostPath: HostPath{ + Path: vPath, + Type: vType, + }, + } +} + +// getHostPathVolume takes a name for a Persistentvolumeclaim +// volume giving it a default name of volName +func getPersistentVolumeClaimVolume(vName string) *Volume { return &Volume{ - Name: defaultVolName, - Path: vPath, - Type: vType, + VolumeType: "PersistentVolumeClaim", + Name: defaultVolName, + PersistentVolumeClaim: PersistentVolumeClaim{ + ClaimName: vName, + }, } } @@ -1257,7 +1288,7 @@ spec: It("podman play kube test with non-existent empty HostPath type volume", func() { hostPathLocation := filepath.Join(tempdir, "file") - pod := getPod(withVolume(getVolume(`""`, hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume(`""`, hostPathLocation))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1272,7 +1303,7 @@ spec: Expect(err).To(BeNil()) f.Close() - pod := getPod(withVolume(getVolume(`""`, hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume(`""`, hostPathLocation))) err = generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1284,7 +1315,7 @@ spec: It("podman play kube test with non-existent File HostPath type volume", func() { hostPathLocation := filepath.Join(tempdir, "file") - pod := getPod(withVolume(getVolume("File", hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume("File", hostPathLocation))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1299,7 +1330,7 @@ spec: Expect(err).To(BeNil()) f.Close() - pod := getPod(withVolume(getVolume("File", hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume("File", hostPathLocation))) err = generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1311,7 +1342,7 @@ spec: It("podman play kube test with FileOrCreate HostPath type volume", func() { hostPathLocation := filepath.Join(tempdir, "file") - pod := getPod(withVolume(getVolume("FileOrCreate", hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume("FileOrCreate", hostPathLocation))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1327,7 +1358,7 @@ spec: It("podman play kube test with DirectoryOrCreate HostPath type volume", func() { hostPathLocation := filepath.Join(tempdir, "file") - pod := getPod(withVolume(getVolume("DirectoryOrCreate", hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume("DirectoryOrCreate", hostPathLocation))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1347,7 +1378,7 @@ spec: Expect(err).To(BeNil()) f.Close() - pod := getPod(withVolume(getVolume("Socket", hostPathLocation))) + pod := getPod(withVolume(getHostPathVolume("Socket", hostPathLocation))) err = generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1356,14 +1387,14 @@ spec: Expect(kube.ExitCode()).NotTo(Equal(0)) }) - It("podman play kube test with read only volume", func() { + It("podman play kube test with read only HostPath volume", func() { hostPathLocation := filepath.Join(tempdir, "file") f, err := os.Create(hostPathLocation) Expect(err).To(BeNil()) f.Close() ctr := getCtr(withVolumeMount(hostPathLocation, true), withImage(BB)) - pod := getPod(withVolume(getVolume("File", hostPathLocation)), withCtr(ctr)) + pod := getPod(withVolume(getHostPathVolume("File", hostPathLocation)), withCtr(ctr)) err = generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -1379,6 +1410,26 @@ spec: Expect(inspect.OutputToString()).To(ContainSubstring(correct)) }) + It("podman play kube test with PersistentVolumeClaim volume", func() { + volumeName := "namedVolume" + + ctr := getCtr(withVolumeMount("/test", false), withImage(BB)) + pod := getPod(withVolume(getPersistentVolumeClaimVolume(volumeName)), withCtr(ctr)) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ (index .Mounts 0).Type }}:{{ (index .Mounts 0).Name }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + + correct := fmt.Sprintf("volume:%s", volumeName) + Expect(inspect.OutputToString()).To(Equal(correct)) + }) + It("podman play kube applies labels to pods", func() { var numReplicas int32 = 5 expectedLabelKey := "key1" diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index ccfbcefae..be0a2f6f0 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -9,7 +9,6 @@ import ( "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" - "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -477,23 +476,4 @@ entrypoint ["/fromimage"] Expect(status3.ExitCode()).To(Equal(0)) Expect(strings.Contains(status3.OutputToString(), "Degraded")).To(BeTrue()) }) - - It("podman create pod invalid network config", func() { - net1 := "n1" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", net1}) - session.WaitWithDefaultTimeout() - defer podmanTest.removeCNINetwork(net1) - Expect(session.ExitCode()).To(BeZero()) - - session = podmanTest.Podman([]string{"pod", "create", "--network", "host", "--network", net1}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) - Expect(session.ErrorToString()).To(ContainSubstring("host")) - Expect(session.ErrorToString()).To(ContainSubstring("bridge")) - - session = podmanTest.Podman([]string{"pod", "create", "--network", "container:abc"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) - Expect(session.ErrorToString()).To(ContainSubstring("pods presently do not support network mode container")) - }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 1d416498c..3e80e953e 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -665,33 +665,4 @@ var _ = Describe("Podman run networking", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(BeZero()) }) - - It("podman run with multiple networks", func() { - net1 := "n1" + stringid.GenerateNonCryptoID() - session := podmanTest.Podman([]string{"network", "create", net1}) - session.WaitWithDefaultTimeout() - defer podmanTest.removeCNINetwork(net1) - Expect(session.ExitCode()).To(BeZero()) - - net2 := "n2" + stringid.GenerateNonCryptoID() - session = podmanTest.Podman([]string{"network", "create", net2}) - session.WaitWithDefaultTimeout() - defer podmanTest.removeCNINetwork(net2) - Expect(session.ExitCode()).To(BeZero()) - - run := podmanTest.Podman([]string{"run", "--network", net1, "--network", net2, ALPINE, "ip", "-o", "-4", "addr"}) - run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) - Expect(len(run.OutputToStringArray())).To(Equal(3)) - Expect(run.OutputToString()).To(ContainSubstring("lo")) - Expect(run.OutputToString()).To(ContainSubstring("eth0")) - Expect(run.OutputToString()).To(ContainSubstring("eth1")) - - //invalid config network host and cni should fail - run = podmanTest.Podman([]string{"run", "--network", "host", "--network", net2, ALPINE, "ip", "-o", "-4", "addr"}) - run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(125)) - Expect(run.ErrorToString()).To(ContainSubstring("host")) - Expect(run.ErrorToString()).To(ContainSubstring("bridge")) - }) }) diff --git a/test/e2e/system_reset_test.go b/test/e2e/system_reset_test.go index b2d350436..e716ce4f3 100644 --- a/test/e2e/system_reset_test.go +++ b/test/e2e/system_reset_test.go @@ -59,7 +59,7 @@ var _ = Describe("podman system reset", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - // If remote then the varlink service should have exited + // If remote then the API service should have exited // On local tests this is a noop podmanTest.StartRemoteService() diff --git a/test/endpoint/commit.go b/test/endpoint/commit.go deleted file mode 100644 index 97e10efb0..000000000 --- a/test/endpoint/commit.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "encoding/json" - "os" - - . "github.com/containers/podman/v2/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman commit", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlinkWithCache() - }) - - AfterEach(func() { - endpointTest.Cleanup() - - }) - - It("ensure commit with uppercase image name does not panic", func() { - body := make(map[string]string) - body["image_name"] = "FOO" - body["format"] = "oci" - body["name"] = "top" - b, err := json.Marshal(body) - Expect(err).To(BeNil()) - // run the container to be committed - _ = endpointTest.startTopContainer("top") - result := endpointTest.Varlink("Commit", string(b), false) - // This indicates an error occurred - Expect(len(result.StdErrToString())).To(BeNumerically(">", 0)) - }) - -}) diff --git a/test/endpoint/config.go b/test/endpoint/config.go deleted file mode 100644 index 9d2a5a239..000000000 --- a/test/endpoint/config.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build varlink - -package endpoint - -import "encoding/json" - -var ( - STORAGE_FS = "vfs" - STORAGE_OPTIONS = "--storage-driver vfs" - ROOTLESS_STORAGE_FS = "vfs" - ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs" - CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels} - nginx = "quay.io/libpod/alpine_nginx:latest" - BB_GLIBC = "docker.io/library/busybox:glibc" - registry = "docker.io/library/registry:2.6" - labels = "quay.io/libpod/alpine_labels:latest" -) - -func makeNameMessage(name string) string { - n := make(map[string]string) - n["name"] = name - b, _ := json.Marshal(n) - return string(b) -} diff --git a/test/endpoint/endpoint.go b/test/endpoint/endpoint.go deleted file mode 100644 index d2c143824..000000000 --- a/test/endpoint/endpoint.go +++ /dev/null @@ -1,225 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "os/exec" - "strconv" - "strings" - "syscall" - "time" - - "github.com/containers/podman/v2/pkg/rootless" - iopodman "github.com/containers/podman/v2/pkg/varlink" - . "github.com/onsi/ginkgo" - "github.com/onsi/gomega/gexec" -) - -var ( - ARTIFACT_DIR = "/tmp/.artifacts" - CGROUP_MANAGER = "systemd" - defaultWaitTimeout = 90 - //RESTORE_IMAGES = []string{ALPINE, BB} - INTEGRATION_ROOT string - ImageCacheDir = "/tmp/podman/imagecachedir" - VarlinkBinary = "/usr/bin/varlink" - ALPINE = "docker.io/library/alpine:latest" - infra = "k8s.gcr.io/pause:3.2" - BB = "docker.io/library/busybox:latest" - redis = "docker.io/library/redis:alpine" - fedoraMinimal = "quay.io/libpod/fedora-minimal:latest" -) - -type EndpointTestIntegration struct { - ArtifactPath string - CNIConfigDir string - CgroupManager string - ConmonBinary string - CrioRoot string - //Host HostOS - ImageCacheDir string - ImageCacheFS string - OCIRuntime string - PodmanBinary string - RemoteTest bool - RunRoot string - SignaturePolicyPath string - StorageOptions string - TmpDir string - Timings []string - VarlinkBinary string - VarlinkCommand *exec.Cmd - VarlinkEndpoint string - VarlinkSession *os.Process -} - -func (p *EndpointTestIntegration) StartVarlink() { - p.startVarlink(false) -} - -func (p *EndpointTestIntegration) StartVarlinkWithCache() { - p.startVarlink(true) -} - -func (p *EndpointTestIntegration) startVarlink(useImageCache bool) { - var ( - counter int - ) - if os.Geteuid() == 0 { - os.MkdirAll("/run/podman", 0755) - } - varlinkEndpoint := p.VarlinkEndpoint - //p.SetVarlinkAddress(p.RemoteSocket) - - args := []string{"varlink", "--time", "0", varlinkEndpoint} - podmanOptions := getVarlinkOptions(p, args) - if useImageCache { - cacheOptions := []string{"--storage-opt", fmt.Sprintf("%s.imagestore=%s", p.ImageCacheFS, p.ImageCacheDir)} - podmanOptions = append(cacheOptions, podmanOptions...) - } - command := exec.Command(p.PodmanBinary, podmanOptions...) - fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " ")) - command.Start() - command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - p.VarlinkCommand = command - p.VarlinkSession = command.Process - for { - if result := p.endpointReady(); result == 0 { - break - } - fmt.Println("Waiting for varlink connection to become active", counter) - time.Sleep(250 * time.Millisecond) - counter++ - if counter > 40 { - Fail("varlink endpoint never became ready") - } - } -} - -func (p *EndpointTestIntegration) endpointReady() int { - session := p.Varlink("GetVersion", "", false) - return session.ExitCode() -} - -func (p *EndpointTestIntegration) StopVarlink() { - var out bytes.Buffer - var pids []int - varlinkSession := p.VarlinkSession - - if !rootless.IsRootless() { - if err := varlinkSession.Kill(); err != nil { - fmt.Fprintf(os.Stderr, "error on varlink stop-kill %q", err) - } - if _, err := varlinkSession.Wait(); err != nil { - fmt.Fprintf(os.Stderr, "error on varlink stop-wait %q", err) - } - - } else { - //p.ResetVarlinkAddress() - parentPid := fmt.Sprintf("%d", p.VarlinkSession.Pid) - pgrep := exec.Command("pgrep", "-P", parentPid) - fmt.Printf("running: pgrep %s\n", parentPid) - pgrep.Stdout = &out - err := pgrep.Run() - if err != nil { - fmt.Fprint(os.Stderr, "unable to find varlink pid") - } - - for _, s := range strings.Split(out.String(), "\n") { - if len(s) == 0 { - continue - } - p, err := strconv.Atoi(s) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to convert %s to int", s) - } - if p != 0 { - pids = append(pids, p) - } - } - - pids = append(pids, p.VarlinkSession.Pid) - for _, pid := range pids { - syscall.Kill(pid, syscall.SIGKILL) - } - } - socket := strings.Split(p.VarlinkEndpoint, ":")[1] - if err := os.Remove(socket); err != nil { - fmt.Println(err) - } -} - -type EndpointSession struct { - *gexec.Session -} - -func getVarlinkOptions(p *EndpointTestIntegration, args []string) []string { - podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s", - p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ") - if os.Getenv("HOOK_OPTION") != "" { - podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION")) - } - podmanOptions = append(podmanOptions, strings.Split(p.StorageOptions, " ")...) - podmanOptions = append(podmanOptions, args...) - return podmanOptions -} - -func (p *EndpointTestIntegration) Varlink(endpoint, message string, more bool) *EndpointSession { - //call unix:/run/user/1000/podman/io.podman/io.podman.GetContainerStats '{"name": "foobar" }' - var ( - command *exec.Cmd - ) - - args := []string{"call"} - if more { - args = append(args, "-m") - } - args = append(args, []string{fmt.Sprintf("%s/io.podman.%s", p.VarlinkEndpoint, endpoint)}...) - if len(message) > 0 { - args = append(args, message) - } - command = exec.Command(p.VarlinkBinary, args...) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - if err != nil { - Fail(fmt.Sprintf("unable to run varlink command: %s\n%v", strings.Join(args, " "), err)) - } - session.Wait(defaultWaitTimeout) - return &EndpointSession{session} -} - -func (s *EndpointSession) StdErrToString() string { - fields := strings.Fields(string(s.Err.Contents())) - return strings.Join(fields, " ") -} - -func (s *EndpointSession) OutputToString() string { - fields := strings.Fields(string(s.Out.Contents())) - return strings.Join(fields, " ") -} - -func (s *EndpointSession) OutputToBytes() []byte { - out := s.OutputToString() - return []byte(out) -} - -func (s *EndpointSession) OutputToStringMap() map[string]string { - var out map[string]string - json.Unmarshal(s.OutputToBytes(), &out) - return out -} - -func (s *EndpointSession) OutputToMapToInt() map[string]int { - var out map[string]int - json.Unmarshal(s.OutputToBytes(), &out) - return out -} - -func (s *EndpointSession) OutputToMoreResponse() iopodman.MoreResponse { - out := make(map[string]iopodman.MoreResponse) - json.Unmarshal(s.OutputToBytes(), &out) - return out["reply"] -} diff --git a/test/endpoint/endpoint_suite_test.go b/test/endpoint/endpoint_suite_test.go deleted file mode 100644 index 1cd5c2b9d..000000000 --- a/test/endpoint/endpoint_suite_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestEndpoint(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Endpoint Suite") -} - -var LockTmpDir string - -var _ = SynchronizedBeforeSuite(func() []byte { - // Cache images - cwd, _ := os.Getwd() - INTEGRATION_ROOT = filepath.Join(cwd, "../../") - podman := Setup("/tmp") - podman.ArtifactPath = ARTIFACT_DIR - if _, err := os.Stat(ARTIFACT_DIR); os.IsNotExist(err) { - if err = os.Mkdir(ARTIFACT_DIR, 0777); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - } - - // make cache dir - if err := os.MkdirAll(ImageCacheDir, 0777); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - - podman.StartVarlink() - for _, image := range CACHE_IMAGES { - podman.createArtifact(image) - } - podman.StopVarlink() - // If running localized tests, the cache dir is created and populated. if the - // tests are remote, this is a no-op - populateCache(podman) - - path, err := ioutil.TempDir("", "libpodlock") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - return []byte(path) -}, func(data []byte) { - LockTmpDir = string(data) -}) - -var _ = SynchronizedAfterSuite(func() {}, - func() { - podman := Setup("/tmp") - if err := os.RemoveAll(podman.CrioRoot); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - if err := os.RemoveAll(podman.ImageCacheDir); err != nil { - fmt.Printf("%q\n", err) - os.Exit(1) - } - }) diff --git a/test/endpoint/exists_test.go b/test/endpoint/exists_test.go deleted file mode 100644 index f7fa8eb44..000000000 --- a/test/endpoint/exists_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "os" - - . "github.com/containers/podman/v2/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman exists", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlinkWithCache() - }) - - AfterEach(func() { - endpointTest.Cleanup() - //f := CurrentGinkgoTestDescription() - //processTestResult(f) - - }) - - It("image exists in local storage", func() { - result := endpointTest.Varlink("ImageExists", makeNameMessage(ALPINE), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) - - It("image exists in local storage by shortname", func() { - result := endpointTest.Varlink("ImageExists", makeNameMessage("alpine"), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) - - It("image does not exist in local storage", func() { - result := endpointTest.Varlink("ImageExists", makeNameMessage("alpineforest"), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(Equal(1)) - }) - - It("container exists in local storage by name", func() { - _ = endpointTest.startTopContainer("top") - result := endpointTest.Varlink("ContainerExists", makeNameMessage("top"), false) - Expect(result.ExitCode()).To(BeZero()) - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) - -}) diff --git a/test/endpoint/pull_test.go b/test/endpoint/pull_test.go deleted file mode 100644 index b3683b7db..000000000 --- a/test/endpoint/pull_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "os" - - . "github.com/containers/podman/v2/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman pull", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlink() - }) - - AfterEach(func() { - endpointTest.Cleanup() - //f := CurrentGinkgoTestDescription() - //processTestResult(f) - - }) - - It("podman pull", func() { - session := endpointTest.Varlink("PullImage", makeNameMessage(ALPINE), false) - Expect(session.ExitCode()).To(BeZero()) - - result := endpointTest.Varlink("ImageExists", makeNameMessage(ALPINE), false) - Expect(result.ExitCode()).To(BeZero()) - - output := result.OutputToMapToInt() - Expect(output["exists"]).To(BeZero()) - }) -}) diff --git a/test/endpoint/setup.go b/test/endpoint/setup.go deleted file mode 100644 index 6bbc8d2bc..000000000 --- a/test/endpoint/setup.go +++ /dev/null @@ -1,214 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/containers/podman/v2/pkg/rootless" - iopodman "github.com/containers/podman/v2/pkg/varlink" - "github.com/containers/storage/pkg/stringid" - "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" -) - -func Setup(tempDir string) *EndpointTestIntegration { - var ( - endpoint string - ) - cwd, _ := os.Getwd() - INTEGRATION_ROOT = filepath.Join(cwd, "../../") - - podmanBinary := filepath.Join(cwd, "../../bin/podman") - if os.Getenv("PODMAN_BINARY") != "" { - podmanBinary = os.Getenv("PODMAN_BINARY") - } - conmonBinary := filepath.Join("/usr/libexec/podman/conmon") - altConmonBinary := "/usr/bin/conmon" - if _, err := os.Stat(conmonBinary); os.IsNotExist(err) { - conmonBinary = altConmonBinary - } - if os.Getenv("CONMON_BINARY") != "" { - conmonBinary = os.Getenv("CONMON_BINARY") - } - storageOptions := STORAGE_OPTIONS - if os.Getenv("STORAGE_OPTIONS") != "" { - storageOptions = os.Getenv("STORAGE_OPTIONS") - } - cgroupManager := CGROUP_MANAGER - if rootless.IsRootless() { - cgroupManager = "cgroupfs" - } - if os.Getenv("CGROUP_MANAGER") != "" { - cgroupManager = os.Getenv("CGROUP_MANAGER") - } - - ociRuntime := os.Getenv("OCI_RUNTIME") - if ociRuntime == "" { - ociRuntime = "runc" - } - os.Setenv("DISABLE_HC_SYSTEMD", "true") - CNIConfigDir := "/etc/cni/net.d" - - storageFs := STORAGE_FS - if rootless.IsRootless() { - storageFs = ROOTLESS_STORAGE_FS - } - - uuid := stringid.GenerateNonCryptoID() - if !rootless.IsRootless() { - endpoint = fmt.Sprintf("unix:/run/podman/io.podman-%s", uuid) - } else { - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - socket := fmt.Sprintf("io.podman-%s", uuid) - fqpath := filepath.Join(runtimeDir, socket) - endpoint = fmt.Sprintf("unix:%s", fqpath) - } - - eti := EndpointTestIntegration{ - ArtifactPath: ARTIFACT_DIR, - CNIConfigDir: CNIConfigDir, - CgroupManager: cgroupManager, - ConmonBinary: conmonBinary, - CrioRoot: filepath.Join(tempDir, "crio"), - ImageCacheDir: ImageCacheDir, - ImageCacheFS: storageFs, - OCIRuntime: ociRuntime, - PodmanBinary: podmanBinary, - RunRoot: filepath.Join(tempDir, "crio-run"), - SignaturePolicyPath: filepath.Join(INTEGRATION_ROOT, "test/policy.json"), - StorageOptions: storageOptions, - TmpDir: tempDir, - // Timings: nil, - VarlinkBinary: VarlinkBinary, - VarlinkCommand: nil, - VarlinkEndpoint: endpoint, - VarlinkSession: nil, - } - return &eti -} - -func (p *EndpointTestIntegration) Cleanup() { - // Remove all containers - // TODO Make methods to do all this? - - p.stopAllContainers() - - // TODO need to make stop all pods - - p.StopVarlink() - // Nuke tempdir - if err := os.RemoveAll(p.TmpDir); err != nil { - fmt.Printf("%q\n", err) - } - - // Clean up the registries configuration file ENV variable set in Create - resetRegistriesConfigEnv() -} - -func (p *EndpointTestIntegration) listContainers() []iopodman.Container { - containers := p.Varlink("ListContainers", "", false) - var varlinkContainers map[string][]iopodman.Container - if err := json.Unmarshal(containers.OutputToBytes(), &varlinkContainers); err != nil { - logrus.Error("failed to unmarshal containers") - } - return varlinkContainers["containers"] -} - -func (p *EndpointTestIntegration) stopAllContainers() { - containers := p.listContainers() - for _, container := range containers { - p.stopContainer(container.Id) - } -} - -func (p *EndpointTestIntegration) stopContainer(cid string) { - p.Varlink("StopContainer", fmt.Sprintf("{\"name\":\"%s\", \"timeout\":0}", cid), false) -} - -func resetRegistriesConfigEnv() { - os.Setenv("REGISTRIES_CONFIG_PATH", "") -} - -func (p *EndpointTestIntegration) createArtifact(image string) { - if os.Getenv("NO_TEST_CACHE") != "" { - return - } - dest := strings.Split(image, "/") - destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1)) - fmt.Printf("Caching %s at %s...", image, destName) - if _, err := os.Stat(destName); os.IsNotExist(err) { - pull := p.Varlink("PullImage", fmt.Sprintf("{\"name\":\"%s\"}", image), false) - Expect(pull.ExitCode()).To(Equal(0)) - - imageSave := iopodman.ImageSaveOptions{ - // Name:image, - // Output: destName, - // Format: "oci-archive", - } - imageSave.Name = image - imageSave.Output = destName - imageSave.Format = "oci-archive" - foo := make(map[string]iopodman.ImageSaveOptions) - foo["options"] = imageSave - f, _ := json.Marshal(foo) - save := p.Varlink("ImageSave", string(f), false) - result := save.OutputToMoreResponse() - Expect(save.ExitCode()).To(Equal(0)) - Expect(os.Rename(result.Id, destName)).To(BeNil()) - fmt.Printf("\n") - } else { - fmt.Printf(" already exists.\n") - } -} - -func populateCache(p *EndpointTestIntegration) { - p.CrioRoot = p.ImageCacheDir - p.StartVarlink() - for _, image := range CACHE_IMAGES { - p.RestoreArtifactToCache(image) - } - p.StopVarlink() -} - -func (p *EndpointTestIntegration) RestoreArtifactToCache(image string) error { - fmt.Printf("Restoring %s...\n", image) - dest := strings.Split(image, "/") - destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1)) - // fmt.Println(destName, p.ImageCacheDir) - load := p.Varlink("LoadImage", fmt.Sprintf("{\"name\": \"%s\", \"inputFile\": \"%s\"}", image, destName), false) - Expect(load.ExitCode()).To(BeZero()) - return nil -} - -func (p *EndpointTestIntegration) startTopContainer(name string) string { - t := true - args := iopodman.Create{ - Args: []string{"docker.io/library/alpine:latest", "top"}, - Tty: &t, - Detach: &t, - } - if len(name) > 0 { - args.Name = &name - } - b, err := json.Marshal(args) - if err != nil { - ginkgo.Fail("failed to marshal data for top container") - } - input := fmt.Sprintf("{\"create\":%s}", string(b)) - top := p.Varlink("CreateContainer", input, false) - if top.ExitCode() != 0 { - ginkgo.Fail("failed to start top container") - } - start := p.Varlink("StartContainer", fmt.Sprintf("{\"name\":\"%s\"}", name), false) - if start.ExitCode() != 0 { - ginkgo.Fail("failed to start top container") - } - return start.OutputToString() -} diff --git a/test/endpoint/version_test.go b/test/endpoint/version_test.go deleted file mode 100644 index b1c8ad867..000000000 --- a/test/endpoint/version_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// +build varlink - -package endpoint - -import ( - "os" - - . "github.com/containers/podman/v2/test/utils" - "github.com/containers/podman/v2/version" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman version", func() { - var ( - tempdir string - err error - endpointTest *EndpointTestIntegration - ) - - BeforeEach(func() { - tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - endpointTest = Setup(tempdir) - endpointTest.StartVarlink() - }) - - AfterEach(func() { - endpointTest.Cleanup() - //f := CurrentGinkgoTestDescription() - //processTestResult(f) - - }) - - It("podman version", func() { - session := endpointTest.Varlink("GetVersion", "", false) - result := session.OutputToStringMap() - Expect(result["version"]).To(Equal(version.Version)) - Expect(session.ExitCode()).To(Equal(0)) - }) -}) diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 98bb0cc57..ee6da30ec 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -59,7 +59,8 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z @test "podman images - history output" { # podman history is persistent: it permanently alters our base image. # Create a dummy image here so we leave our setup as we found it. - run_podman run --name my-container $IMAGE true + # Multiple --name options confirm command-line override (last one wins) + run_podman run --name ignore-me --name my-container $IMAGE true run_podman commit my-container my-test-image run_podman images my-test-image --format '{{ .History }}' @@ -87,7 +88,8 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z } @test "podman images - filter" { - run_podman inspect --format '{{.ID}}' $IMAGE + # Multiple --format options confirm command-line override (last one wins) + run_podman inspect --format '{{.XYZ}}' --format '{{.ID}}' $IMAGE iid=$output run_podman images --noheading --filter=after=$iid diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 12df966e2..37695f205 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -449,7 +449,9 @@ json-file | f msg=$(random_string 20) pidfile="${PODMAN_TMPDIR}/$(random_string 20)" - run_podman run --name myctr --log-driver journald --conmon-pidfile $pidfile $IMAGE echo $msg + # Multiple --log-driver options to confirm that last one wins + run_podman run --name myctr --log-driver=none --log-driver journald \ + --conmon-pidfile $pidfile $IMAGE echo $msg journalctl --output cat _PID=$(cat $pidfile) is "$output" "$msg" "check that journalctl output equals the container output" @@ -464,7 +466,9 @@ json-file | f run_podman run --rm $IMAGE date -r $testfile is "$output" "Sun Sep 13 12:26:40 UTC 2020" "podman run with no TZ" - run_podman run --rm --tz=MST7MDT $IMAGE date -r $testfile + # Multiple --tz options; confirm that the last one wins + run_podman run --rm --tz=US/Eastern --tz=Iceland --tz=MST7MDT \ + $IMAGE date -r $testfile is "$output" "Sun Sep 13 06:26:40 MDT 2020" "podman run with --tz=MST7MDT" # --tz=local pays attention to /etc/localtime, not $TZ. We set TZ anyway, @@ -532,4 +536,16 @@ json-file | f run_podman untag $IMAGE $newtag $newtag2 } +@test "podman run with --net=host and --port prints warning" { + rand=$(random_string 10) + + # Please keep the duplicate "--net" options; this tests against #8507, + # a regression in which subsequent --net options did not override earlier. + run_podman run --rm -p 8080 --net=none --net=host $IMAGE echo $rand + is "${lines[0]}" \ + "Port mappings have been discarded as one of the Host, Container, Pod, and None network modes are in use" \ + "Warning is emitted before container output" + is "${lines[1]}" "$rand" "Container runs successfully despite warning" +} + # vim: filetype=sh diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index dec2df4d5..1ed2779b2 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -35,4 +35,51 @@ load helpers run_podman rm $cid } +@test "podman ps --filter" { + run_podman run -d --name runner $IMAGE top + cid_runner=$output + + run_podman run -d --name stopped $IMAGE true + cid_stopped=$output + run_podman wait stopped + + run_podman run -d --name failed $IMAGE false + cid_failed=$output + run_podman wait failed + + run_podman create --name created $IMAGE echo hi + cid_created=$output + + run_podman ps --filter name=runner --format '{{.ID}}' + is "$output" "${cid_runner:0:12}" "filter: name=runner" + + # Stopped container should not appear (because we're not using -a) + run_podman ps --filter name=stopped --format '{{.ID}}' + is "$output" "" "filter: name=stopped (without -a)" + + # Again, but with -a + run_podman ps -a --filter name=stopped --format '{{.ID}}' + is "$output" "${cid_stopped:0:12}" "filter: name=stopped (with -a)" + + run_podman ps --filter status=stopped --format '{{.Names}}' --sort names + is "${lines[0]}" "failed" "status=stopped: 1 of 2" + is "${lines[1]}" "stopped" "status=stopped: 2 of 2" + + run_podman ps --filter status=exited --filter exited=0 --format '{{.Names}}' + is "$output" "stopped" "exited=0" + + run_podman ps --filter status=exited --filter exited=1 --format '{{.Names}}' + is "$output" "failed" "exited=1" + + # Multiple statuses allowed; and test sort=created + run_podman ps -a --filter status=exited --filter status=running \ + --format '{{.Names}}' --sort created + is "${lines[0]}" "runner" "status=stopped: 1 of 3" + is "${lines[1]}" "stopped" "status=stopped: 2 of 3" + is "${lines[2]}" "failed" "status=stopped: 3 of 3" + + run_podman stop -t 1 runner + run_podman rm -a +} + # vim: filetype=sh diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 83bcd13eb..59da503a6 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -135,10 +135,13 @@ echo "\$1" printenv | grep MYENV | sort | sed -e 's/^MYENV.=//' EOF - # For overriding with --env-file - cat >$PODMAN_TMPDIR/env-file <<EOF + # For overriding with --env-file; using multiple files confirms that + # the --env-file option is cumulative, not last-one-wins. + cat >$PODMAN_TMPDIR/env-file1 <<EOF MYENV3=$s_env3 http_proxy=http-proxy-in-env-file +EOF + cat >$PODMAN_TMPDIR/env-file2 <<EOF https_proxy=https-proxy-in-env-file EOF @@ -185,7 +188,8 @@ EOF export MYENV2="$s_env2" export MYENV3="env-file-should-override-env-host!" run_podman run --rm \ - --env-file=$PODMAN_TMPDIR/env-file \ + --env-file=$PODMAN_TMPDIR/env-file1 \ + --env-file=$PODMAN_TMPDIR/env-file2 \ ${ENVHOST} \ -e MYENV4="$s_env4" \ build_test @@ -205,7 +209,9 @@ EOF # Proxies - environment should override container, but not env-file http_proxy=http-proxy-from-env ftp_proxy=ftp-proxy-from-env \ - run_podman run --rm --env-file=$PODMAN_TMPDIR/env-file \ + run_podman run --rm \ + --env-file=$PODMAN_TMPDIR/env-file1 \ + --env-file=$PODMAN_TMPDIR/env-file2 \ build_test \ printenv http_proxy https_proxy ftp_proxy is "${lines[0]}" "http-proxy-in-env-file" "env-file overrides env" @@ -222,7 +228,8 @@ EOF is "$output" "$workdir" "pwd command in container" # Determine buildah version, so we can confirm it gets into Labels - run_podman info --format '{{ .Host.BuildahVersion }}' + # Multiple --format options confirm command-line override (last one wins) + run_podman info --format '{{.Ignore}}' --format '{{ .Host.BuildahVersion }}' is "$output" "[1-9][0-9.-]\+" ".Host.BuildahVersion is reasonable" buildah_version=$output diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats index edd7dedc4..c028e16c9 100644 --- a/test/system/075-exec.bats +++ b/test/system/075-exec.bats @@ -91,7 +91,8 @@ load helpers # #6829 : add username to /etc/passwd inside container if --userns=keep-id @test "podman exec - with keep-id" { - run_podman run -d --userns=keep-id $IMAGE sh -c \ + # Multiple --userns options confirm command-line override (last one wins) + run_podman run -d --userns=private --userns=keep-id $IMAGE sh -c \ "echo READY;while [ ! -f /tmp/stop ]; do sleep 1; done" cid="$output" wait_for_ready $cid diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index b0f645c53..51835e4a3 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -286,6 +286,10 @@ EOF is "$output" "nc: bind: Address in use" \ "two containers cannot bind to same port" + # make sure we can ping; failure here might mean that capabilities are wrong + run_podman run --rm --pod mypod $IMAGE ping -c1 127.0.0.1 + run_podman run --rm --pod mypod $IMAGE ping -c1 $hostname + # While the container is still running, run 'podman ps' (no --format) # and confirm that the output includes the published port run_podman ps --filter id=$cid diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go index 9ab47e60c..10e3f17ed 100644 --- a/vendor/github.com/containers/buildah/buildah.go +++ b/vendor/github.com/containers/buildah/buildah.go @@ -28,7 +28,7 @@ const ( Package = "buildah" // Version for the Package. Bump version in contrib/rpm/buildah.spec // too. - Version = "1.18.0" + Version = "1.19.0-dev" // The value we use to identify what type of information, currently a // serialized Builder structure, we are using as per-container state. // This should only be changed when we make incompatible changes to diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod index b1f3ad67a..0d795f6b6 100644 --- a/vendor/github.com/containers/buildah/go.mod +++ b/vendor/github.com/containers/buildah/go.mod @@ -5,12 +5,11 @@ go 1.12 require ( github.com/containerd/containerd v1.4.1 // indirect github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 - github.com/containers/common v0.26.3 - github.com/containers/image/v5 v5.8.0 + github.com/containers/common v0.29.0 + github.com/containers/image/v5 v5.8.1 github.com/containers/ocicrypt v1.0.3 - github.com/containers/storage v1.24.0 + github.com/containers/storage v1.24.1 github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v17.12.0-ce-rc1.0.20201020191947-73dc6a680cdd+incompatible // indirect github.com/docker/go-units v0.4.0 github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 github.com/fsouza/go-dockerclient v1.6.6 diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum index 069328c38..e3413bc68 100644 --- a/vendor/github.com/containers/buildah/go.sum +++ b/vendor/github.com/containers/buildah/go.sum @@ -73,20 +73,17 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc= github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containers/common v0.26.3 h1:5Kb5fMmJ7/xMiJ+iEbPA+5pQpl/FGxCgJex4nml4Slo= -github.com/containers/common v0.26.3/go.mod h1:hJWZIlrl5MsE2ELNRa+MPp6I1kPbXHauuj0Ym4BsLG4= -github.com/containers/image/v5 v5.7.0 h1:fiTC8/Xbr+zEP6njGTZtPW/3UD7MC93nC9DbUoWdxkA= -github.com/containers/image/v5 v5.7.0/go.mod h1:8aOy+YaItukxghRORkvhq5ibWttHErzDLy6egrKfKos= -github.com/containers/image/v5 v5.8.0 h1:B3FGHi0bdGXgg698kBIGOlHCXN5n+scJr6/5354GOPU= -github.com/containers/image/v5 v5.8.0/go.mod h1:jKxdRtyIDumVa56hdsZvV+gwx4zB50hRou6pIuCWLkg= +github.com/containers/common v0.29.0 h1:hTMC+urdkk5bKfhL/OgCixIX5xjJgQ2l2jPG745ECFQ= +github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA= +github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q= +github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c= github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g= -github.com/containers/storage v1.23.6/go.mod h1:haFs0HRowKwyzvWEx9EgI3WsL8XCSnBDb5f8P5CAxJY= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= -github.com/containers/storage v1.24.0 h1:Fo2LkF7tkMLmo38sTZ/G8wHjcn8JfUFPfyTxM4WwMfk= -github.com/containers/storage v1.24.0/go.mod h1:A4d3BzuZK9b3oLVEsiSRhZLPIx3z7utgiPyXLK/YMhY= +github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc= +github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -232,8 +229,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= -github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= @@ -278,7 +275,6 @@ github.com/moby/sys/mount v0.1.1 h1:mdhBytJ1SMmMat0gtzWWjFX/87K5j6E/7Q5z7rR0cZY= github.com/moby/sys/mount v0.1.1/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= -github.com/moby/sys/mountinfo v0.3.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/term v0.0.0-20200429084858-129dac9f73f6/go.mod h1:or9wGItza1sRcM4Wd3dIv8DsFHYQuFsMHEdxUIlUxms= diff --git a/vendor/github.com/containers/buildah/image.go b/vendor/github.com/containers/buildah/image.go index 154bc503f..7c55020ab 100644 --- a/vendor/github.com/containers/buildah/image.go +++ b/vendor/github.com/containers/buildah/image.go @@ -321,10 +321,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System } // If we're not re-exporting the data, and we're reusing layers individually, reuse // the blobsum and diff IDs. - if !i.exporting && !i.squash && layerID != i.layerID { - if layer.UncompressedDigest == "" { - return nil, errors.Errorf("unable to look up size of layer %q", layerID) - } + if !i.exporting && !i.squash && layerID != i.layerID && layer.UncompressedDigest != "" { layerBlobSum := layer.UncompressedDigest layerBlobSize := layer.UncompressedSize diffID := layer.UncompressedDigest diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index a97a403b3..1ec21e786 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -185,6 +185,8 @@ type BuildOptions struct { Jobs *int // LogRusage logs resource usage for each step. LogRusage bool + // Excludes is a list of excludes to be used instead of the .dockerignore file. + Excludes []string } // BuildDockerfiles parses a set of one or more Dockerfiles (which may be diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go index 8c96b4e67..3c41ec1d2 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go @@ -130,9 +130,12 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod return nil, errors.Wrapf(err, "failed to get container config") } - excludes, err := imagebuilder.ParseDockerignore(options.ContextDirectory) - if err != nil { - return nil, err + excludes := options.Excludes + if len(excludes) == 0 { + excludes, err = imagebuilder.ParseDockerignore(options.ContextDirectory) + if err != nil { + return nil, err + } } capabilities, err := defaultContainerConfig.Capabilities("", options.AddCapabilities, options.DropCapabilities) if err != nil { diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go index 6c058e226..191645b89 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go @@ -368,6 +368,7 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error { Stderr: s.executor.err, Quiet: s.executor.quiet, NamespaceOptions: s.executor.namespaceOptions, + Terminal: buildah.WithoutTerminal, } if config.NetworkDisabled { options.ConfigureNetwork = buildah.NetworkDisabled @@ -1144,7 +1145,11 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p // lines in the Dockerfile up till the point we are at in the build. manifestType, history, diffIDs, err := s.executor.getImageTypeAndHistoryAndDiffIDs(ctx, image.ID) if err != nil { - return "", errors.Wrapf(err, "error getting history of %q", image.ID) + // It's possible that this image is for another architecture, which results + // in a custom-crafted error message that we'd have to use substring matching + // to recognize. Instead, ignore the image. + logrus.Debugf("error getting history of %q (%v), ignoring it", image.ID, err) + continue } // If this candidate isn't of the type that we're building, then it may have lost // some format-specific information that a building-without-cache run wouldn't lose. diff --git a/vendor/github.com/containers/buildah/install.md b/vendor/github.com/containers/buildah/install.md index 00381e16a..119315d1f 100644 --- a/vendor/github.com/containers/buildah/install.md +++ b/vendor/github.com/containers/buildah/install.md @@ -59,35 +59,31 @@ sudo dnf -y install buildah #### [Debian](https://debian.org) -The buildah package is [being worked on](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=928083) -for inclusion in the default Debian repos. - -Alternatively, the [Kubic project](https://build.opensuse.org/project/show/devel:kubic:libcontainers:stable) -provides packages for Debian 10, testing and unstable. +The buildah package is available in +the [Bullseye (testing) branch](https://packages.debian.org/bullseye/buildah), which +will be the next stable release (Debian 11) as well as Debian Unstable/Sid. ```bash -# Debian Unstable/Sid -echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list -wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Debian_Unstable/Release.key -O Release.key +# Debian Testing/Bullseye or Unstable/Sid +sudo apt-get update +sudo apt-get -y install buildah +``` -# Debian Testing -echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list -wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Debian_Testing/Release.key -O Release.key +The [Kubic project](https://build.opensuse.org/project/show/devel:kubic:libcontainers:stable) +provides packages for Debian 10. The Kubic packages for Debian Testing/Bullseye and Debian Unstable/Sid +have been discontinued to avoid +[conflicts](https://github.com/containers/buildah/issues/2797) with the official packages. -# Debian 10 -echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list -wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Debian_10/Release.key -O Release.key +Caution: If you upgrade from Debian 10 to Testing/Bullseye or +Unstable/Sid you would likely end up downgrading Buildah because the version in +OBS is more frequently updated than the one in Debian's official repositories, +due to how Debian works. -sudo apt-key add - < Release.key -sudo apt-get update -qq -sudo apt-get -qq -y install buildah -``` - -### [Fedora](https://www.fedoraproject.org), [CentOS](https://www.centos.org) +### [Fedora](https://www.fedoraproject.org) ```bash -sudo yum -y install buildah +sudo dnf -y install buildah ``` ### [Fedora SilverBlue](https://silverblue.fedoraproject.org) @@ -127,19 +123,25 @@ sudo subscription-manager repos --enable=rhel-7-server-extras-rpms sudo yum -y install buildah ``` -#### [Raspbian](https://raspbian.org) +#### [Raspberry Pi OS armhf (ex Raspbian)](https://www.raspberrypi.org/downloads/raspberry-pi-os/) The Kubic project provides packages for Raspbian 10. ```bash # Raspbian 10 -echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Raspbian_10/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list -wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Raspbian_10/Release.key -O Release.key -sudo apt-key add - < Release.key +echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Raspbian_10/ /' | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list +curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Raspbian_10/Release.key | sudo apt-key add - sudo apt-get update -qq sudo apt-get -qq -y install buildah ``` +#### [Raspberry Pi OS arm64 (beta)](https://downloads.raspberrypi.org/raspios_arm64/images/) + +Raspberry Pi OS use the standard Debian's repositories, +so it is fully compatible with Debian's arm64 repository. +You can simply follow the [steps for Debian](#debian) to install podman. + + ### [RHEL8 Beta](https://www.redhat.com/en/blog/powering-its-future-while-preserving-present-introducing-red-hat-enterprise-linux-8-beta?intcmp=701f2000001Cz6OAAS) ```bash @@ -149,7 +151,16 @@ sudo yum module install -y buildah ### [Ubuntu](https://www.ubuntu.com) -The Kubic project provides packages for Ubuntu 18.04, 19.04 and 19.10 (it should also work with direct derivatives like Pop!\_OS). +The buildah package is available in the official repositories for Ubuntu 20.10 +and newer. + +```bash +# Ubuntu 20.10 and newer +sudo apt-get -y update +sudo apt-get -y install buildah +``` + +The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides packages for some older but supported Ubuntu versions (it should also work with direct derivatives like Pop!\_OS). ```bash . /etc/os-release diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go index 62a328de0..123548d97 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/common.go +++ b/vendor/github.com/containers/buildah/pkg/cli/common.go @@ -59,6 +59,7 @@ type BudResults struct { Creds string DisableCompression bool DisableContentTrust bool + IgnoreFile string File []string Format string Iidfile string @@ -185,6 +186,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry") fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default") fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "This is a Docker specific option and is a NOOP") + fs.StringVar(&flags.IgnoreFile, "ignorefile", "", "path to an alternate .dockerignore file") fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile") fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.") fs.StringVar(&flags.Iidfile, "iidfile", "", "`file` to write the image ID to") @@ -231,6 +233,7 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["creds"] = commonComp.AutocompleteNone flagCompletion["file"] = commonComp.AutocompleteDefault flagCompletion["format"] = commonComp.AutocompleteNone + flagCompletion["ignorefile"] = commonComp.AutocompleteDefault flagCompletion["iidfile"] = commonComp.AutocompleteDefault flagCompletion["jobs"] = commonComp.AutocompleteNone flagCompletion["label"] = commonComp.AutocompleteNone diff --git a/vendor/github.com/containers/buildah/pkg/umask/umask_unsupported.go b/vendor/github.com/containers/buildah/pkg/umask/umask_unsupported.go deleted file mode 100644 index 20913a718..000000000 --- a/vendor/github.com/containers/buildah/pkg/umask/umask_unsupported.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !linux,!darwin - -package umask - -func CheckUmask() {} - -func SetUmask(int) int { return 0 } diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index d907941ed..d20d39423 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -25,10 +25,10 @@ import ( "github.com/containers/buildah/chroot" "github.com/containers/buildah/copier" "github.com/containers/buildah/pkg/overlay" - "github.com/containers/buildah/pkg/secrets" "github.com/containers/buildah/util" "github.com/containers/common/pkg/capabilities" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/subscriptions" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/reexec" @@ -216,16 +216,28 @@ func (b *Builder) Run(command []string, options RunOptions) error { } // Empty file, so no need to recreate if it exists if _, ok := bindFiles["/run/.containerenv"]; !ok { - // Empty string for now, but we may consider populating this later containerenvPath := filepath.Join(path, "/run/.containerenv") if err = os.MkdirAll(filepath.Dir(containerenvPath), 0755); err != nil { return err } - emptyFile, err := os.Create(containerenvPath) - if err != nil { + + rootless := 0 + if unshare.IsRootless() { + rootless = 1 + } + // Populate the .containerenv with container information + containerenv := fmt.Sprintf(`\ +engine="buildah-%s" +name=%q +id=%q +image=%q +imageid=%q +rootless=%d +`, Version, b.Container, b.ContainerID, b.FromImage, b.FromImageID, rootless) + + if err = ioutils.AtomicWriteFile(containerenvPath, []byte(containerenv), 0755); err != nil { return err } - emptyFile.Close() if err := label.Relabel(containerenvPath, b.MountLabel, false); err != nil { return err } @@ -477,15 +489,15 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st return errors.Wrapf(err, "error determining work directory for container %q", b.ContainerID) } - // Figure out which UID and GID to tell the secrets package to use + // Figure out which UID and GID to tell the subscritions package to use // for files that it creates. rootUID, rootGID, err := util.GetHostRootIDs(spec) if err != nil { return err } - // Get the list of secrets mounts. - secretMounts := secrets.SecretMountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, mountPoint, int(rootUID), int(rootGID), unshare.IsRootless(), false) + // Get the list of subscriptionss mounts. + secretMounts := subscriptions.MountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, mountPoint, int(rootUID), int(rootGID), unshare.IsRootless(), false) // Add temporary copies of the contents of volume locations at the // volume locations, unless we already have something there. diff --git a/vendor/github.com/containers/common/pkg/subscriptions/mounts.conf b/vendor/github.com/containers/common/pkg/subscriptions/mounts.conf new file mode 100644 index 000000000..b7cde9d8a --- /dev/null +++ b/vendor/github.com/containers/common/pkg/subscriptions/mounts.conf @@ -0,0 +1 @@ +/usr/share/rhel/secrets:/run/secrets diff --git a/vendor/github.com/containers/buildah/pkg/secrets/secrets.go b/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go index 32f888fa8..6aa66b0c8 100644 --- a/vendor/github.com/containers/buildah/pkg/secrets/secrets.go +++ b/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go @@ -1,4 +1,4 @@ -package secrets +package subscriptions import ( "bufio" @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - "github.com/containers/buildah/pkg/umask" + "github.com/containers/common/pkg/umask" "github.com/containers/storage/pkg/idtools" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" @@ -27,16 +27,16 @@ var ( UserOverrideMountsFile = filepath.Join(os.Getenv("HOME"), ".config/containers/mounts.conf") ) -// secretData stores the name of the file and the content read from it -type secretData struct { +// subscriptionData stores the name of the file and the content read from it +type subscriptionData struct { name string data []byte mode os.FileMode dirMode os.FileMode } -// saveTo saves secret data to given directory -func (s secretData) saveTo(dir string) error { +// saveTo saves subscription data to given directory +func (s subscriptionData) saveTo(dir string) error { path := filepath.Join(dir, s.name) if err := os.MkdirAll(filepath.Dir(path), s.dirMode); err != nil { return err @@ -44,10 +44,10 @@ func (s secretData) saveTo(dir string) error { return ioutil.WriteFile(path, s.data, s.mode) } -func readAll(root, prefix string, parentMode os.FileMode) ([]secretData, error) { +func readAll(root, prefix string, parentMode os.FileMode) ([]subscriptionData, error) { path := filepath.Join(root, prefix) - data := []secretData{} + data := []subscriptionData{} files, err := ioutil.ReadDir(path) if err != nil { @@ -74,7 +74,7 @@ func readAll(root, prefix string, parentMode os.FileMode) ([]secretData, error) return data, nil } -func readFileOrDir(root, name string, parentMode os.FileMode) ([]secretData, error) { +func readFileOrDir(root, name string, parentMode os.FileMode) ([]subscriptionData, error) { path := filepath.Join(root, name) s, err := os.Stat(path) @@ -93,7 +93,7 @@ func readFileOrDir(root, name string, parentMode os.FileMode) ([]secretData, err if err != nil { return nil, err } - return []secretData{{ + return []subscriptionData{{ name: name, data: bytes, mode: s.Mode(), @@ -101,13 +101,13 @@ func readFileOrDir(root, name string, parentMode os.FileMode) ([]secretData, err }}, nil } -func getHostSecretData(hostDir string, mode os.FileMode) ([]secretData, error) { - var allSecrets []secretData - hostSecrets, err := readAll(hostDir, "", mode) +func getHostSubscriptionData(hostDir string, mode os.FileMode) ([]subscriptionData, error) { + var allSubscriptions []subscriptionData + hostSubscriptions, err := readAll(hostDir, "", mode) if err != nil { - return nil, errors.Wrapf(err, "failed to read secrets from %q", hostDir) + return nil, errors.Wrapf(err, "failed to read subscriptions from %q", hostDir) } - return append(allSecrets, hostSecrets...), nil + return append(allSubscriptions, hostSubscriptions...), nil } func getMounts(filePath string) []string { @@ -136,7 +136,7 @@ func getMounts(filePath string) []string { } // getHostAndCtrDir separates the host:container paths -func getMountsMap(path string) (string, string, error) { +func getMountsMap(path string) (string, string, error) { //nolint arr := strings.SplitN(path, ":", 2) switch len(arr) { case 1: @@ -147,27 +147,21 @@ func getMountsMap(path string) (string, string, error) { return "", "", errors.Errorf("unable to get host and container dir from path: %s", path) } -// SecretMounts copies, adds, and mounts the secrets to the container root filesystem -// Deprecated, Please use SecretMountWithUIDGID -func SecretMounts(mountLabel, containerWorkingDir, mountFile string, rootless, disableFips bool) []rspec.Mount { - return SecretMountsWithUIDGID(mountLabel, containerWorkingDir, mountFile, containerWorkingDir, 0, 0, rootless, disableFips) -} - -// SecretMountsWithUIDGID copies, adds, and mounts the secrets to the container root filesystem +// MountsWithUIDGID copies, adds, and mounts the subscriptions to the container root filesystem // mountLabel: MAC/SELinux label for container content -// containerWorkingDir: Private data for storing secrets on the host mounted in container. +// containerWorkingDir: Private data for storing subscriptions on the host mounted in container. // mountFile: Additional mount points required for the container. // mountPoint: Container image mountpoint -// uid: to assign to content created for secrets -// gid: to assign to content created for secrets +// uid: to assign to content created for subscriptions +// gid: to assign to content created for subscriptions // rootless: indicates whether container is running in rootless mode // disableFips: indicates whether system should ignore fips mode -func SecretMountsWithUIDGID(mountLabel, containerWorkingDir, mountFile, mountPoint string, uid, gid int, rootless, disableFips bool) []rspec.Mount { +func MountsWithUIDGID(mountLabel, containerWorkingDir, mountFile, mountPoint string, uid, gid int, rootless, disableFips bool) []rspec.Mount { var ( - secretMounts []rspec.Mount - mountFiles []string + subscriptionMounts []rspec.Mount + mountFiles []string ) - // Add secrets from paths given in the mounts.conf files + // Add subscriptions from paths given in the mounts.conf files // mountFile will have a value if the hidden --default-mounts-file flag is set // Note for testing purposes only if mountFile == "" { @@ -180,31 +174,32 @@ func SecretMountsWithUIDGID(mountLabel, containerWorkingDir, mountFile, mountPoi } for _, file := range mountFiles { if _, err := os.Stat(file); err == nil { - mounts, err := addSecretsFromMountsFile(file, mountLabel, containerWorkingDir, uid, gid) + mounts, err := addSubscriptionsFromMountsFile(file, mountLabel, containerWorkingDir, uid, gid) if err != nil { - logrus.Warnf("error mounting secrets, skipping entry in %s: %v", file, err) + logrus.Warnf("error mounting subscriptions, skipping entry in %s: %v", file, err) } - secretMounts = mounts + subscriptionMounts = mounts break } } - // Only add FIPS secret mount if disableFips=false + // Only add FIPS subscription mount if disableFips=false if disableFips { - return secretMounts + return subscriptionMounts } - // Add FIPS mode secret if /etc/system-fips exists on the host + // Add FIPS mode subscription if /etc/system-fips exists on the host _, err := os.Stat("/etc/system-fips") - if err == nil { - if err := addFIPSModeSecret(&secretMounts, containerWorkingDir, mountPoint, mountLabel, uid, gid); err != nil { - logrus.Errorf("error adding FIPS mode secret to container: %v", err) + switch { + case err == nil: + if err := addFIPSModeSubscription(&subscriptionMounts, containerWorkingDir, mountPoint, mountLabel, uid, gid); err != nil { + logrus.Errorf("error adding FIPS mode subscription to container: %v", err) } - } else if os.IsNotExist(err) { - logrus.Debug("/etc/system-fips does not exist on host, not mounting FIPS mode secret") - } else { - logrus.Errorf("stat /etc/system-fips failed for FIPS mode secret: %v", err) + case os.IsNotExist(err): + logrus.Debug("/etc/system-fips does not exist on host, not mounting FIPS mode subscription") + default: + logrus.Errorf("stat /etc/system-fips failed for FIPS mode subscription: %v", err) } - return secretMounts + return subscriptionMounts } func rchown(chowndir string, uid, gid int) error { @@ -213,9 +208,9 @@ func rchown(chowndir string, uid, gid int) error { }) } -// addSecretsFromMountsFile copies the contents of host directory to container directory +// addSubscriptionsFromMountsFile copies the contents of host directory to container directory // and returns a list of mounts -func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string, uid, gid int) ([]rspec.Mount, error) { +func addSubscriptionsFromMountsFile(filePath, mountLabel, containerWorkingDir string, uid, gid int) ([]rspec.Mount, error) { var mounts []rspec.Mount defaultMountsPaths := getMounts(filePath) for _, path := range defaultMountsPaths { @@ -235,7 +230,7 @@ func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string, ctrDirOrFileOnHost := filepath.Join(containerWorkingDir, ctrDirOrFile) - // In the event of a restart, don't want to copy secrets over again as they already would exist in ctrDirOrFileOnHost + // In the event of a restart, don't want to copy subscriptions over again as they already would exist in ctrDirOrFileOnHost _, err = os.Stat(ctrDirOrFileOnHost) if os.IsNotExist(err) { @@ -245,17 +240,17 @@ func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string, } // Don't let the umask have any influence on the file and directory creation - oldUmask := umask.SetUmask(0) - defer umask.SetUmask(oldUmask) + oldUmask := umask.Set(0) + defer umask.Set(oldUmask) switch mode := fileInfo.Mode(); { case mode.IsDir(): if err = os.MkdirAll(ctrDirOrFileOnHost, mode.Perm()); err != nil { return nil, errors.Wrapf(err, "making container directory %q failed", ctrDirOrFileOnHost) } - data, err := getHostSecretData(hostDirOrFile, mode.Perm()) + data, err := getHostSubscriptionData(hostDirOrFile, mode.Perm()) if err != nil { - return nil, errors.Wrapf(err, "getting host secret data failed") + return nil, errors.Wrapf(err, "getting host subscription data failed") } for _, s := range data { if err := s.saveTo(ctrDirOrFileOnHost); err != nil { @@ -305,15 +300,15 @@ func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir string, return mounts, nil } -// addFIPSModeSecret creates /run/secrets/system-fips in the container +// addFIPSModeSubscription creates /run/secrets/system-fips in the container // root filesystem if /etc/system-fips exists on hosts. // This enables the container to be FIPS compliant and run openssl in // FIPS mode as the host is also in FIPS mode. -func addFIPSModeSecret(mounts *[]rspec.Mount, containerWorkingDir, mountPoint, mountLabel string, uid, gid int) error { - secretsDir := "/run/secrets" - ctrDirOnHost := filepath.Join(containerWorkingDir, secretsDir) +func addFIPSModeSubscription(mounts *[]rspec.Mount, containerWorkingDir, mountPoint, mountLabel string, uid, gid int) error { + subscriptionsDir := "/run/secrets" + ctrDirOnHost := filepath.Join(containerWorkingDir, subscriptionsDir) if _, err := os.Stat(ctrDirOnHost); os.IsNotExist(err) { - if err = idtools.MkdirAllAs(ctrDirOnHost, 0755, uid, gid); err != nil { + if err = idtools.MkdirAllAs(ctrDirOnHost, 0755, uid, gid); err != nil { //nolint return errors.Wrapf(err, "making container directory %q on host failed", ctrDirOnHost) } if err = label.Relabel(ctrDirOnHost, mountLabel, false); err != nil { @@ -330,10 +325,10 @@ func addFIPSModeSecret(mounts *[]rspec.Mount, containerWorkingDir, mountPoint, m defer file.Close() } - if !mountExists(*mounts, secretsDir) { + if !mountExists(*mounts, subscriptionsDir) { m := rspec.Mount{ Source: ctrDirOnHost, - Destination: secretsDir, + Destination: subscriptionsDir, Type: "bind", Options: []string{"bind", "rprivate"}, } diff --git a/vendor/github.com/containers/buildah/pkg/umask/umask_unix.go b/vendor/github.com/containers/common/pkg/umask/umask_unix.go index 02e10945b..bb589f7ac 100644 --- a/vendor/github.com/containers/buildah/pkg/umask/umask_unix.go +++ b/vendor/github.com/containers/common/pkg/umask/umask_unix.go @@ -8,13 +8,13 @@ import ( "github.com/sirupsen/logrus" ) -func CheckUmask() { - oldUmask := syscall.Umask(0022) +func Check() { + oldUmask := syscall.Umask(0022) //nolint if (oldUmask & ^0022) != 0 { logrus.Debugf("umask value too restrictive. Forcing it to 022") } } -func SetUmask(value int) int { +func Set(value int) int { return syscall.Umask(value) } diff --git a/vendor/github.com/containers/common/pkg/umask/umask_unsupported.go b/vendor/github.com/containers/common/pkg/umask/umask_unsupported.go new file mode 100644 index 000000000..9041d5f20 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/umask/umask_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux,!darwin + +package umask + +func Check() {} + +func Set(int) int { return 0 } diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go index 2ceebc552..846636d75 100644 --- a/vendor/github.com/spf13/cobra/bash_completions.go +++ b/vendor/github.com/spf13/cobra/bash_completions.go @@ -5,95 +5,70 @@ import ( "fmt" "io" "os" + "sort" + "strings" + + "github.com/spf13/pflag" ) // Annotations for Bash completion. const ( - BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" - // BashCompCustom should be avoided as it only works for bash. - // Function RegisterFlagCompletionFunc() should be used instead. + BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" ) -// GenBashCompletion generates bash completion file and writes to the passed writer. -func (c *Command) GenBashCompletion(w io.Writer) error { - return c.genBashCompletion(w, false) -} - -// GenBashCompletionWithDesc generates bash completion file with descriptions and writes to the passed writer. -func (c *Command) GenBashCompletionWithDesc(w io.Writer) error { - return c.genBashCompletion(w, true) -} - -// GenBashCompletionFile generates bash completion file. -func (c *Command) GenBashCompletionFile(filename string) error { - return c.genBashCompletionFile(filename, false) -} - -// GenBashCompletionFileWithDesc generates bash completion file with descriptions. -func (c *Command) GenBashCompletionFileWithDesc(filename string) error { - return c.genBashCompletionFile(filename, true) +func writePreamble(buf *bytes.Buffer, name string) { + buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) + buf.WriteString(fmt.Sprintf(` +__%[1]s_debug() +{ + if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + echo "$*" >> "${BASH_COMP_DEBUG_FILE}" + fi } -func (c *Command) genBashCompletionFile(filename string, includeDesc bool) error { - outFile, err := os.Create(filename) - if err != nil { - return err - } - defer outFile.Close() - - return c.genBashCompletion(outFile, includeDesc) +# Homebrew on Macs have version 1.3 of bash-completion which doesn't include +# _init_completion. This is a very minimal version of that function. +__%[1]s_init_completion() +{ + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword } -func (c *Command) genBashCompletion(w io.Writer, includeDesc bool) error { - buf := new(bytes.Buffer) - if len(c.BashCompletionFunction) > 0 { - buf.WriteString(c.BashCompletionFunction + "\n") - } - genBashComp(buf, c.Name(), includeDesc) - - _, err := buf.WriteTo(w) - return err +__%[1]s_index_of_word() +{ + local w word=$1 + shift + index=0 + for w in "$@"; do + [[ $w = "$word" ]] && return + index=$((index+1)) + done + index=-1 } -func genBashComp(buf *bytes.Buffer, name string, includeDesc bool) { - compCmd := ShellCompRequestCmd - if !includeDesc { - compCmd = ShellCompNoDescRequestCmd - } - - buf.WriteString(fmt.Sprintf(`# bash completion for %-36[1]s -*- shell-script -*- - -__%[1]s_debug() +__%[1]s_contains_word() { - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then - echo "$*" >> "${BASH_COMP_DEBUG_FILE}" - fi + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 } -__%[1]s_perform_completion() +__%[1]s_handle_go_custom_completion() { - __%[1]s_debug - __%[1]s_debug "========= starting completion logic ==========" - __%[1]s_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" - - # The user could have moved the cursor backwards on the command-line. - # We need to trigger completion from the $cword location, so we need - # to truncate the command-line ($words) up to the $cword location. - words=("${words[@]:0:$cword+1}") - __%[1]s_debug "Truncated words[*]: ${words[*]}," + __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}" local shellCompDirectiveError=%[3]d local shellCompDirectiveNoSpace=%[4]d local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d - local shellCompDirectiveLegacyCustomComp=%[8]d - local shellCompDirectiveLegacyCustomArgsComp=%[9]d - local out requestComp lastParam lastChar comp directive args flagPrefix + local out requestComp lastParam lastChar comp directive args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly %[1]s allows to handle aliases @@ -102,24 +77,16 @@ __%[1]s_perform_completion() lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} - __%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}" + __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. - __%[1]s_debug "Adding extra empty parameter" + __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter" requestComp="${requestComp} \"\"" fi - # When completing a flag with an = (e.g., %[1]s -n=<TAB>) - # bash focuses on the part after the =, so we need to remove - # the flag part from $cur - if [[ "${cur}" == -*=* ]]; then - flagPrefix="${cur%%%%=*}=" - cur="${cur#*=}" - fi - - __%[1]s_debug "Calling ${requestComp}" + __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}" # Use eval to handle any environment variables and such out=$(eval "${requestComp}" 2>/dev/null) @@ -131,23 +98,23 @@ __%[1]s_perform_completion() # There is not directive specified directive=0 fi - __%[1]s_debug "The completion directive is: ${directive}" - __%[1]s_debug "The completions are: ${out[*]}" + __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" + __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. - __%[1]s_debug "Received error from custom completion go code" + __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" return else if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "Activating no space" + __%[1]s_debug "${FUNCNAME[0]}: activating no space" compopt -o nospace fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "Activating no file completion" + __%[1]s_debug "${FUNCNAME[0]}: activating no file completion" compopt +o default fi fi @@ -156,7 +123,6 @@ __%[1]s_perform_completion() if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local fullFilter filter filteringCmd - # Do not use quotes around the $out variable or else newline # characters will be kept. for filter in ${out[*]}; do @@ -168,173 +134,545 @@ __%[1]s_perform_completion() $filteringCmd elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then # File completion for directories only - + local subDir # Use printf to strip any trailing newline - local subdir subdir=$(printf "%%s" "${out[0]}") if [ -n "$subdir" ]; then __%[1]s_debug "Listing directories in $subdir" - pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return + __%[1]s_handle_subdirs_in_dir_flag "$subdir" else __%[1]s_debug "Listing directories in ." _filedir -d fi - elif [ $((directive & shellCompDirectiveLegacyCustomComp)) -ne 0 ]; then - local cmd - __%[1]s_debug "Legacy custom completion. Directive: $directive, cmds: ${out[*]}" - - # The following variables should get their value through the commands - # we have received as completions and are parsing below. - local last_command - local nouns - - # Execute every command received - while IFS='' read -r cmd; do - __%[1]s_debug "About to execute: $cmd" - eval "$cmd" - done < <(printf "%%s\n" "${out[@]}") - - __%[1]s_debug "last_command: $last_command" - __%[1]s_debug "nouns[0]: ${nouns[0]}, nouns[1]: ${nouns[1]}" - - if [ $((directive & shellCompDirectiveLegacyCustomArgsComp)) -ne 0 ]; then - # We should call the global legacy custom completion function, if it is defined - if declare -F __%[1]s_custom_func >/dev/null; then - # Use command name qualified legacy custom func - __%[1]s_debug "About to call: __%[1]s_custom_func" - __%[1]s_custom_func - elif declare -F __custom_func >/dev/null; then - # Otherwise fall back to unqualified legacy custom func for compatibility - __%[1]s_debug "About to call: __custom_func" - __custom_func - fi - fi else - local tab - tab=$(printf '\t') - local longest=0 - # Look for the longest completion so that we can format things nicely while IFS='' read -r comp; do - comp=${comp%%%%$tab*} - if ((${#comp}>longest)); then - longest=${#comp} + COMPREPLY+=("$comp") + done < <(compgen -W "${out[*]}" -- "$cur") + fi +} + +__%[1]s_handle_reply() +{ + __%[1]s_debug "${FUNCNAME[0]}" + local comp + case $cur in + -*) + if [[ $(type -t compopt) = "builtin" ]]; then + compopt -o nospace + fi + local allflags + if [ ${#must_have_one_flag[@]} -ne 0 ]; then + allflags=("${must_have_one_flag[@]}") + else + allflags=("${flags[*]} ${two_word_flags[*]}") + fi + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${allflags[*]}" -- "$cur") + if [[ $(type -t compopt) = "builtin" ]]; then + [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace fi - done < <(printf "%%s\n" "${out[@]}") - local completions=() - while IFS='' read -r comp; do - if [ -z "$comp" ]; then - continue + # complete after --flag=abc + if [[ $cur == *=* ]]; then + if [[ $(type -t compopt) = "builtin" ]]; then + compopt +o nospace + fi + + local index flag + flag="${cur%%=*}" + __%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}" + COMPREPLY=() + if [[ ${index} -ge 0 ]]; then + PREFIX="" + cur="${cur#*=}" + ${flags_completion[${index}]} + if [ -n "${ZSH_VERSION}" ]; then + # zsh completion needs --flag= prefix + eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" + fi + fi fi + return 0; + ;; + esac + + # check if we are handling a flag with special work handling + local index + __%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}" + if [[ ${index} -ge 0 ]]; then + ${flags_completion[${index}]} + return + fi - __%[1]s_debug "Original comp: $comp" - comp="$(__%[1]s_format_comp_descriptions "$comp" "$longest")" - __%[1]s_debug "Final comp: $comp" - completions+=("$comp") - done < <(printf "%%s\n" "${out[@]}") + # we are parsing a flag and don't have a special handler, no completion + if [[ ${cur} != "${words[cword]}" ]]; then + return + fi + local completions + completions=("${commands[@]}") + if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then + completions+=("${must_have_one_noun[@]}") + elif [[ -n "${has_completion_function}" ]]; then + # if a go completion function is provided, defer to that function + __%[1]s_handle_go_custom_completion + fi + if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then + completions+=("${must_have_one_flag[@]}") + fi + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${completions[*]}" -- "$cur") + + if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then while IFS='' read -r comp; do - # Although this script should only be used for bash - # there may be programs that still convert the bash - # script into a zsh one. To continue supporting those - # programs, we do this single adaptation for zsh - if [ -n "${ZSH_VERSION}" ]; then - # zsh completion needs --flag= prefix - COMPREPLY+=("$flagPrefix$comp") - else - COMPREPLY+=("$comp") - fi - done < <(compgen -W "${completions[*]}" -- "$cur") - - # If there is a single completion left, remove the description text - if [ ${#COMPREPLY[*]} -eq 1 ]; then - __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%%%% *}" - __%[1]s_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY=() COMPREPLY+=("$comp") + done < <(compgen -W "${noun_aliases[*]}" -- "$cur") + fi + + if [[ ${#COMPREPLY[@]} -eq 0 ]]; then + if declare -F __%[1]s_custom_func >/dev/null; then + # try command name qualified custom func + __%[1]s_custom_func + else + # otherwise fall back to unqualified for compatibility + declare -F __custom_func >/dev/null && __custom_func + fi + fi + + # available in bash-completion >= 2, not always present on macOS + if declare -F __ltrim_colon_completions >/dev/null; then + __ltrim_colon_completions "$cur" + fi + + # If there is only 1 completion and it is a flag with an = it will be completed + # but we don't want a space after the = + if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then + compopt -o nospace + fi +} + +# The arguments should be in the form "ext1|ext2|extn" +__%[1]s_handle_filename_extension_flag() +{ + local ext="$1" + _filedir "@(${ext})" +} + +__%[1]s_handle_subdirs_in_dir_flag() +{ + local dir="$1" + pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return +} + +__%[1]s_handle_flag() +{ + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + # if a command required a flag, and we found it, unset must_have_one_flag() + local flagname=${words[c]} + local flagvalue + # if the word contained an = + if [[ ${words[c]} == *"="* ]]; then + flagvalue=${flagname#*=} # take in as flagvalue after the = + flagname=${flagname%%=*} # strip everything after the = + flagname="${flagname}=" # but put the = back + fi + __%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}" + if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then + must_have_one_flag=() + fi + + # if you set a flag which only applies to this command, don't show subcommands + if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then + commands=() + fi + + # keep flag value with flagname as flaghash + # flaghash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + if [ -n "${flagvalue}" ] ; then + flaghash[${flagname}]=${flagvalue} + elif [ -n "${words[ $((c+1)) ]}" ] ; then + flaghash[${flagname}]=${words[ $((c+1)) ]} + else + flaghash[${flagname}]="true" # pad "true" for bool flag + fi + fi + + # skip the argument to a two word flag + if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then + __%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument" + c=$((c+1)) + # if we are looking for a flags value, don't show commands + if [[ $c -eq $cword ]]; then + commands=() fi fi - __%[1]s_handle_special_char "$cur" : - __%[1]s_handle_special_char "$cur" = + c=$((c+1)) + } -__%[1]s_handle_special_char() +__%[1]s_handle_noun() { - local comp="$1" - local char=$2 - if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then - local word=${comp%%"${comp##*${char}}"} - local idx=${#COMPREPLY[*]} - while [[ $((--idx)) -ge 0 ]]; do - COMPREPLY[$idx]=${COMPREPLY[$idx]#"$word"} - done + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then + must_have_one_noun=() + elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then + must_have_one_noun=() fi + + nouns+=("${words[c]}") + c=$((c+1)) } -__%[1]s_format_comp_descriptions() +__%[1]s_handle_command() { - local tab - tab=$(printf '\t') - local comp="$1" - local longest=$2 - - # Properly format the description string which follows a tab character if there is one - if [[ "$comp" == *$tab* ]]; then - desc=${comp#*$tab} - comp=${comp%%%%$tab*} - - # $COLUMNS stores the current shell width. - # Remove an extra 4 because we add 2 spaces and 2 parentheses. - maxdesclength=$(( COLUMNS - longest - 4 )) - - # Make sure we can fit a description of at least 8 characters - # if we are to align the descriptions. - if [[ $maxdesclength -gt 8 ]]; then - # Add the proper number of spaces to align the descriptions - for ((i = ${#comp} ; i < longest ; i++)); do - comp+=" " - done + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + local next_command + if [[ -n ${last_command} ]]; then + next_command="_${last_command}_${words[c]//:/__}" + else + if [[ $c -eq 0 ]]; then + next_command="_%[1]s_root_command" else - # Don't pad the descriptions so we can fit more text after the completion - maxdesclength=$(( COLUMNS - ${#comp} - 4 )) + next_command="_${words[c]//:/__}" fi + fi + c=$((c+1)) + __%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}" + declare -F "$next_command" >/dev/null && $next_command +} - # If there is enough space for any description text, - # truncate the descriptions that are too long for the shell width - if [ $maxdesclength -gt 0 ]; then - if [ ${#desc} -gt $maxdesclength ]; then - desc=${desc:0:$(( maxdesclength - 1 ))} - desc+="…" - fi - comp+=" ($desc)" +__%[1]s_handle_word() +{ + if [[ $c -ge $cword ]]; then + __%[1]s_handle_reply + return + fi + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + if [[ "${words[c]}" == -* ]]; then + __%[1]s_handle_flag + elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then + __%[1]s_handle_command + elif [[ $c -eq 0 ]]; then + __%[1]s_handle_command + elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then + # aliashash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + words[c]=${aliashash[${words[c]}]} + __%[1]s_handle_command + else + __%[1]s_handle_noun fi + else + __%[1]s_handle_noun fi + __%[1]s_handle_word +} - # Must use printf to escape all special characters - printf "%%q" "${comp}" +`, name, ShellCompNoDescRequestCmd, + ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) } -__start_%[1]s() -{ +func writePostscript(buf *bytes.Buffer, name string) { + name = strings.Replace(name, ":", "__", -1) + buf.WriteString(fmt.Sprintf("__start_%s()\n", name)) + buf.WriteString(fmt.Sprintf(`{ local cur prev words cword + declare -A flaghash 2>/dev/null || : + declare -A aliashash 2>/dev/null || : + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -s || return + else + __%[1]s_init_completion -n "=" || return + fi - COMPREPLY=() - _get_comp_words_by_ref -n "=:" cur prev words cword - - __%[1]s_perform_completion + local c=0 + local flags=() + local two_word_flags=() + local local_nonpersistent_flags=() + local flags_with_completion=() + local flags_completion=() + local commands=("%[1]s") + local must_have_one_flag=() + local must_have_one_noun=() + local has_completion_function + local last_command + local nouns=() + + __%[1]s_handle_word } -if [[ $(type -t compopt) = "builtin" ]]; then - complete -o default -F __start_%[1]s %[1]s +`, name)) + buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then + complete -o default -F __start_%s %s else - complete -o default -o nospace -F __start_%[1]s %[1]s + complete -o default -o nospace -F __start_%s %s fi -# ex: ts=4 sw=4 et filetype=sh -`, name, compCmd, - ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, - shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) +`, name, name, name, name)) + buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n") +} + +func writeCommands(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" commands=()\n") + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() && c != cmd.helpCommand { + continue + } + buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) + writeCmdAliases(buf, c) + } + buf.WriteString("\n") +} + +func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) { + for key, value := range annotations { + switch key { + case BashCompFilenameExt: + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + + var ext string + if len(value) > 0 { + ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|") + } else { + ext = "_filedir" + } + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) + case BashCompCustom: + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + if len(value) > 0 { + handlers := strings.Join(value, "; ") + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) + } else { + buf.WriteString(" flags_completion+=(:)\n") + } + case BashCompSubdirsInDir: + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + + var ext string + if len(value) == 1 { + ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0] + } else { + ext = "_filedir -d" + } + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) + } + } +} + +func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { + name := flag.Shorthand + format := " " + if len(flag.NoOptDefVal) == 0 { + format += "two_word_" + } + format += "flags+=(\"-%s\")\n" + buf.WriteString(fmt.Sprintf(format, name)) + writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) +} + +func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { + name := flag.Name + format := " flags+=(\"--%s" + if len(flag.NoOptDefVal) == 0 { + format += "=" + } + format += "\")\n" + buf.WriteString(fmt.Sprintf(format, name)) + if len(flag.NoOptDefVal) == 0 { + format = " two_word_flags+=(\"--%s\")\n" + buf.WriteString(fmt.Sprintf(format, name)) + } + writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) +} + +func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) { + name := flag.Name + format := " local_nonpersistent_flags+=(\"--%[1]s\")\n" + if len(flag.NoOptDefVal) == 0 { + format += " local_nonpersistent_flags+=(\"--%[1]s=\")\n" + } + buf.WriteString(fmt.Sprintf(format, name)) + if len(flag.Shorthand) > 0 { + buf.WriteString(fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand)) + } +} + +// Setup annotations for go completions for registered flags +func prepareCustomAnnotationsForFlags(cmd *Command) { + for flag := range flagCompletionFunctions { + // Make sure the completion script calls the __*_go_custom_completion function for + // every registered flag. We need to do this here (and not when the flag was registered + // for completion) so that we can know the root command name for the prefix + // of __<prefix>_go_custom_completion + if flag.Annotations == nil { + flag.Annotations = map[string][]string{} + } + flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())} + } +} + +func writeFlags(buf *bytes.Buffer, cmd *Command) { + prepareCustomAnnotationsForFlags(cmd) + buf.WriteString(` flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + +`) + localNonPersistentFlags := cmd.LocalNonPersistentFlags() + cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + writeFlag(buf, flag, cmd) + if len(flag.Shorthand) > 0 { + writeShortFlag(buf, flag, cmd) + } + // localNonPersistentFlags are used to stop the completion of subcommands when one is set + // if TraverseChildren is true we should allow to complete subcommands + if localNonPersistentFlags.Lookup(flag.Name) != nil && !cmd.Root().TraverseChildren { + writeLocalNonPersistentFlag(buf, flag) + } + }) + cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + writeFlag(buf, flag, cmd) + if len(flag.Shorthand) > 0 { + writeShortFlag(buf, flag, cmd) + } + }) + + buf.WriteString("\n") +} + +func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" must_have_one_flag=()\n") + flags := cmd.NonInheritedFlags() + flags.VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + for key := range flag.Annotations { + switch key { + case BashCompOneRequiredFlag: + format := " must_have_one_flag+=(\"--%s" + if flag.Value.Type() != "bool" { + format += "=" + } + format += "\")\n" + buf.WriteString(fmt.Sprintf(format, flag.Name)) + + if len(flag.Shorthand) > 0 { + buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) + } + } + } + }) +} + +func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" must_have_one_noun=()\n") + sort.Sort(sort.StringSlice(cmd.ValidArgs)) + for _, value := range cmd.ValidArgs { + // Remove any description that may be included following a tab character. + // Descriptions are not supported by bash completion. + value = strings.Split(value, "\t")[0] + buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) + } + if cmd.ValidArgsFunction != nil { + buf.WriteString(" has_completion_function=1\n") + } +} + +func writeCmdAliases(buf *bytes.Buffer, cmd *Command) { + if len(cmd.Aliases) == 0 { + return + } + + sort.Sort(sort.StringSlice(cmd.Aliases)) + + buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) + for _, value := range cmd.Aliases { + buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value)) + buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) + } + buf.WriteString(` fi`) + buf.WriteString("\n") +} +func writeArgAliases(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" noun_aliases=()\n") + sort.Sort(sort.StringSlice(cmd.ArgAliases)) + for _, value := range cmd.ArgAliases { + buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) + } +} + +func gen(buf *bytes.Buffer, cmd *Command) { + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() && c != cmd.helpCommand { + continue + } + gen(buf, c) + } + commandName := cmd.CommandPath() + commandName = strings.Replace(commandName, " ", "_", -1) + commandName = strings.Replace(commandName, ":", "__", -1) + + if cmd.Root() == cmd { + buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName)) + } else { + buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) + } + + buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) + buf.WriteString("\n") + buf.WriteString(" command_aliases=()\n") + buf.WriteString("\n") + + writeCommands(buf, cmd) + writeFlags(buf, cmd) + writeRequiredFlag(buf, cmd) + writeRequiredNouns(buf, cmd) + writeArgAliases(buf, cmd) + buf.WriteString("}\n\n") +} + +// GenBashCompletion generates bash completion file and writes to the passed writer. +func (c *Command) GenBashCompletion(w io.Writer) error { + buf := new(bytes.Buffer) + writePreamble(buf, c.Name()) + if len(c.BashCompletionFunction) > 0 { + buf.WriteString(c.BashCompletionFunction + "\n") + } + gen(buf, c) + writePostscript(buf, c.Name()) + + _, err := buf.WriteTo(w) + return err +} + +func nonCompletableFlag(flag *pflag.Flag) bool { + return flag.Hidden || len(flag.Deprecated) > 0 +} + +// GenBashCompletionFile generates bash completion file. +func (c *Command) GenBashCompletionFile(filename string) error { + outFile, err := os.Create(filename) + if err != nil { + return err + } + defer outFile.Close() + + return c.GenBashCompletion(outFile) } diff --git a/vendor/github.com/spf13/cobra/custom_completions.go b/vendor/github.com/spf13/cobra/custom_completions.go index e2c68ae1e..f9e88e081 100644 --- a/vendor/github.com/spf13/cobra/custom_completions.go +++ b/vendor/github.com/spf13/cobra/custom_completions.go @@ -51,11 +51,6 @@ const ( // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs - // For internal use only. - // Used to maintain backwards-compatibility with the legacy bash custom completions. - shellCompDirectiveLegacyCustomComp - shellCompDirectiveLegacyCustomArgsComp - // =========================================================================== // All directives using iota should be above this one. @@ -99,12 +94,6 @@ func (d ShellCompDirective) string() string { if d&ShellCompDirectiveFilterDirs != 0 { directives = append(directives, "ShellCompDirectiveFilterDirs") } - if d&shellCompDirectiveLegacyCustomComp != 0 { - directives = append(directives, "shellCompDirectiveLegacyCustomComp") - } - if d&shellCompDirectiveLegacyCustomArgsComp != 0 { - directives = append(directives, "shellCompDirectiveLegacyCustomArgsComp") - } if len(directives) == 0 { directives = append(directives, "ShellCompDirectiveDefault") } @@ -160,6 +149,10 @@ func (c *Command) initCompleteCmd(args []string) { fmt.Fprintln(finalCmd.OutOrStdout(), comp) } + if directive >= shellCompDirectiveMaxValue { + directive = ShellCompDirectiveDefault + } + // As the last printout, print the completion directive for the completion script to parse. // The directive integer must be that last character following a single colon (:). // The completion script expects :<directive> @@ -369,10 +362,6 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi var comps []string comps, directive = completionFn(finalCmd, finalArgs, toComplete) completions = append(completions, comps...) - } else { - // If there is no Go custom completion defined, check for legacy bash - // custom completion to preserve backwards-compatibility - completions, directive = checkLegacyCustomCompletion(finalCmd, finalArgs, flag, completions, directive) } return finalCmd, completions, directive, nil @@ -453,16 +442,7 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p if len(lastArg) > 0 && lastArg[0] == '-' { if index := strings.Index(lastArg, "="); index >= 0 { // Flag with an = - if strings.HasPrefix(lastArg[:index], "--") { - // Flag has full name - flagName = lastArg[2:index] - } else { - // Flag is shorthand - // We have to get the last shorthand flag name - // e.g. `-asd` => d to provide the correct completion - // https://github.com/spf13/cobra/issues/1257 - flagName = lastArg[index-1 : index] - } + flagName = strings.TrimLeft(lastArg[:index], "-") lastArg = lastArg[index+1:] flagWithEqual = true } else { @@ -479,16 +459,8 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p // If the flag contains an = it means it has already been fully processed, // so we don't need to deal with it here. if index := strings.Index(prevArg, "="); index < 0 { - if strings.HasPrefix(prevArg, "--") { - // Flag has full name - flagName = prevArg[2:] - } else { - // Flag is shorthand - // We have to get the last shorthand flag name - // e.g. `-asd` => d to provide the correct completion - // https://github.com/spf13/cobra/issues/1257 - flagName = prevArg[len(prevArg)-1:] - } + flagName = strings.TrimLeft(prevArg, "-") + // Remove the uncompleted flag or else there could be an error created // for an invalid value for that flag trimmedArgs = args[:len(args)-1] @@ -541,65 +513,6 @@ func findFlag(cmd *Command, name string) *pflag.Flag { return cmd.Flag(name) } -func nonCompletableFlag(flag *pflag.Flag) bool { - return flag.Hidden || len(flag.Deprecated) > 0 -} - -// This function checks if legacy bash custom completion should be performed and if so, -// it provides the shell script with the necessary information. -func checkLegacyCustomCompletion(cmd *Command, args []string, flag *pflag.Flag, completions []string, directive ShellCompDirective) ([]string, ShellCompDirective) { - // Check if any legacy custom completion is defined for the program - if len(cmd.Root().BashCompletionFunction) > 0 { - // Legacy custom completion is only triggered if no other completions were found. - if len(completions) == 0 { - if flag != nil { - // For legacy custom flag completion, we must let the script know the bash - // functions it should call based on the content of the annotation BashCompCustom. - if values, present := flag.Annotations[BashCompCustom]; present { - if len(values) > 0 { - handlers := strings.Join(values, "; ") - // We send the commands to set the shell variables that are needed - // for legacy custom completions followed by the functions to call - // to perform the actual flag completion - completions = append(prepareLegacyCustomCompletionVars(cmd, args), handlers) - directive = directive | shellCompDirectiveLegacyCustomComp - } - } - } else { - // Check if the legacy custom_func is defined. - // This check will work for both "__custom_func" and "__<program>_custom_func". - // This could happen if the program defined some functions for legacy flag completion - // but not the legacy custom_func. - if strings.Contains(cmd.Root().BashCompletionFunction, "_custom_func") { - // For legacy args completion, the script already knows what to call - // so we only need to tell it the commands to set the shell variables needed - completions = prepareLegacyCustomCompletionVars(cmd, args) - directive = directive | shellCompDirectiveLegacyCustomComp | shellCompDirectiveLegacyCustomArgsComp - } - } - } - } - return completions, directive -} - -// The original bash completion script had some shell variables that are used by legacy bash -// custom completions. Let's set those variables to allow those legacy custom completions -// to continue working. -func prepareLegacyCustomCompletionVars(cmd *Command, args []string) []string { - var compVarCmds []string - - // "last_command" variable - commandName := cmd.CommandPath() - commandName = strings.Replace(commandName, " ", "_", -1) - commandName = strings.Replace(commandName, ":", "__", -1) - compVarCmds = append(compVarCmds, fmt.Sprintf("last_command=%s", commandName)) - - // "nouns" array variable - compVarCmds = append(compVarCmds, fmt.Sprintf("nouns=(%s)", strings.Join(args, " "))) - - return compVarCmds -} - // CompDebug prints the specified string to the same file as where the // completion script prints its logs. // Note that completion printouts should never be on stdout as they would diff --git a/vendor/github.com/spf13/cobra/fish_completions.go b/vendor/github.com/spf13/cobra/fish_completions.go index aeaef0087..eaae9bca8 100644 --- a/vendor/github.com/spf13/cobra/fish_completions.go +++ b/vendor/github.com/spf13/cobra/fish_completions.go @@ -28,9 +28,9 @@ function __%[1]s_debug end function __%[1]s_perform_completion - __%[1]s_debug "Starting __%[1]s_perform_completion" + __%[1]s_debug "Starting __%[1]s_perform_completion with: $argv" - set args (string split -- " " (commandline -c)) + set args (string split -- " " "$argv") set lastArg "$args[-1]" __%[1]s_debug "args: $args" @@ -71,22 +71,31 @@ function __%[1]s_perform_completion printf "%%s\n" "$directiveLine" end -# This function does two things: -# - Obtain the completions and store them in the global __%[1]s_comp_results -# - Return false if file completion should be performed +# This function does three things: +# 1- Obtain the completions and store them in the global __%[1]s_comp_results +# 2- Set the __%[1]s_comp_do_file_comp flag if file completion should be performed +# and unset it otherwise +# 3- Return true if the completion results are not empty function __%[1]s_prepare_completions - __%[1]s_debug "" - __%[1]s_debug "========= starting completion logic ==========" - # Start fresh + set --erase __%[1]s_comp_do_file_comp set --erase __%[1]s_comp_results - set results (__%[1]s_perform_completion) + # Check if the command-line is already provided. This is useful for testing. + if not set --query __%[1]s_comp_commandLine + # Use the -c flag to allow for completion in the middle of the line + set __%[1]s_comp_commandLine (commandline -c) + end + __%[1]s_debug "commandLine is: $__%[1]s_comp_commandLine" + + set results (__%[1]s_perform_completion "$__%[1]s_comp_commandLine") + set --erase __%[1]s_comp_commandLine __%[1]s_debug "Completion results: $results" if test -z "$results" __%[1]s_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps + set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -101,8 +110,6 @@ function __%[1]s_prepare_completions set shellCompDirectiveNoFileComp %[6]d set shellCompDirectiveFilterFileExt %[7]d set shellCompDirectiveFilterDirs %[8]d - set shellCompDirectiveLegacyCustomComp %[9]d - set shellCompDirectiveLegacyCustomArgsComp %[10]d if test -z "$directive" set directive 0 @@ -112,14 +119,6 @@ function __%[1]s_prepare_completions if test $compErr -eq 1 __%[1]s_debug "Received error directive: aborting." # Might as well do file completion, in case it helps - return 1 - end - - set legacyCustom (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomComp) %% 2) - set legacyCustomArgs (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomArgsComp) %% 2) - if test $legacyCustom -eq 1; or test $legacyCustomArgs -eq 1 - __%[1]s_debug "Legacy bash custom completion not applicable to fish" - # Do full file completion instead set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -129,6 +128,7 @@ function __%[1]s_prepare_completions if test $filefilter -eq 1; or test $dirfilter -eq 1 __%[1]s_debug "File extension filtering or directory filtering not supported" # Do full file completion instead + set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -137,51 +137,27 @@ function __%[1]s_prepare_completions __%[1]s_debug "nospace: $nospace, nofiles: $nofiles" - # If we want to prevent a space, or if file completion is NOT disabled, - # we need to count the number of valid completions. - # To do so, we will filter on prefix as the completions we have received - # may not already be filtered so as to allow fish to match on different - # criteria than prefix. - if test $nospace -ne 0; or test $nofiles -eq 0 - set prefix (commandline -t) - __%[1]s_debug "prefix: $prefix" - - set completions - for comp in $__%[1]s_comp_results - if test (string match -e -r "^$prefix" "$comp") - set -a completions $comp - end - end - set --global __%[1]s_comp_results $completions - __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results" - - # Important not to quote the variable for count to work - set numComps (count $__%[1]s_comp_results) - __%[1]s_debug "numComps: $numComps" - - if test $numComps -eq 1; and test $nospace -ne 0 - # To support the "nospace" directive we trick the shell - # by outputting an extra, longer completion. - # We must first split on \t to get rid of the descriptions because - # the extra character we add to the fake second completion must be - # before the description. We don't need descriptions anyway since - # there is only a single real completion which the shell will expand - # immediately. - __%[1]s_debug "Adding second completion to perform nospace directive" - set split (string split --max 1 \t $__%[1]s_comp_results[1]) - set --global __%[1]s_comp_results $split[1] $split[1]. - __%[1]s_debug "Completions are now: $__%[1]s_comp_results" - end - - if test $numComps -eq 0; and test $nofiles -eq 0 - # To be consistent with bash and zsh, we only trigger file - # completion when there are no other completions - __%[1]s_debug "Requesting file completion" - return 1 - end + # Important not to quote the variable for count to work + set numComps (count $__%[1]s_comp_results) + __%[1]s_debug "numComps: $numComps" + + if test $numComps -eq 1; and test $nospace -ne 0 + # To support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __%[1]s_debug "Adding second completion to perform nospace directive" + set --append __%[1]s_comp_results $__%[1]s_comp_results[1]. end - return 0 + if test $numComps -eq 0; and test $nofiles -eq 0 + __%[1]s_debug "Requesting file completion" + set --global __%[1]s_comp_do_file_comp 1 + end + + # If we don't want file completion, we must return true even if there + # are no completions found. This is because fish will perform the last + # completion command, even if its condition is false, if no other + # completion command was triggered + return (not set --query __%[1]s_comp_do_file_comp) end # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves @@ -194,14 +170,21 @@ complete --do-complete "%[2]s " > /dev/null 2>&1 # Remove any pre-existing completions for the program since we will be handling all of them. complete -c %[2]s -e -# The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results -# which provides the program's completion choices. +# The order in which the below two lines are defined is very important so that __%[1]s_prepare_completions +# is called first. It is __%[1]s_prepare_completions that sets up the __%[1]s_comp_do_file_comp variable. +# +# This completion will be run second as complete commands are added FILO. +# It triggers file completion choices when __%[1]s_comp_do_file_comp is set. +complete -c %[2]s -n 'set --query __%[1]s_comp_do_file_comp' + +# This completion will be run first as complete commands are added FILO. +# The call to __%[1]s_prepare_completions will setup both __%[1]s_comp_results and __%[1]s_comp_do_file_comp. +# It provides the program's completion choices. complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' `, nameForVar, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, - shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) } // GenFishCompletion generates fish completion file and writes to the passed writer. diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go index ef290e2eb..92a70394a 100644 --- a/vendor/github.com/spf13/cobra/zsh_completions.go +++ b/vendor/github.com/spf13/cobra/zsh_completions.go @@ -94,10 +94,8 @@ _%[1]s() local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d - local shellCompDirectiveLegacyCustomComp=%[8]d - local shellCompDirectiveLegacyCustomArgsComp=%[9]d - local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace + local lastParam lastChar flagPrefix requestComp out directive compCount comp lastComp local -a completions __%[1]s_debug "\n========= starting completion logic ==========" @@ -165,6 +163,7 @@ _%[1]s() return fi + compCount=0 while IFS='\n' read -r comp; do if [ -n "$comp" ]; then # If requested, completions are returned with a description. @@ -176,17 +175,13 @@ _%[1]s() local tab=$(printf '\t') comp=${comp//$tab/:} + ((compCount++)) __%[1]s_debug "Adding completion: ${comp}" completions+=${comp} lastComp=$comp fi done < <(printf "%%s\n" "${out[@]}") - if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then - __%[1]s_debug "Activating nospace." - noSpace="-S ''" - fi - if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local filteringCmd @@ -213,40 +208,25 @@ _%[1]s() __%[1]s_debug "Listing directories in ." fi - local result _arguments '*:dirname:_files -/'" ${flagPrefix}" - result=$? if [ -n "$subdir" ]; then popd >/dev/null 2>&1 fi - return $result - else - __%[1]s_debug "Calling _describe" - if eval _describe "completions" completions $flagPrefix $noSpace; then - __%[1]s_debug "_describe found some completions" - - # Return the success of having called _describe - return 0 + elif [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ] && [ ${compCount} -eq 1 ]; then + __%[1]s_debug "Activating nospace." + # We can use compadd here as there is no description when + # there is only one completion. + compadd -S '' "${lastComp}" + elif [ ${compCount} -eq 0 ]; then + if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then + __%[1]s_debug "deactivating file completion" else - __%[1]s_debug "_describe did not find completions." - __%[1]s_debug "Checking if we should do file completion." - if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then - __%[1]s_debug "deactivating file completion" - - # We must return an error code here to let zsh know that there were no - # completions found by _describe; this is what will trigger other - # matching algorithms to attempt to find completions. - # For example zsh can match letters in the middle of words. - return 1 - else - # Perform file completion - __%[1]s_debug "Activating file completion" - - # We must return the result of this command, so it must be the - # last command, or else we must store its result to return it. - _arguments '*:filename:_files'" ${flagPrefix}" - fi + # Perform file completion + __%[1]s_debug "activating file completion" + _arguments '*:filename:_files'" ${flagPrefix}" fi + else + _describe "completions" completions $(echo $flagPrefix) fi } @@ -256,6 +236,5 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then fi `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, - shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) } diff --git a/vendor/github.com/varlink/go/LICENSE b/vendor/github.com/varlink/go/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/vendor/github.com/varlink/go/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go b/vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go deleted file mode 100644 index 98a983c5e..000000000 --- a/vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go +++ /dev/null @@ -1,445 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "go/format" - "io/ioutil" - "os" - "path" - "strings" - - "github.com/varlink/go/varlink/idl" -) - -func writeType(b *bytes.Buffer, t *idl.Type, json bool, ident int) { - switch t.Kind { - case idl.TypeBool: - b.WriteString("bool") - - case idl.TypeInt: - b.WriteString("int64") - - case idl.TypeFloat: - b.WriteString("float64") - - case idl.TypeString, idl.TypeEnum: - b.WriteString("string") - - case idl.TypeObject: - b.WriteString("json.RawMessage") - - case idl.TypeArray: - b.WriteString("[]") - writeType(b, t.ElementType, json, ident) - - case idl.TypeMap: - b.WriteString("map[string]") - writeType(b, t.ElementType, json, ident) - - case idl.TypeMaybe: - b.WriteString("*") - writeType(b, t.ElementType, json, ident) - - case idl.TypeAlias: - b.WriteString(t.Alias) - - case idl.TypeStruct: - if len(t.Fields) == 0 { - b.WriteString("struct{}") - } else { - b.WriteString("struct {\n") - for _, field := range t.Fields { - for i := 0; i < ident+1; i++ { - b.WriteString("\t") - } - - b.WriteString(strings.Title(field.Name) + " ") - writeType(b, field.Type, json, ident+1) - if json { - b.WriteString(" `json:\"" + field.Name) - if field.Type.Kind == idl.TypeMaybe { - b.WriteString(",omitempty") - } - b.WriteString("\"`") - } - b.WriteString("\n") - } - for i := 0; i < ident; i++ { - b.WriteString("\t") - } - b.WriteString("}") - } - } -} - -func writeDocString(b *bytes.Buffer, s string) { - if s == "" { - return - } - - // Quote multi-line docstrings - b.WriteString("// " + strings.Replace(s, "\n", "\n// ", -1)) - b.WriteString("\n") -} - -func generateTemplate(description string) (string, []byte, error) { - description = strings.TrimRight(description, "\n") - - midl, err := idl.New(description) - if err != nil { - return "", nil, err - } - - pkgname := strings.Replace(midl.Name, ".", "", -1) - - var b bytes.Buffer - b.WriteString("// Generated with github.com/varlink/go/cmd/varlink-go-interface-generator\n\n") - - writeDocString(&b, midl.Doc) - b.WriteString("package " + pkgname + "\n\n") - b.WriteString("@IMPORTS@\n\n") - - b.WriteString("// Generated type declarations\n\n") - - for _, a := range midl.Aliases { - writeDocString(&b, a.Doc) - b.WriteString("type " + a.Name + " ") - writeType(&b, a.Type, true, 0) - b.WriteString("\n\n") - } - - for _, a := range midl.Errors { - writeDocString(&b, a.Doc) - b.WriteString("type " + a.Name + " ") - writeType(&b, a.Type, true, 0) - b.WriteString("\nfunc (e " + a.Name + ") Error() string {\n") - b.WriteString("\treturn \"" + midl.Name + "." + a.Name + "\"\n") - b.WriteString("}\n\n") - } - - b.WriteString("func Dispatch_Error(err error) error {\n") - b.WriteString("\tif e, ok := err.(*varlink.Error); ok {\n") - b.WriteString("\t\tswitch e.Name {\n") - for _, a := range midl.Errors { - b.WriteString("\t\tcase \"" + midl.Name + "." + a.Name + "\":\n") - b.WriteString("\t\t\terrorRawParameters := e.Parameters.(*json.RawMessage)\n") - b.WriteString("\t\t\tif errorRawParameters == nil {\n") - b.WriteString("\t\t\t\treturn e\n") - b.WriteString("\t\t\t}\n") - b.WriteString("\t\t\tvar param " + a.Name + "\n") - b.WriteString("\t\t\terr := json.Unmarshal(*errorRawParameters, ¶m)\n") - b.WriteString("\t\t\tif err != nil {\n") - b.WriteString("\t\t\t\treturn e\n") - b.WriteString("\t\t\t}\n") - b.WriteString("\t\t\treturn ¶m\n") - } - b.WriteString("\t\t}\n") - b.WriteString("\t}\n") - b.WriteString("\treturn err\n") - b.WriteString("}\n\n") - - b.WriteString("// Generated client method calls\n\n") - - for _, m := range midl.Methods { - writeDocString(&b, m.Doc) - b.WriteString("type " + m.Name + "_methods struct{}\n") - b.WriteString("func " + m.Name + "() " + m.Name + "_methods { return " + m.Name + "_methods{} }\n\n") - - b.WriteString("func (m " + m.Name + "_methods) Call(c *varlink.Connection") - for _, field := range m.In.Fields { - b.WriteString(", " + field.Name + "_in_ ") - writeType(&b, field.Type, false, 1) - } - b.WriteString(") (") - for _, field := range m.Out.Fields { - b.WriteString(field.Name + "_out_ ") - writeType(&b, field.Type, false, 1) - b.WriteString(", ") - } - b.WriteString("err_ error) {\n") - b.WriteString("receive, err_ := m.Send(c, 0") - for _, field := range m.In.Fields { - b.WriteString(", " + field.Name + "_in_ ") - } - b.WriteString(")\n") - b.WriteString("if err_ != nil {\n" + - "\treturn\n" + - "}\n") - b.WriteString("\t") - for _, field := range m.Out.Fields { - b.WriteString(field.Name + "_out_ ") - b.WriteString(", ") - } - b.WriteString("_, err_ = receive()\n") - b.WriteString("\treturn\n" + - "}\n\n") - - b.WriteString("func (m " + m.Name + "_methods) Send(c *varlink.Connection, flags uint64") - for _, field := range m.In.Fields { - b.WriteString(", " + field.Name + "_in_ ") - writeType(&b, field.Type, false, 1) - } - b.WriteString(") (func() (") - for _, field := range m.Out.Fields { - writeType(&b, field.Type, false, 1) - b.WriteString(", ") - } - b.WriteString("uint64, error), error) {\n") - if len(m.In.Fields) > 0 { - b.WriteString("\tvar in ") - writeType(&b, m.In, true, 1) - b.WriteString("\n") - for _, field := range m.In.Fields { - switch field.Type.Kind { - case idl.TypeStruct, idl.TypeArray, idl.TypeMap: - b.WriteString("\tin." + strings.Title(field.Name) + " = ") - writeType(&b, field.Type, true, 1) - b.WriteString("(" + field.Name + "_in_)\n") - - default: - b.WriteString("\tin." + strings.Title(field.Name) + " = " + field.Name + "_in_\n") - } - } - b.WriteString("\treceive, err := c.Send(\"" + midl.Name + "." + m.Name + "\", in, flags)\n") - } else { - b.WriteString("\treceive, err := c.Send(\"" + midl.Name + "." + m.Name + "\", nil, flags)\n") - } - b.WriteString("\tif err != nil {\n" + - "\t\treturn nil, err\n" + - "\t}\n") - b.WriteString("\treturn func() (") - for _, field := range m.Out.Fields { - b.WriteString(field.Name + "_out_ ") - writeType(&b, field.Type, false, 3) - b.WriteString(", ") - } - b.WriteString("flags uint64, err error) {\n") - if len(m.Out.Fields) > 0 { - b.WriteString("\t\tvar out ") - writeType(&b, m.Out, true, 2) - b.WriteString("\n") - b.WriteString("\t\tflags, err = receive(&out)\n") - } else { - b.WriteString("\t\tflags, err = receive(nil)\n") - } - b.WriteString("\t\tif err != nil {\n" + - "\t\t\terr = Dispatch_Error(err)\n" + - "\t\t\treturn\n" + - "\t\t}\n") - for _, field := range m.Out.Fields { - b.WriteString("\t\t" + field.Name + "_out_ = ") - switch field.Type.Kind { - case idl.TypeStruct, idl.TypeArray, idl.TypeMap: - writeType(&b, field.Type, false, 2) - b.WriteString("(out." + strings.Title(field.Name) + ")\n") - - default: - b.WriteString("out." + strings.Title(field.Name) + "\n") - } - } - b.WriteString("\t\treturn\n" + - "\t}, nil\n") - b.WriteString("}\n\n") - } - - b.WriteString("// Generated service interface with all methods\n\n") - - b.WriteString("type " + pkgname + "Interface interface {\n") - for _, m := range midl.Methods { - b.WriteString("\t" + m.Name + "(c VarlinkCall") - for _, field := range m.In.Fields { - b.WriteString(", " + field.Name + "_ ") - writeType(&b, field.Type, false, 1) - } - b.WriteString(") error\n") - } - b.WriteString("}\n\n") - - b.WriteString("// Generated service object with all methods\n\n") - - b.WriteString("type VarlinkCall struct{ varlink.Call }\n\n") - - b.WriteString("// Generated reply methods for all varlink errors\n\n") - - for _, e := range midl.Errors { - writeDocString(&b, e.Doc) - b.WriteString("func (c *VarlinkCall) Reply" + e.Name + "(") - for i, field := range e.Type.Fields { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(field.Name + "_ ") - writeType(&b, field.Type, false, 1) - } - b.WriteString(") error {\n") - b.WriteString("\tvar out " + e.Name + "\n") - if len(e.Type.Fields) > 0 { - for _, field := range e.Type.Fields { - switch field.Type.Kind { - case idl.TypeStruct, idl.TypeArray, idl.TypeMap: - b.WriteString("\tout." + strings.Title(field.Name) + " = ") - writeType(&b, field.Type, true, 1) - b.WriteString("(" + field.Name + "_)\n") - - default: - b.WriteString("\tout." + strings.Title(field.Name) + " = " + field.Name + "_\n") - } - } - } - b.WriteString("\treturn c.ReplyError(\"" + midl.Name + "." + e.Name + "\", &out)\n") - b.WriteString("}\n\n") - } - - b.WriteString("// Generated reply methods for all varlink methods\n\n") - - for _, m := range midl.Methods { - b.WriteString("func (c *VarlinkCall) Reply" + m.Name + "(") - for i, field := range m.Out.Fields { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(field.Name + "_ ") - writeType(&b, field.Type, false, 1) - } - b.WriteString(") error {\n") - if len(m.Out.Fields) > 0 { - b.WriteString("\tvar out ") - writeType(&b, m.Out, true, 1) - b.WriteString("\n") - for _, field := range m.Out.Fields { - switch field.Type.Kind { - case idl.TypeStruct, idl.TypeArray, idl.TypeMap: - b.WriteString("\tout." + strings.Title(field.Name) + " = ") - writeType(&b, field.Type, true, 1) - b.WriteString("(" + field.Name + "_)\n") - - default: - b.WriteString("\tout." + strings.Title(field.Name) + " = " + field.Name + "_\n") - } - } - b.WriteString("\treturn c.Reply(&out)\n") - } else { - b.WriteString("\treturn c.Reply(nil)\n") - } - b.WriteString("}\n\n") - } - - b.WriteString("// Generated dummy implementations for all varlink methods\n\n") - - for _, m := range midl.Methods { - writeDocString(&b, m.Doc) - b.WriteString("func (s *VarlinkInterface) " + m.Name + "(c VarlinkCall") - for _, field := range m.In.Fields { - b.WriteString(", " + field.Name + "_ ") - writeType(&b, field.Type, false, 1) - } - b.WriteString(") error {\n" + - "\treturn c.ReplyMethodNotImplemented(\"" + midl.Name + "." + m.Name + "\")\n" + - "}\n\n") - } - - b.WriteString("// Generated method call dispatcher\n\n") - - b.WriteString("func (s *VarlinkInterface) VarlinkDispatch(call varlink.Call, methodname string) error {\n" + - "\tswitch methodname {\n") - for _, m := range midl.Methods { - b.WriteString("\tcase \"" + m.Name + "\":\n") - if len(m.In.Fields) > 0 { - b.WriteString("\t\tvar in ") - writeType(&b, m.In, true, 2) - b.WriteString("\n") - b.WriteString("\t\terr := call.GetParameters(&in)\n" + - "\t\tif err != nil {\n" + - "\t\t\treturn call.ReplyInvalidParameter(\"parameters\")\n" + - "\t\t}\n") - b.WriteString("\t\treturn s." + pkgname + "Interface." + m.Name + "(VarlinkCall{call}") - if len(m.In.Fields) > 0 { - for _, field := range m.In.Fields { - switch field.Type.Kind { - case idl.TypeStruct, idl.TypeArray, idl.TypeMap: - b.WriteString(", ") - writeType(&b, field.Type, false, 2) - b.WriteString("(in." + strings.Title(field.Name) + ")") - - default: - b.WriteString(", in." + strings.Title(field.Name)) - } - } - } - b.WriteString(")\n") - } else { - b.WriteString("\t\treturn s." + pkgname + "Interface." + m.Name + "(VarlinkCall{call})\n") - } - b.WriteString("\n") - } - b.WriteString("\tdefault:\n" + - "\t\treturn call.ReplyMethodNotFound(methodname)\n" + - "\t}\n" + - "}\n\n") - - b.WriteString("// Generated varlink interface name\n\n") - - b.WriteString("func (s *VarlinkInterface) VarlinkGetName() string {\n" + - "\treturn `" + midl.Name + "`\n" + "}\n\n") - - b.WriteString("// Generated varlink interface description\n\n") - - // Special-quote backtick, it cannot be part of a backtick-quoted string - b.WriteString("func (s *VarlinkInterface) VarlinkGetDescription() string {\n" + - "\treturn `" + strings.Replace(midl.Description, "`", "` + \"`\" + `", -1) + "\n`\n}\n\n") - - b.WriteString("// Generated service interface\n\n") - - b.WriteString("type VarlinkInterface struct {\n" + - "\t" + pkgname + "Interface\n" + - "}\n\n") - - b.WriteString("func VarlinkNew(m " + pkgname + "Interface) *VarlinkInterface {\n" + - "\treturn &VarlinkInterface{m}\n" + - "}\n") - - ret_string := b.String() - - if strings.Contains(ret_string, "json.RawMessage") { - ret_string = strings.Replace(ret_string, "@IMPORTS@", "import (\n\t\"github.com/varlink/go/varlink\"\n\t\"encoding/json\"\n)", 1) - } else { - ret_string = strings.Replace(ret_string, "@IMPORTS@", `import "github.com/varlink/go/varlink"`, 1) - } - - pretty, err := format.Source([]byte(ret_string)) - if err != nil { - return "", nil, err - } - - return pkgname, pretty, nil -} - -func generateFile(varlinkFile string) { - file, err := ioutil.ReadFile(varlinkFile) - if err != nil { - fmt.Fprintf(os.Stderr, "Error reading file '%s': %s\n", varlinkFile, err) - os.Exit(1) - } - - pkgname, b, err := generateTemplate(string(file)) - if err != nil { - fmt.Fprintf(os.Stderr, "Error parsing file '%s': %s\n", varlinkFile, err) - os.Exit(1) - } - - filename := path.Dir(varlinkFile) + "/" + pkgname + ".go" - err = ioutil.WriteFile(filename, b, 0660) - if err != nil { - fmt.Fprintf(os.Stderr, "Error writing file '%s': %s\n", filename, err) - os.Exit(1) - } -} - -func main() { - if len(os.Args) != 2 { - fmt.Printf("Usage: %s <file>\n", os.Args[0]) - os.Exit(1) - } - generateFile(os.Args[1]) -} diff --git a/vendor/github.com/varlink/go/varlink/bridge.go b/vendor/github.com/varlink/go/varlink/bridge.go deleted file mode 100644 index c478dcd88..000000000 --- a/vendor/github.com/varlink/go/varlink/bridge.go +++ /dev/null @@ -1,65 +0,0 @@ -// +build !windows - -package varlink - -import ( - "bufio" - "io" - "net" - "os" - "os/exec" -) - -type PipeCon struct { - net.Conn - cmd *exec.Cmd - reader *io.ReadCloser - writer *io.WriteCloser -} - -func (p PipeCon) Close() error { - err1 := (*p.reader).Close() - err2 := (*p.writer).Close() - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - p.cmd.Wait() - - return nil -} - -// NewBridgeWithStderr returns a new connection with the given bridge. -func NewBridgeWithStderr(bridge string, stderr io.Writer) (*Connection, error) { - //var err error - - c := Connection{} - cmd := exec.Command("sh", "-c", bridge) - cmd.Stderr = stderr - r, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - w, err := cmd.StdinPipe() - if err != nil { - return nil, err - } - c.conn = PipeCon{nil, cmd, &r, &w} - c.address = "" - c.Reader = bufio.NewReader(r) - c.Writer = bufio.NewWriter(w) - - err = cmd.Start() - if err != nil { - return nil, err - } - - return &c, nil -} - -// NewBridge returns a new connection with the given bridge. -func NewBridge(bridge string) (*Connection, error) { - return NewBridgeWithStderr(bridge, os.Stderr) -} diff --git a/vendor/github.com/varlink/go/varlink/bridge_windows.go b/vendor/github.com/varlink/go/varlink/bridge_windows.go deleted file mode 100644 index 42953b871..000000000 --- a/vendor/github.com/varlink/go/varlink/bridge_windows.go +++ /dev/null @@ -1,63 +0,0 @@ -package varlink - -import ( - "bufio" - "io" - "net" - "os" - "os/exec" -) - -type PipeCon struct { - net.Conn - cmd *exec.Cmd - reader *io.ReadCloser - writer *io.WriteCloser -} - -func (p PipeCon) Close() error { - err1 := (*p.reader).Close() - err2 := (*p.writer).Close() - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - p.cmd.Wait() - - return nil -} - -// NewBridgeWithStderr returns a new connection with the given bridge. -func NewBridgeWithStderr(bridge string, stderr io.Writer) (*Connection, error) { - //var err error - - c := Connection{} - cmd := exec.Command("cmd", "/C", bridge) - cmd.Stderr = stderr - r, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - w, err := cmd.StdinPipe() - if err != nil { - return nil, err - } - c.conn = PipeCon{nil, cmd, &r, &w} - c.address = "" - c.Reader = bufio.NewReader(r) - c.Writer = bufio.NewWriter(w) - - err = cmd.Start() - if err != nil { - return nil, err - } - - return &c, nil -} - -// NewBridge returns a new connection with the given bridge. -func NewBridge(bridge string) (*Connection, error) { - return NewBridgeWithStderr(bridge, os.Stderr) -}
\ No newline at end of file diff --git a/vendor/github.com/varlink/go/varlink/call.go b/vendor/github.com/varlink/go/varlink/call.go deleted file mode 100644 index 0eaf24aca..000000000 --- a/vendor/github.com/varlink/go/varlink/call.go +++ /dev/null @@ -1,104 +0,0 @@ -package varlink - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "net" - "strings" -) - -// Call is a method call retrieved by a Service. The connection from the -// client can be terminated by returning an error from the call instead -// of sending a reply or error reply. -type Call struct { - *bufio.Reader - *bufio.Writer - Conn *net.Conn - Request *[]byte - In *serviceCall - Continues bool - Upgrade bool -} - -// WantsMore indicates if the calling client accepts more than one reply to this method call. -func (c *Call) WantsMore() bool { - return c.In.More -} - -// WantsUpgrade indicates that the calling client wants the connection to be upgraded. -func (c *Call) WantsUpgrade() bool { - return c.In.Upgrade -} - -// IsOneway indicate that the calling client does not expect a reply. -func (c *Call) IsOneway() bool { - return c.In.Oneway -} - -// GetParameters retrieves the method call parameters. -func (c *Call) GetParameters(p interface{}) error { - if c.In.Parameters == nil { - return fmt.Errorf("empty parameters") - } - return json.Unmarshal(*c.In.Parameters, p) -} - -func (c *Call) sendMessage(r *serviceReply) error { - if c.In.Oneway { - return nil - } - - b, e := json.Marshal(r) - if e != nil { - return e - } - - b = append(b, 0) - _, e = c.Writer.Write(b) - if e != nil { - if e == io.EOF { - return io.ErrUnexpectedEOF - } - return e - } - e = c.Writer.Flush() - if e == io.EOF { - return io.ErrUnexpectedEOF - } - return e -} - -// Reply sends a reply to this method call. -func (c *Call) Reply(parameters interface{}) error { - if !c.Continues { - return c.sendMessage(&serviceReply{ - Parameters: parameters, - }) - } - - if !c.In.More { - return fmt.Errorf("call did not set more, it does not expect continues") - } - - return c.sendMessage(&serviceReply{ - Continues: true, - Parameters: parameters, - }) -} - -// ReplyError sends an error reply to this method call. -func (c *Call) ReplyError(name string, parameters interface{}) error { - r := strings.LastIndex(name, ".") - if r <= 0 { - return fmt.Errorf("invalid error name") - } - if name[:r] == "org.varlink.service" { - return fmt.Errorf("refused to send org.varlink.service errors") - } - return c.sendMessage(&serviceReply{ - Error: name, - Parameters: parameters, - }) -} diff --git a/vendor/github.com/varlink/go/varlink/connection.go b/vendor/github.com/varlink/go/varlink/connection.go deleted file mode 100644 index 596caa825..000000000 --- a/vendor/github.com/varlink/go/varlink/connection.go +++ /dev/null @@ -1,291 +0,0 @@ -package varlink - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "net" - "strings" -) - -// Message flags for Send(). More indicates that the client accepts more than one method -// reply to this call. Oneway requests, that the service must not send a method reply to -// this call. Continues indicates that the service will send more than one reply. -const ( - More = 1 << iota - Oneway = 1 << iota - Continues = 1 << iota - Upgrade = 1 << iota -) - -// Error is a varlink error returned from a method call. -type Error struct { - Name string - Parameters interface{} -} - -func (e *Error) DispatchError() error { - errorRawParameters := e.Parameters.(*json.RawMessage) - - switch e.Name { - case "org.varlink.service.InterfaceNotFound": - var param InterfaceNotFound - if errorRawParameters != nil { - err := json.Unmarshal(*errorRawParameters, ¶m) - if err != nil { - return e - } - } - return ¶m - case "org.varlink.service.MethodNotFound": - var param MethodNotFound - if errorRawParameters != nil { - err := json.Unmarshal(*errorRawParameters, ¶m) - if err != nil { - return e - } - } - return ¶m - case "org.varlink.service.MethodNotImplemented": - var param MethodNotImplemented - if errorRawParameters != nil { - err := json.Unmarshal(*errorRawParameters, ¶m) - if err != nil { - return e - } - } - return ¶m - case "org.varlink.service.InvalidParameter": - var param InvalidParameter - if errorRawParameters != nil { - err := json.Unmarshal(*errorRawParameters, ¶m) - if err != nil { - return e - } - } - return ¶m - } - return e -} - -// Error returns the fully-qualified varlink error name. -func (e *Error) Error() string { - return e.Name -} - -// Connection is a connection from a client to a service. -type Connection struct { - io.Closer - address string - conn net.Conn - Reader *bufio.Reader - Writer *bufio.Writer -} - -// Send sends a method call. It returns a receive() function which is called to retrieve the method reply. -// If Send() is called with the `More`flag and the receive() function carries the `Continues` flag, receive() -// can be called multiple times to retrieve multiple replies. -func (c *Connection) Send(method string, parameters interface{}, flags uint64) (func(interface{}) (uint64, error), error) { - type call struct { - Method string `json:"method"` - Parameters interface{} `json:"parameters,omitempty"` - More bool `json:"more,omitempty"` - Oneway bool `json:"oneway,omitempty"` - Upgrade bool `json:"upgrade,omitempty"` - } - - if (flags&More != 0) && (flags&Oneway != 0) { - return nil, &Error{ - Name: "org.varlink.InvalidParameter", - Parameters: "oneway", - } - } - - if (flags&More != 0) && (flags&Upgrade != 0) { - return nil, &Error{ - Name: "org.varlink.InvalidParameter", - Parameters: "more", - } - } - - m := call{ - Method: method, - Parameters: parameters, - More: flags&More != 0, - Oneway: flags&Oneway != 0, - Upgrade: flags&Upgrade != 0, - } - b, err := json.Marshal(m) - if err != nil { - return nil, err - } - - b = append(b, 0) - _, err = c.Writer.Write(b) - if err != nil { - if err == io.EOF { - return nil, io.ErrUnexpectedEOF - } - return nil, err - } - - err = c.Writer.Flush() - if err != nil { - if err == io.EOF { - return nil, io.ErrUnexpectedEOF - } - return nil, err - } - - receive := func(out_parameters interface{}) (uint64, error) { - type reply struct { - Parameters *json.RawMessage `json:"parameters"` - Continues bool `json:"continues"` - Error string `json:"error"` - } - - out, err := c.Reader.ReadBytes('\x00') - if err != nil { - if err == io.EOF { - return 0, io.ErrUnexpectedEOF - } - return 0, err - } - - var m reply - err = json.Unmarshal(out[:len(out)-1], &m) - if err != nil { - return 0, err - } - - if m.Error != "" { - e := &Error{ - Name: m.Error, - Parameters: m.Parameters, - } - return 0, e.DispatchError() - } - - if m.Parameters != nil { - json.Unmarshal(*m.Parameters, out_parameters) - } - - if m.Continues { - return Continues, nil - } - - return 0, nil - } - - return receive, nil -} - -// Call sends a method call and returns the method reply. -func (c *Connection) Call(method string, parameters interface{}, out_parameters interface{}) error { - receive, err := c.Send(method, ¶meters, 0) - if err != nil { - return err - } - - _, err = receive(out_parameters) - return err -} - -// GetInterfaceDescription requests the interface description string from the service. -func (c *Connection) GetInterfaceDescription(name string) (string, error) { - type request struct { - Interface string `json:"interface"` - } - type reply struct { - Description string `json:"description"` - } - - var r reply - err := c.Call("org.varlink.service.GetInterfaceDescription", request{Interface: name}, &r) - if err != nil { - return "", err - } - - return r.Description, nil -} - -// GetInfo requests information about the service. -func (c *Connection) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error { - type reply struct { - Vendor string `json:"vendor"` - Product string `json:"product"` - Version string `json:"version"` - URL string `json:"url"` - Interfaces []string `json:"interfaces"` - } - - var r reply - err := c.Call("org.varlink.service.GetInfo", nil, &r) - if err != nil { - return err - } - - if vendor != nil { - *vendor = r.Vendor - } - if product != nil { - *product = r.Product - } - if version != nil { - *version = r.Version - } - if url != nil { - *url = r.URL - } - if interfaces != nil { - *interfaces = r.Interfaces - } - - return nil -} - -// Close terminates the connection. -func (c *Connection) Close() error { - return c.conn.Close() -} - -// NewConnection returns a new connection to the given address. -func NewConnection(address string) (*Connection, error) { - var err error - - words := strings.SplitN(address, ":", 2) - - if len(words) != 2 { - return nil, fmt.Errorf("Protocol missing") - } - - protocol := words[0] - addr := words[1] - - // Ignore parameters after ';' - words = strings.SplitN(addr, ";", 2) - if words != nil { - addr = words[0] - } - - switch protocol { - case "unix": - break - - case "tcp": - break - } - - c := Connection{} - c.conn, err = net.Dial(protocol, addr) - if err != nil { - return nil, err - } - - c.address = address - c.Reader = bufio.NewReader(c.conn) - c.Writer = bufio.NewWriter(c.conn) - - return &c, nil -} diff --git a/vendor/github.com/varlink/go/varlink/doc.go b/vendor/github.com/varlink/go/varlink/doc.go deleted file mode 100644 index de1ed2380..000000000 --- a/vendor/github.com/varlink/go/varlink/doc.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Package varlink provides varlink client and server implementations. See http://varlink.org -for more information about varlink. - -Example varlink interface definition in a org.example.this.varlink file: - interface org.example.this - - method Ping(in: string) -> (out: string) - -Generated Go module in a orgexamplethis/orgexamplethis.go file. The generated module -provides reply methods for all methods specified in the varlink interface description. -The stub implementations return a MethodNotImplemented error; the service implementation -using this module will override the methods with its own implementation. - // Generated with github.com/varlink/go/cmd/varlink-go-interface-generator - package orgexamplethis - - import "github.com/varlink/go/varlink" - - type orgexamplethisInterface interface { - Ping(c VarlinkCall, in string) error - } - - type VarlinkCall struct{ varlink.Call } - - func (c *VarlinkCall) ReplyPing(out string) error { - var out struct { - Out string `json:"out,omitempty"` - } - out.Out = out - return c.Reply(&out) - } - - func (s *VarlinkInterface) Ping(c VarlinkCall, in string) error { - return c.ReplyMethodNotImplemented("Ping") - } - - [...] - -Service implementing the interface and its method: - import ("orgexamplethis") - - type Data struct { - orgexamplethis.VarlinkInterface - data string - } - - data := Data{data: "test"} - - func (d *Data) Ping(call orgexamplethis.VarlinkCall, ping string) error { - return call.ReplyPing(ping) - } - - service, _ = varlink.NewService( - "Example", - "This", - "1", - "https://example.org/this", - ) - - service.RegisterInterface(orgexamplethis.VarlinkNew(&data)) - err := service.Listen("unix:/run/org.example.this", 0) -*/ -package varlink diff --git a/vendor/github.com/varlink/go/varlink/idl/idl.go b/vendor/github.com/varlink/go/varlink/idl/idl.go deleted file mode 100644 index b09b4798b..000000000 --- a/vendor/github.com/varlink/go/varlink/idl/idl.go +++ /dev/null @@ -1,497 +0,0 @@ -// Package idl provides a varlink interface description parser. -package idl - -import ( - "bytes" - "fmt" - "regexp" -) - -// Valid TypeKind values. -const ( - TypeBool = iota - TypeInt - TypeFloat - TypeString - TypeObject - TypeArray - TypeMaybe - TypeMap - TypeStruct - TypeEnum - TypeAlias -) - -// TypeKind specifies the type of an Type. -type TypeKind uint - -// Type represents a varlink type. Types are method input and output parameters, -// error output parameters, or custom defined types in the interface description. -type Type struct { - Kind TypeKind - ElementType *Type - Alias string - Fields []TypeField -} - -// TypeField is a named member of a TypeStruct. -type TypeField struct { - Name string - Type *Type -} - -// Alias represents a named Type in the interface description. -type Alias struct { - Name string - Doc string - Type *Type -} - -// Method represents a method defined in the interface description. -type Method struct { - Name string - Doc string - In *Type - Out *Type -} - -// Error represents an error defined in the interface description. -type Error struct { - Name string - Doc string - Type *Type -} - -// IDL represents a parsed varlink interface description with types, methods, errors and -// documentation. -type IDL struct { - Name string - Doc string - Description string - Members []interface{} - Aliases []*Alias - Methods []*Method - Errors []*Error -} - -type parser struct { - input string - position int - lineStart int - lastComment bytes.Buffer -} - -func (p *parser) next() int { - r := -1 - - if p.position < len(p.input) { - r = int(p.input[p.position]) - } - - p.position++ - return r -} - -func (p *parser) backup() { - p.position-- -} - -func (p *parser) advance() bool { - for { - char := p.next() - - if char == '\n' { - p.lineStart = p.position - p.lastComment.Reset() - - } else if char == ' ' || char == '\t' { - // ignore - - } else if char == '#' { - p.next() - start := p.position - for { - c := p.next() - if c < 0 || c == '\n' { - p.backup() - break - } - } - if p.lastComment.Len() > 0 { - p.lastComment.WriteByte('\n') - } - p.lastComment.WriteString(p.input[start:p.position]) - p.next() - - } else { - p.backup() - break - } - } - - return p.position < len(p.input) -} - -func (p *parser) advanceOnLine() { - for { - char := p.next() - if char != ' ' { - p.backup() - return - } - } -} - -func (p *parser) readKeyword() string { - start := p.position - - for { - char := p.next() - if char < 'a' || char > 'z' { - p.backup() - break - } - } - - return p.input[start:p.position] -} - -func (p *parser) readInterfaceName() string { - start := p.position - dnrx := regexp.MustCompile(`^[a-z]+(\.[a-z0-9]+([-][a-z0-9]+)*)+`) - name := dnrx.FindString(p.input[start:]) - if name != "" { - if len(name) > 255 { - return "" - } - p.position += len(name) - return name - } - xdnrx := regexp.MustCompile(`^xn--[a-z0-9]+(\.[a-z0-9]+([-][a-z0-9]+)*)+`) - name = xdnrx.FindString(p.input[start:]) - if name != "" { - if len(name) > 255 { - return "" - } - p.position += len(name) - return name - } - return "" -} - -func (p *parser) readFieldName() string { - start := p.position - - char := p.next() - if char < 'a' || char > 'z' { - p.backup() - return "" - } - - for { - char := p.next() - if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') && char != '_' { - p.backup() - break - } - } - - return p.input[start:p.position] -} - -func (p *parser) readTypeName() string { - start := p.position - - for { - char := p.next() - if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') { - p.backup() - break - } - } - - return p.input[start:p.position] -} - -func (p *parser) readStructType() *Type { - if p.next() != '(' { - p.backup() - return nil - } - - t := &Type{Kind: TypeStruct} - t.Fields = make([]TypeField, 0) - - char := p.next() - if char != ')' { - p.backup() - - for { - field := TypeField{} - - p.advance() - field.Name = p.readFieldName() - if field.Name == "" { - return nil - } - - p.advance() - - // Enums have no types, they are just a list of names - if p.next() == ':' { - if t.Kind == TypeEnum { - return nil - } - - p.advance() - field.Type = p.readType() - if field.Type == nil { - return nil - } - - } else { - t.Kind = TypeEnum - p.backup() - } - - t.Fields = append(t.Fields, field) - - p.advance() - char = p.next() - if char != ',' { - break - } - } - - if char != ')' { - return nil - } - } - - return t -} - -func (p *parser) readType() *Type { - var t *Type - - switch p.next() { - case '?': - e := p.readType() - if e == nil { - return nil - } - if e.Kind == TypeMaybe { - return nil - } - t = &Type{Kind: TypeMaybe, ElementType: e} - - case '[': - var kind TypeKind - - switch p.readKeyword() { - case "string": - kind = TypeMap - - case "": - kind = TypeArray - - default: - return nil - } - - if p.next() != ']' { - return nil - } - e := p.readType() - if e == nil { - return nil - } - t = &Type{Kind: kind, ElementType: e} - - default: - p.backup() - if keyword := p.readKeyword(); keyword != "" { - switch keyword { - case "bool": - t = &Type{Kind: TypeBool} - - case "int": - t = &Type{Kind: TypeInt} - - case "float": - t = &Type{Kind: TypeFloat} - - case "string": - t = &Type{Kind: TypeString} - - case "object": - t = &Type{Kind: TypeObject} - } - - } else if name := p.readTypeName(); name != "" { - t = &Type{Kind: TypeAlias, Alias: name} - - } else if t = p.readStructType(); t == nil { - return nil - } - } - - return t -} - -func (p *parser) readAlias(idl *IDL) (*Alias, error) { - a := &Alias{} - - p.advance() - a.Doc = p.lastComment.String() - a.Name = p.readTypeName() - if a.Name == "" { - return nil, fmt.Errorf("missing type name") - } - - p.advance() - a.Type = p.readType() - if a.Type == nil { - return nil, fmt.Errorf("missing type declaration") - } - - return a, nil -} - -func (p *parser) readMethod(idl *IDL) (*Method, error) { - m := &Method{} - - p.advance() - m.Doc = p.lastComment.String() - m.Name = p.readTypeName() - if m.Name == "" { - return nil, fmt.Errorf("missing method type") - } - - p.advance() - m.In = p.readType() - if m.In == nil { - return nil, fmt.Errorf("missing method input") - } - - p.advance() - one := p.next() - two := p.next() - if (one != '-') || two != '>' { - return nil, fmt.Errorf("missing method '->' operator") - } - - p.advance() - m.Out = p.readType() - if m.Out == nil { - return nil, fmt.Errorf("missing method output") - } - - return m, nil -} - -func (p *parser) readError(idl *IDL) (*Error, error) { - e := &Error{} - - p.advance() - e.Doc = p.lastComment.String() - e.Name = p.readTypeName() - if e.Name == "" { - return nil, fmt.Errorf("missing error name") - } - - p.advanceOnLine() - e.Type = p.readType() - - return e, nil -} - -func (p *parser) readIDL() (*IDL, error) { - if keyword := p.readKeyword(); keyword != "interface" { - return nil, fmt.Errorf("missing interface keyword") - } - - idl := &IDL{ - Members: make([]interface{}, 0), - Aliases: make([]*Alias, 0), - Methods: make([]*Method, 0), - Errors: make([]*Error, 0), - } - - p.advance() - idl.Doc = p.lastComment.String() - idl.Name = p.readInterfaceName() - if idl.Name == "" { - return nil, fmt.Errorf("interface name") - } - - // Check for duplicates - members := make(map[string]struct{}, 0) - - for { - if !p.advance() { - break - } - - switch keyword := p.readKeyword(); keyword { - case "type": - a, err := p.readAlias(idl) - if err != nil { - return nil, err - } - if _, ok := members[a.Name]; ok { - return nil, fmt.Errorf("type `%s` already defined", a.Name) - } - members[a.Name] = struct{}{} - idl.Aliases = append(idl.Aliases, a) - idl.Members = append(idl.Members, a) - - case "method": - m, err := p.readMethod(idl) - if err != nil { - return nil, err - } - if _, ok := members[m.Name]; ok { - return nil, fmt.Errorf("method `%s` already defined", m.Name) - } - members[m.Name] = struct{}{} - idl.Methods = append(idl.Methods, m) - idl.Members = append(idl.Members, m) - - case "error": - e, err := p.readError(idl) - if err != nil { - return nil, err - } - if _, ok := members[e.Name]; ok { - return nil, fmt.Errorf("error `%s` already defined", e.Name) - } - members[e.Name] = struct{}{} - idl.Errors = append(idl.Errors, e) - idl.Members = append(idl.Members, e) - - default: - return nil, fmt.Errorf("unknown keyword '%s'", keyword) - } - } - - return idl, nil -} - -// New parses a varlink interface description. -func New(description string) (*IDL, error) { - p := &parser{input: description} - - p.advance() - idl, err := p.readIDL() - if err != nil { - return nil, err - } - - if len(idl.Methods) == 0 { - return nil, fmt.Errorf("no methods defined") - } - - idl.Description = description - return idl, nil -} diff --git a/vendor/github.com/varlink/go/varlink/orgvarlinkservice.go b/vendor/github.com/varlink/go/varlink/orgvarlinkservice.go deleted file mode 100644 index 600c99d34..000000000 --- a/vendor/github.com/varlink/go/varlink/orgvarlinkservice.go +++ /dev/null @@ -1,162 +0,0 @@ -package varlink - -// The requested interface was not found. -type InterfaceNotFound struct { - Interface string `json:"interface"` -} - -func (e InterfaceNotFound) Error() string { - return "org.varlink.service.InterfaceNotFound" -} - -// The requested method was not found -type MethodNotFound struct { - Method string `json:"method"` -} - -func (e MethodNotFound) Error() string { - return "org.varlink.service.MethodNotFound" -} - -// The interface defines the requested method, but the service does not -// implement it. -type MethodNotImplemented struct { - Method string `json:"method"` -} - -func (e MethodNotImplemented) Error() string { - return "org.varlink.service.MethodNotImplemented" -} - -// One of the passed parameters is invalid. -type InvalidParameter struct { - Parameter string `json:"parameter"` -} - -func (e InvalidParameter) Error() string { - return "org.varlink.service.InvalidParameter" -} - -func doReplyError(c *Call, name string, parameters interface{}) error { - return c.sendMessage(&serviceReply{ - Error: name, - Parameters: parameters, - }) -} - -// ReplyInterfaceNotFound sends a org.varlink.service errror reply to this method call -func (c *Call) ReplyInterfaceNotFound(interfaceA string) error { - var out InterfaceNotFound - out.Interface = interfaceA - return doReplyError(c, "org.varlink.service.InterfaceNotFound", &out) -} - -// ReplyMethodNotFound sends a org.varlink.service errror reply to this method call -func (c *Call) ReplyMethodNotFound(method string) error { - var out MethodNotFound - out.Method = method - return doReplyError(c, "org.varlink.service.MethodNotFound", &out) -} - -// ReplyMethodNotImplemented sends a org.varlink.service errror reply to this method call -func (c *Call) ReplyMethodNotImplemented(method string) error { - var out MethodNotImplemented - out.Method = method - return doReplyError(c, "org.varlink.service.MethodNotImplemented", &out) -} - -// ReplyInvalidParameter sends a org.varlink.service errror reply to this method call -func (c *Call) ReplyInvalidParameter(parameter string) error { - var out InvalidParameter - out.Parameter = parameter - return doReplyError(c, "org.varlink.service.InvalidParameter", &out) -} - -func (c *Call) replyGetInfo(vendor string, product string, version string, url string, interfaces []string) error { - var out struct { - Vendor string `json:"vendor,omitempty"` - Product string `json:"product,omitempty"` - Version string `json:"version,omitempty"` - URL string `json:"url,omitempty"` - Interfaces []string `json:"interfaces,omitempty"` - } - out.Vendor = vendor - out.Product = product - out.Version = version - out.URL = url - out.Interfaces = interfaces - return c.Reply(&out) -} - -func (c *Call) replyGetInterfaceDescription(description string) error { - var out struct { - Description string `json:"description,omitempty"` - } - out.Description = description - return c.Reply(&out) -} - -func (s *Service) orgvarlinkserviceDispatch(c Call, methodname string) error { - switch methodname { - case "GetInfo": - return s.getInfo(c) - case "GetInterfaceDescription": - var in struct { - Interface string `json:"interface"` - } - err := c.GetParameters(&in) - if err != nil { - return c.ReplyInvalidParameter("parameters") - } - return s.getInterfaceDescription(c, in.Interface) - - default: - return c.ReplyMethodNotFound(methodname) - } -} - -func (s *orgvarlinkserviceInterface) VarlinkDispatch(call Call, methodname string) error { - return nil -} - -func (s *orgvarlinkserviceInterface) VarlinkGetName() string { - return `org.varlink.service` -} - -func (s *orgvarlinkserviceInterface) VarlinkGetDescription() string { - return `# The Varlink Service Interface is provided by every varlink service. It -# describes the service and the interfaces it implements. -interface org.varlink.service - -# Get a list of all the interfaces a service provides and information -# about the implementation. -method GetInfo() -> ( - vendor: string, - product: string, - version: string, - url: string, - interfaces: []string -) - -# Get the description of an interface that is implemented by this service. -method GetInterfaceDescription(interface: string) -> (description: string) - -# The requested interface was not found. -error InterfaceNotFound (interface: string) - -# The requested method was not found -error MethodNotFound (method: string) - -# The interface defines the requested method, but the service does not -# implement it. -error MethodNotImplemented (method: string) - -# One of the passed parameters is invalid. -error InvalidParameter (parameter: string)` -} - -type orgvarlinkserviceInterface struct{} - -func orgvarlinkserviceNew() *orgvarlinkserviceInterface { - return &orgvarlinkserviceInterface{} -} diff --git a/vendor/github.com/varlink/go/varlink/resolver.go b/vendor/github.com/varlink/go/varlink/resolver.go deleted file mode 100644 index f0f4487d2..000000000 --- a/vendor/github.com/varlink/go/varlink/resolver.go +++ /dev/null @@ -1,92 +0,0 @@ -package varlink - -// ResolverAddress is the well-known address of the varlink interface resolver, -// it translates varlink interface names to varlink service addresses. -const ResolverAddress = "unix:/run/org.varlink.resolver" - -// Resolver resolves varlink interface names to varlink addresses -type Resolver struct { - address string - conn *Connection -} - -// Resolve resolves a varlink interface name to a varlink address. -func (r *Resolver) Resolve(iface string) (string, error) { - type request struct { - Interface string `json:"interface"` - } - type reply struct { - Address string `json:"address"` - } - - /* don't ask the resolver for itself */ - if iface == "org.varlink.resolver" { - return r.address, nil - } - - var rep reply - err := r.conn.Call("org.varlink.resolver.Resolve", &request{Interface: iface}, &rep) - if err != nil { - return "", err - } - - return rep.Address, nil -} - -// GetInfo requests information about the resolver. -func (r *Resolver) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error { - type reply struct { - Vendor string - Product string - Version string - URL string - Interfaces []string - } - - var rep reply - err := r.conn.Call("org.varlink.resolver.GetInfo", nil, &rep) - if err != nil { - return err - } - - if vendor != nil { - *vendor = rep.Vendor - } - if product != nil { - *product = rep.Product - } - if version != nil { - *version = rep.Version - } - if url != nil { - *url = rep.URL - } - if interfaces != nil { - *interfaces = rep.Interfaces - } - - return nil -} - -// Close terminates the resolver. -func (r *Resolver) Close() error { - return r.conn.Close() -} - -// NewResolver returns a new resolver connected to the given address. -func NewResolver(address string) (*Resolver, error) { - if address == "" { - address = ResolverAddress - } - - c, err := NewConnection(address) - if err != nil { - return nil, err - } - r := Resolver{ - address: address, - conn: c, - } - - return &r, nil -} diff --git a/vendor/github.com/varlink/go/varlink/service.go b/vendor/github.com/varlink/go/varlink/service.go deleted file mode 100644 index bf13aa1de..000000000 --- a/vendor/github.com/varlink/go/varlink/service.go +++ /dev/null @@ -1,375 +0,0 @@ -package varlink - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "os" - "strings" - "sync" - "time" -) - -type dispatcher interface { - VarlinkDispatch(c Call, methodname string) error - VarlinkGetName() string - VarlinkGetDescription() string -} - -type serviceCall struct { - Method string `json:"method"` - Parameters *json.RawMessage `json:"parameters,omitempty"` - More bool `json:"more,omitempty"` - Oneway bool `json:"oneway,omitempty"` - Upgrade bool `json:"upgrade,omitempty"` -} - -type serviceReply struct { - Parameters interface{} `json:"parameters,omitempty"` - Continues bool `json:"continues,omitempty"` - Error string `json:"error,omitempty"` -} - -// Service represents an active varlink service. In addition to the registered custom varlink Interfaces, every service -// implements the org.varlink.service interface which allows clients to retrieve information about the -// running service. -type Service struct { - vendor string - product string - version string - url string - interfaces map[string]dispatcher - names []string - descriptions map[string]string - running bool - listener net.Listener - conncounter int64 - mutex sync.Mutex - protocol string - address string -} - -// ServiceTimoutError helps API users to special-case timeouts. -type ServiceTimeoutError struct{} - -func (ServiceTimeoutError) Error() string { - return "service timeout" -} - -func (s *Service) getInfo(c Call) error { - return c.replyGetInfo(s.vendor, s.product, s.version, s.url, s.names) -} - -func (s *Service) getInterfaceDescription(c Call, name string) error { - if name == "" { - return c.ReplyInvalidParameter("interface") - } - - description, ok := s.descriptions[name] - if !ok { - return c.ReplyInvalidParameter("interface") - } - - return c.replyGetInterfaceDescription(description) -} - -func (s *Service) HandleMessage(conn *net.Conn, reader *bufio.Reader, writer *bufio.Writer, request []byte) error { - var in serviceCall - - err := json.Unmarshal(request, &in) - - if err != nil { - return err - } - - c := Call{ - Conn: conn, - Reader: reader, - Writer: writer, - In: &in, - Request: &request, - } - - r := strings.LastIndex(in.Method, ".") - if r <= 0 { - return c.ReplyInvalidParameter("method") - } - - interfacename := in.Method[:r] - methodname := in.Method[r+1:] - - if interfacename == "org.varlink.service" { - return s.orgvarlinkserviceDispatch(c, methodname) - } - - // Find the interface and method in our service - iface, ok := s.interfaces[interfacename] - if !ok { - return c.ReplyInterfaceNotFound(interfacename) - } - - return iface.VarlinkDispatch(c, methodname) -} - -// Shutdown shuts down the listener of a running service. -func (s *Service) Shutdown() { - s.running = false - s.mutex.Lock() - if s.listener != nil { - s.listener.Close() - } - s.mutex.Unlock() -} - -func (s *Service) handleConnection(conn net.Conn, wg *sync.WaitGroup) { - defer func() { s.mutex.Lock(); s.conncounter--; s.mutex.Unlock(); wg.Done() }() - reader := bufio.NewReader(conn) - writer := bufio.NewWriter(conn) - - for { - request, err := reader.ReadBytes('\x00') - if err != nil { - break - } - - err = s.HandleMessage(&conn, reader, writer, request[:len(request)-1]) - if err != nil { - // FIXME: report error - //fmt.Fprintf(os.Stderr, "handleMessage: %v", err) - break - } - } - - conn.Close() -} - -func (s *Service) teardown() { - s.mutex.Lock() - s.listener = nil - s.running = false - s.protocol = "" - s.address = "" - s.mutex.Unlock() -} - -func (s *Service) parseAddress(address string) error { - words := strings.SplitN(address, ":", 2) - if len(words) != 2 { - return fmt.Errorf("Unknown protocol") - } - - s.protocol = words[0] - s.address = words[1] - - // Ignore parameters after ';' - words = strings.SplitN(s.address, ";", 2) - if words != nil { - s.address = words[0] - } - - switch s.protocol { - case "unix": - break - case "tcp": - break - - default: - return fmt.Errorf("Unknown protocol") - } - - return nil -} - -func (s *Service) GetListener() (*net.Listener, error) { - s.mutex.Lock() - l := s.listener - s.mutex.Unlock() - return &l, nil -} - -func (s *Service) setListener() error { - l := activationListener() - if l == nil { - if s.protocol == "unix" && s.address[0] != '@' { - os.Remove(s.address) - } - - var err error - l, err = net.Listen(s.protocol, s.address) - if err != nil { - return err - } - - if s.protocol == "unix" && s.address[0] != '@' { - l.(*net.UnixListener).SetUnlinkOnClose(true) - } - } - - s.mutex.Lock() - s.listener = l - s.mutex.Unlock() - - return nil -} - -func (s *Service) refreshTimeout(timeout time.Duration) error { - switch l := s.listener.(type) { - case *net.UnixListener: - if err := l.SetDeadline(time.Now().Add(timeout)); err != nil { - return err - } - case *net.TCPListener: - if err := l.SetDeadline(time.Now().Add(timeout)); err != nil { - return err - } - - } - return nil -} - -// Listen starts a Service. -func (s *Service) Bind(address string) error { - s.mutex.Lock() - if s.running { - s.mutex.Unlock() - return fmt.Errorf("Init(): already running") - } - s.mutex.Unlock() - - s.parseAddress(address) - - err := s.setListener() - if err != nil { - return err - } - return nil -} - -// Listen starts a Service. -func (s *Service) Listen(address string, timeout time.Duration) error { - var wg sync.WaitGroup - defer func() { s.teardown(); wg.Wait() }() - - err := s.Bind(address) - if err != nil { - return err - } - - s.mutex.Lock() - s.running = true - l := s.listener - s.mutex.Unlock() - - for s.running { - if timeout != 0 { - if err := s.refreshTimeout(timeout); err != nil { - return err - } - } - conn, err := l.Accept() - if err != nil { - if err.(net.Error).Timeout() { - s.mutex.Lock() - if s.conncounter == 0 { - s.mutex.Unlock() - return ServiceTimeoutError{} - } - s.mutex.Unlock() - continue - } - if !s.running { - return nil - } - return err - } - s.mutex.Lock() - s.conncounter++ - s.mutex.Unlock() - wg.Add(1) - go s.handleConnection(conn, &wg) - } - - return nil -} - -// Listen starts a Service. -func (s *Service) DoListen(timeout time.Duration) error { - var wg sync.WaitGroup - defer func() { s.teardown(); wg.Wait() }() - - s.mutex.Lock() - l := s.listener - s.mutex.Unlock() - - if l == nil { - return fmt.Errorf("No listener set") - } - - s.mutex.Lock() - s.running = true - s.mutex.Unlock() - - for s.running { - if timeout != 0 { - if err := s.refreshTimeout(timeout); err != nil { - return err - } - } - conn, err := l.Accept() - if err != nil { - if err.(net.Error).Timeout() { - s.mutex.Lock() - if s.conncounter == 0 { - s.mutex.Unlock() - return ServiceTimeoutError{} - } - s.mutex.Unlock() - continue - } - if !s.running { - return nil - } - return err - } - s.mutex.Lock() - s.conncounter++ - s.mutex.Unlock() - wg.Add(1) - go s.handleConnection(conn, &wg) - } - - return nil -} - -// RegisterInterface registers a varlink.Interface containing struct to the Service -func (s *Service) RegisterInterface(iface dispatcher) error { - name := iface.VarlinkGetName() - if _, ok := s.interfaces[name]; ok { - return fmt.Errorf("interface '%s' already registered", name) - } - - if s.running { - return fmt.Errorf("service is already running") - } - s.interfaces[name] = iface - s.descriptions[name] = iface.VarlinkGetDescription() - s.names = append(s.names, name) - - return nil -} - -// NewService creates a new Service which implements the list of given varlink interfaces. -func NewService(vendor string, product string, version string, url string) (*Service, error) { - s := Service{ - vendor: vendor, - product: product, - version: version, - url: url, - interfaces: make(map[string]dispatcher), - descriptions: make(map[string]string), - } - err := s.RegisterInterface(orgvarlinkserviceNew()) - - return &s, err -} diff --git a/vendor/github.com/varlink/go/varlink/socketactivation.go b/vendor/github.com/varlink/go/varlink/socketactivation.go deleted file mode 100644 index a64c0dc8e..000000000 --- a/vendor/github.com/varlink/go/varlink/socketactivation.go +++ /dev/null @@ -1,63 +0,0 @@ -// +build !windows - -package varlink - -import ( - "net" - "os" - "strconv" - "strings" - "syscall" -) - -func activationListener() net.Listener { - pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) - if err != nil || pid != os.Getpid() { - return nil - } - - nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) - if err != nil || nfds < 1 { - return nil - } - - fd := -1 - - // If more than one file descriptor is passed, find the - // "varlink" tag. The first file descriptor is always 3. - if nfds > 1 { - fdnames, set := os.LookupEnv("LISTEN_FDNAMES") - if !set { - return nil - } - - names := strings.Split(fdnames, ":") - if len(names) != nfds { - return nil - } - - for i, name := range names { - if name == "varlink" { - fd = 3 + i - break - } - } - - if fd < 0 { - return nil - } - - } else { - fd = 3 - } - - syscall.CloseOnExec(fd) - - file := os.NewFile(uintptr(fd), "varlink") - listener, err := net.FileListener(file) - if err != nil { - return nil - } - - return listener -} diff --git a/vendor/github.com/varlink/go/varlink/socketactivation_windows.go b/vendor/github.com/varlink/go/varlink/socketactivation_windows.go deleted file mode 100644 index fb0894531..000000000 --- a/vendor/github.com/varlink/go/varlink/socketactivation_windows.go +++ /dev/null @@ -1,7 +0,0 @@ -package varlink - -import "net" - -func activationListener() net.Listener { - return nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 674b7a4e4..0c6b4de57 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -67,7 +67,7 @@ github.com/containernetworking/plugins/pkg/utils/hwaddr github.com/containernetworking/plugins/pkg/utils/sysctl github.com/containernetworking/plugins/plugins/ipam/host-local/backend github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator -# github.com/containers/buildah v1.18.0 +# github.com/containers/buildah v1.18.1-0.20201125084616-dd26b137459c github.com/containers/buildah github.com/containers/buildah/bind github.com/containers/buildah/chroot @@ -84,9 +84,7 @@ github.com/containers/buildah/pkg/manifests github.com/containers/buildah/pkg/overlay github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/rusage -github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented -github.com/containers/buildah/pkg/umask github.com/containers/buildah/util # github.com/containers/common v0.29.0 github.com/containers/common/pkg/apparmor @@ -100,7 +98,9 @@ github.com/containers/common/pkg/report github.com/containers/common/pkg/report/camelcase github.com/containers/common/pkg/retry github.com/containers/common/pkg/seccomp +github.com/containers/common/pkg/subscriptions github.com/containers/common/pkg/sysinfo +github.com/containers/common/pkg/umask github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon/runner/config @@ -512,7 +512,7 @@ github.com/seccomp/libseccomp-golang # github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus github.com/sirupsen/logrus/hooks/syslog -# github.com/spf13/cobra v1.1.1 => github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706 +# github.com/spf13/cobra v1.1.1 github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag @@ -549,10 +549,6 @@ github.com/ulikunitz/xz github.com/ulikunitz/xz/internal/hash github.com/ulikunitz/xz/internal/xlog github.com/ulikunitz/xz/lzma -# github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b -github.com/varlink/go/cmd/varlink-go-interface-generator -github.com/varlink/go/varlink -github.com/varlink/go/varlink/idl # github.com/vbatts/tar-split v0.11.1 github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/tar/asm diff --git a/version/version.go b/version/version.go index a62648eea..0bba0147b 100644 --- a/version/version.go +++ b/version/version.go @@ -8,9 +8,9 @@ import ( // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -var Version = semver.MustParse("2.2.0-dev") +var Version = semver.MustParse("3.0.0-dev") // APIVersion is the version for the remote // client API. It is used to determine compatibility // between a remote podman client and its backend -var APIVersion = semver.MustParse("2.1.0") +var APIVersion = semver.MustParse("3.0.0") |