diff options
73 files changed, 1047 insertions, 320 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 0745b1e7b..514889969 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -176,10 +176,40 @@ vendor_task: failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh |& ${TIMESTAMP}' +# This task runs `make varlink_api_generate` followed by ./hack/tree_status.sh to check +# whether the git tree is clean. +varlink_api_task: + + depends_on: + - "gating" + + env: + CIRRUS_WORKING_DIR: "/usr/src/libpod" + # Used by tree_status.sh + SUGGESTION: 'remove API.md, then "make varlink_api_generate" and commit changes.' + + # Runs within Cirrus's "community cluster" + container: + image: "quay.io/libpod/gate:latest" + cpu: 4 + memory: 12 + + timeout_in: 10m + + vendor_script: + - '/usr/local/bin/entrypoint.sh varlink_api_generate' + - 'cd /go/src/github.com/containers/libpod && ./hack/tree_status.sh' + + on_failure: + failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + + build_each_commit_task: depends_on: - "gating" + - "vendor" + - "varlink_api" # $CIRRUS_BASE_BRANCH is only set when testing a PR only_if: $CIRRUS_BRANCH != 'master' @@ -232,6 +262,8 @@ testing_task: depends_on: - "gating" + - "varlink_api" + - "vendor" - "build_each_commit" gce_instance: @@ -275,6 +307,8 @@ special_testing_task: depends_on: - "gating" + - "varlink_api" + - "vendor" - "build_each_commit" gce_instance: @@ -388,6 +422,8 @@ success_task: depends_on: # ignores any dependent task conditions - "gating" + - "varlink_api" + - "vendor" - "build_each_commit_task" - "testing" - "rootless_testing_task" @@ -45,6 +45,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func ExportImage(name: string, destination: string, compress: bool, tags: []string) string](#ExportImage) +[func GenerateKube(name: string, service: bool) KubePodService](#GenerateKube) + +[func GenerateSystemd(name: string, restart: string, timeout: int, useName: bool) string](#GenerateSystemd) + [func GetAttachSockets(name: string) Sockets](#GetAttachSockets) [func GetContainer(id: string) Container](#GetContainer) @@ -57,6 +61,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [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) @@ -73,6 +79,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [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) @@ -87,6 +95,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [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) @@ -153,6 +163,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [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) @@ -211,6 +223,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [type InfoStore](#InfoStore) +[type KubePodService](#KubePodService) + [type ListPodContainerInfo](#ListPodContainerInfo) [type ListPodData](#ListPodData) @@ -245,10 +259,14 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [error ContainerNotFound](#ContainerNotFound) +[error ErrCtrStopped](#ErrCtrStopped) + [error ErrorOccurred](#ErrorOccurred) [error ImageNotFound](#ImageNotFound) +[error InvalidState](#InvalidState) + [error NoContainerRunning](#NoContainerRunning) [error NoContainersInPod](#NoContainersInPod) @@ -445,6 +463,17 @@ a booleon option to force compression. It also takes in a string array of tags tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID of the image is returned. If the image cannot be found in local storage, an [ImageNotFound](#ImageNotFound) error will be returned. See also [ImportImage](ImportImage). +### <a name="GenerateKube"></a>func GenerateKube +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method GenerateKube(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="GenerateSystemd"></a>func GenerateSystemd +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method GenerateSystemd(name: [string](https://godoc.org/builtin#string), restart: [string](https://godoc.org/builtin#string), timeout: [int](https://godoc.org/builtin#int), useName: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> + ### <a name="GetAttachSockets"></a>func GetAttachSockets <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> @@ -522,6 +551,11 @@ method GetContainersByContext(all: [bool](https://godoc.org/builtin#bool), lates 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;"> @@ -621,6 +655,11 @@ method GetPodsByContext(all: [bool](https://godoc.org/builtin#bool), latest: [bo 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;"> @@ -669,6 +708,16 @@ the IDs of the removed images are returned. 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;"> @@ -1039,6 +1088,11 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.StopPod '{"name": "135d71 method TagImage(name: [string](https://godoc.org/builtin#string), tagged: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> TagImage takes the name or ID of an image in local storage as well as the desired tag name. If the image cannot be found, an [ImageNotFound](#ImageNotFound) error will be returned; otherwise, the ID of the image is returned on success. +### <a name="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;"> @@ -1445,6 +1499,8 @@ quiet [](#) readonly [](#) +readonlytmpfs [](#) + restart [](#) rm [](#) @@ -1666,6 +1722,13 @@ 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 @@ -1917,6 +1980,9 @@ force [bool](https://godoc.org/builtin#bool) ### <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="ErrorOccurred"></a>type ErrorOccurred ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message @@ -1924,6 +1990,9 @@ 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. @@ -1933,7 +2002,7 @@ NoContainersInPod means a pod has no containers on which to perform the operatio the pod ID. ### <a name="PodContainerError"></a>type PodContainerError -PodContainerError means a container associated with a pod failed to preform an operation. It contains +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 @@ -1,6 +1,6 @@ GO ?= go DESTDIR ?= / -EPOCH_TEST_COMMIT ?= 7b7397481960c85379d8eb1ed21e76da2ce8a4fc +EPOCH_TEST_COMMIT ?= a9fc570dd844bf1ebd1f106f1b8091882b4a2b29 HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD diff --git a/changelog.txt b/changelog.txt index 92a17f8d0..c72117d7f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,205 @@ +- Changelog for v1.3.0 (2019-05-06) + * Update release notes for 1.3.0 release + * Bump to Buildah v1.8.2 + * Document events logger options in libpod.conf manpage + * Try and fix restart-policy tests + * fix logout message if login only with docker + * Fix manpage typos + * Small code fix + * Fix 'restart' event in journald + * change from sysregistries to sysregistriesv2 + * Address review comments on restart policy + * Add a test for restart policy + * Add a restart event, and make one during restart policy + * Restart policy should not run if a container is running + * Restart policy conflicts with the --rm flag + * Move to using constants for valid restart policy types + * Add manpage information for restart policy + * Add support for retry count with --restart flag + * Sending signals to containers prevents restart policy + * Add container restart policy to Libpod & Podman + * Add a StoppedByUser field to the DB + * top: fallback to execing ps(1) + * clean up shared/parse/parse.go + * Generate systemd unit files for containers + * Fix podman-in-podman volume test + * Cirrus: Add pipefail confirmation check + * Cirrus: timestamp all output script output + * Update c/storage to v1.12.6 + * Fix typo in init manpage + * Add an InvalidState varlink error for Init + * Bump Buildah to v1.8.1, ImageBuilder to v1.1.0 + * Add variable for global flags to runlabel + * docs: Fix typo "healthcheck" pt2 + * cirrus lib.sh: refactor req_env_var() + * Remove two bits of dead code + * http-proxy: improve docs + * Small fixes for #2950 + * container: drop rootless check + * Add basic structure of podman init command + * Move handling of ReadOnlyTmpfs into new mounts code + * Begin adding volume tests + * Ensure that named volumes have their options parsed + * Add options parsing for tmpfs mounts + * Use EqualValues instead of reflect equality + * Hit a number of to-do comments in unified volumes code + * Fix options for non-bind and non-tmpfs volumes + * Migrate unit tests from cmd/podman into pkg/spec + * Migrate to unified volume handling code + * Always pass pod into MakeContainerConfig + * Remove non-config fields from CreateConfig + * Add a new function for converting a CreateConfig + * podman-remote port + * install.md contains hints for rootless setup on arch linux + * auto pass http_proxy into container + * enable podman-remote on windows + * Use 'sudo tee' in tutorial so install works as non-root + * Refactor container cleanup to use latest functions + * Move --mount in run man page + * Add details on rootless Podman to the readme + * podman-remote stop + * correct upstream vndr issues + * runtime: pass down the context + * system: add new subcommand "migrate" + * Vendor in latest buildah code + * remove manual install of libsystemd-dev + * Vendor in latest containers/storage + * Add --read-only-tmpfs options + * Fix remote-client testing reports + * podman-remote prune containers + * Do not hard fail on non-decodable events + * update psgo to v1.2.1 + * Add System event type and renumber, refresh events + * enable podman remote top + * fix login supports credHelpers config + * Cirrus: Collect audit log on success and failure + * Add a debug message indicating that a refresh occurred + * image: rework parent/child/history matching + * images: add context to GetParent/IsParent/Remove/Prune... + * build podman-remote with Dockerfile. + * point to 3rd party tools for `docker-compose` format + * Update vendor of container/storage + * journald event logging + * podman remote-client restart containers + * Cirrus: Use freshly built images + * Cirrus: Bump up runc commit + * Cirrus: fix obsolete Ubuntu package + * Cirrus: Install libsystemd-dev on Ubuntu + * pull: special case all-tags semantics + * Fix test compile + * Trim whitespace from ps -q before comparing + * Enhance tests for stop to check results + * Add extra CI tests for stopping all containers + * Fix podman stop --all attempting to stop created ctrs + * Cirrus: Temp. override container-selinux on F29 + * Refactor of 'podman prune' to better support remote + * bats - various small updates + * podman-remote pause|unpause + * Internal names do not match external names + * Add header to play kube output + * Clean up after play kube failure + * rootless: not close more FDs than needed + * Fix COPR builds to start working again + * Fix podman command --change CMD + * podman-remote start + * Vendor in latest Buildah + * Added remote pod prune + * Add podman pod prune + * podman-remote container commands + * Fix segfaults attribute to missing options + * Call the runtime with WithRenumber() when asked + * Add File mounts to play kube + * cmd, pkg: drop commented code + * pod: drop dead code + * rootless, mount: not create namespace + * Incorporate image and default environment variables in play kube + * Validate ENV/LABEL Change options in varlink + * oci: fix umount of /sys/kernel + * Revert "rootless: set controlling terminal for podman in the userns" + * Remove old crio reference from man pages + * create: fix segfault if container name already exists + * adding uidmap to install steps for ubuntu + * podman-remote generate kube + * rootless: do not block SIGTSTP + * rootless: set controlling terminal for podman in the userns + * Use GetContainer instead of LookupContainer for full ID + * pull: exit with error if the image is not found + * Use the same SELinux label for CRIU log files + * pull: remove cryptic error message + * new uidmap BATS test: fix + * adding additional update, needed for install + * Fix README.md -> rootless.md link + * Fixes for podman-remote run and attach + * remote-client checkpoint/restore + * Expand debugging for container cleanup errors + * spec: mask /sys/kernel when bind mounting /sys + * Add --include-volumes flag to 'podman commit' + * oci: add /sys/kernel to the masked paths + * userns: prevent /sys/kernel/* paths in the container + * imagefilter dangling handling corrected + * rootless: fix segfault on refresh if there are containers + * Add demo script and cast to images + * Initial remote flag clean up + * (minor): fix misspelled 'Healthcheck' + * BATS tests: start supporting podman-remote + * Add the ability to attach remotely to a container + * Print header for 'podman images' even with no images + * podman-remote ps + * Re-run (make vendor) to drop the now unnecessary collation code and tables + * Potentially breaking: Make hooks sort order locale-independent + * Implement podman-remote rm + * ps: now works with --size and nonroot + * Update invalid name errors to report the correct regex + * cirrus: enable remote tests for rootless + * test: fix remote tests for rootless + * test: enable userns e2e tests for rootless + * CI check for --help vs man pages: usability fix + * podman-remote create|run + * Correct varlink pull panic + * add image rmi event + * Revert "Switch to golangci-lint" + * Document shortcomings with rootless podman + * podman: enable kube for rootless + * kube: correctly set the default for MemorySwappiness + * rootless: enable healthcheck tests + * Respect image entrypoint in play kube + * Increase CI resources to help avoid hitting timeouts + * podman-remote image tree + * Added port forwarding and IP address hint. + * fix bug podman cp directory + * Fix E2E tests + * Drop LocalVolumes from our the database + * Major rework of --volumes-from flag + * Volume force-remove now removed dependent containers + * Add handling for new named volumes code in pkg/spec + * Create non-existing named volumes at container create + * Switch Libpod over to new explicit named volumes + * Add named volumes for each container to database + * Add varcheck linter + * Add deadcode linter + * Update lint to use golangci-lint + * Update registrar unit tests to match them of cri-o + * Update run tests to be skipped when not supported + * Fix Dockerfile dependencies for packer tests + * Update Dockerfile to use golang:1.12 image + * Fix a potential segfault in podman search + * Improve podman pod rm -a test + * Cirrus: Update F28 -> F29 container image + * --size does not work with rootless at present + * add remote-client diff + * Cirrus: Support special-case modes of testing + * rootless: use a single user namespace + * rootless: remove SkipStorageSetup() + * Update cri-o annotations + * Update README with current version + * docs/podman*.md: fix numerous option typos and spacing errors + * docs/podman-rm.1.md: delete "Not yet implemented" msg for volume removal + * docs/podman-inspect.1.md: add missing option hyphen for "-t" + * Bump gitvalidation epoch + * Bump to v1.3.0-dev + * Fix location of libpod.conf + * Capitalize global options help information + - Changelog for v1.2.0 (2019-03-30) * Update release notes for v1.2.0 * Remove wait event diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index b770aaca0..aaa4513d8 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -32,6 +32,10 @@ type MainFlags struct { CpuProfile string LogLevel string TmpDir string + + RemoteUserName string + RemoteHost string + VarlinkAddress string } type AttachValues struct { diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 14451d944..3a409f503 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -39,13 +39,11 @@ func getImageSubCommands() []*cobra.Command { func getContainerSubCommands() []*cobra.Command { return []*cobra.Command{ - _checkpointCommand, _cleanupCommand, _commitCommand, _execCommand, _mountCommand, _refreshCommand, - _restoreCommand, _runlabelCommand, _statsCommand, _umountCommand, diff --git a/cmd/podman/container.go b/cmd/podman/container.go index bbf01d1f8..530175a55 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -51,6 +51,7 @@ var ( // Commands that are universally implemented. containerCommands = []*cobra.Command{ _attachCommand, + _checkpointCommand, _containerExistsCommand, _contInspectSubCommand, _diffCommand, @@ -64,6 +65,7 @@ var ( _portCommand, _pruneContainersCommand, _restartCommand, + _restoreCommand, _runCommand, _rmCommand, _startCommand, diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go index a3335050a..c8bb3ad3e 100644 --- a/cmd/podman/main_remote.go +++ b/cmd/podman/main_remote.go @@ -3,17 +3,17 @@ package main import ( - "os" - - "github.com/containers/libpod/pkg/rootless" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) const remote = true func init() { - // remote client specific flags can go here. + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", "", "username on the remote host") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", "", "remote host") + // TODO maybe we allow the altering of this for bridge connections? + //rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") } func setSyslog() error { @@ -29,16 +29,6 @@ func profileOff(cmd *cobra.Command) error { } func setupRootless(cmd *cobra.Command, args []string) error { - if rootless.IsRootless() { - became, ret, err := rootless.BecomeRootInUserNS() - if err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - if became { - os.Exit(ret) - } - } return nil } diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 623f17050..eb5181126 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -497,7 +497,7 @@ func psDisplay(c *cliconfig.PsValues, runtime *adapter.LocalRuntime) error { } else { // Print namespace information - ns := shared.GetNamespaces(container.Pid) + ns := runtime.GetNamespaces(container) fmt.Fprintf(w, "\n%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Names, container.Pid, ns.Cgroup, ns.IPC, ns.MNT, ns.NET, ns.PIDNS, ns.User, ns.UTS) } diff --git a/cmd/podman/shared/intermediate_varlink.go b/cmd/podman/shared/intermediate_varlink.go index 5e21245e3..4742d4909 100644 --- a/cmd/podman/shared/intermediate_varlink.go +++ b/cmd/podman/shared/intermediate_varlink.go @@ -3,8 +3,6 @@ package shared import ( - "fmt" - "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/pkg/rootless" @@ -209,7 +207,6 @@ func boolFromVarlink(v *bool, flagName string, defaultValue bool) CRBool { cr.Val = defaultValue cr.Changed = false } else { - fmt.Println(flagName, cr.Val) cr.Val = *v cr.Changed = true } diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 978678a84..215542d2c 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -3,11 +3,17 @@ package main import ( + "fmt" + "os" + "path/filepath" "time" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/pkg/varlinkapi" "github.com/containers/libpod/version" "github.com/pkg/errors" @@ -45,13 +51,31 @@ func init() { } func varlinkCmd(c *cliconfig.VarlinkValues) error { - args := c.InputArgs - if len(args) < 1 { - return errors.Errorf("you must provide a varlink URI") + varlinkURI := adapter.DefaultAddress + if rootless.IsRootless() { + xdg, err := util.GetRootlessRuntimeDir() + if err != nil { + return err + } + socketDir := filepath.Join(xdg, "podman/io.podman") + if _, err := os.Stat(filepath.Dir(socketDir)); os.IsNotExist(err) { + if err := os.Mkdir(filepath.Dir(socketDir), 0755); err != nil { + return err + } + } + varlinkURI = fmt.Sprintf("unix:%s", socketDir) } + args := c.InputArgs + if len(args) > 1 { - return errors.Errorf("too many arguments. Requires exactly 1") + return errors.Errorf("too many arguments. you may optionally provide 1") + } + + if len(args) > 0 { + varlinkURI = args[0] } + + logrus.Debugf("Using varlink socket: %s", varlinkURI) timeout := time.Duration(c.Timeout) * time.Millisecond // Create a single runtime for varlink @@ -81,7 +105,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error { } // Run the varlink server at the given address - if err = service.Listen(args[0], timeout); err != nil { + if err = service.Listen(varlinkURI, timeout); err != nil { switch err.(type) { case varlink.ServiceTimeoutError: logrus.Infof("varlink service expired (use --timeout to increase session time beyond %d ms, 0 means never timeout)", c.Int64("timeout")) diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index ace81646c..faaecdb6b 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1254,4 +1254,7 @@ error ErrorOccurred (reason: string) error RuntimeError (reason: string) # The Podman endpoint requires that you use a streaming connection. -error WantsMoreRequired (reason: string)
\ No newline at end of file +error WantsMoreRequired (reason: string) + +# Container is already stopped +error ErrCtrStopped (id: string) diff --git a/contrib/cirrus/rootless_test.sh b/contrib/cirrus/rootless_test.sh index 8351214f3..eab06bac0 100755 --- a/contrib/cirrus/rootless_test.sh +++ b/contrib/cirrus/rootless_test.sh @@ -14,8 +14,6 @@ then exit 1 fi -export PODMAN_VARLINK_ADDRESS=unix:/tmp/podman-$(id -u) - echo "." echo "Hello, my name is $USER and I live in $PWD can I be your friend?" diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index f3ee01bca..d69b673e0 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -39,7 +39,7 @@ %global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7}) Name: podman -Version: 1.3.0 +Version: 1.3.1 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 3a44c70af..cf36106e8 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -244,26 +244,6 @@ inside of the container. Read in a line delimited file of environment variables -**--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`, -`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 -for the container in any other way will override the values that would have -been passed thru from the host. (Other ways to specify the proxy for the -container include passing the values with the `--env` flag, or hardcoding the -proxy environment at container build time.) - -For example, to disable passing these environment variables from host to -container: - -`--http-proxy=false` - -Defaults to `true` - **--expose**=[] Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection @@ -281,20 +261,27 @@ Add additional groups to run as **--healthcheck**="" -Set or alter a healthcheck for a container. The value must be of the format of: +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. + +**--healthcheck-interval**="" + +Set an interval for the healthchecks (a value of `disable` results in no automatic timer setup) (default "30s") - `[OPTIONS] CMD command` +**--healthcheck-retries=** - where options can be any of the follow: - * --interval=DURATION (default: 30s) - * --timeout=DURATION (default: 30s) - * --start-period=DURATION (default: 0s) - * --retries=N (default: 3) +The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`. -Note: options are *not* required. +**--healthcheck-start-period**="" -The command is a command to be executed inside your container that determines your container health. The -command is required. +The initialization time needed for a container to bootstrap. The value can be expressed in time format like +`2m3s`. The default value is `0s` + +**--healthcheck-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`. **--hostname**="" @@ -306,6 +293,26 @@ Sets the container host name that is available inside the container. 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`, +`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 +for the container in any other way will override the values that would have +been passed thru from the host. (Other ways to specify the proxy for the +container include passing the values with the `--env` flag, or hardcoding the +proxy environment at container build time.) + +For example, to disable passing these environment variables from host to +container: + +`--http-proxy=false` + +Defaults to `true` + **--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore* Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind'). diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index a200b12fb..1840e0f0b 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -251,26 +251,6 @@ inside of the container. Read in a line delimited file of environment variables -**--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`, -`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 -for the container in any other way will override the values that would have -been passed thru from the host. (Other ways to specify the proxy for the -container include passing the values with the `--env` flag, or hardcoding the -proxy environment at container build time.) - -For example, to disable passing these environment variables from host to -container: - -`--http-proxy=false` - -Defaults to `true` - **--expose**=[] Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection @@ -289,20 +269,27 @@ Add additional groups to run as **--healthcheck**="" -Set or alter a healthcheck for a container. The value must be of the format of: +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. + +**--healthcheck-interval**="" + +Set an interval for the healthchecks (a value of `disable` results in no automatic timer setup) (default "30s") - `[OPTIONS] CMD command` +**--healthcheck-retries=** - where options can be any of the follow: - * --interval=DURATION (default: 30s) - * --timeout=DURATION (default: 30s) - * --start-period=DURATION (default: 0s) - * --retries=N (default: 3) +The number of retries allowed before a healthcheck is considered to be unhealthy. The default value is `3`. -Note: options are *not* required. +**--healthcheck-start-period**="" -The command is a command to be executed inside your container that determines your container health. The -command is required. +The initialization time needed for a container to bootstrap. The value can be expressed in time format like +`2m3s`. The default value is `0s` + +**--healthcheck-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`. **--hostname**="" @@ -314,6 +301,26 @@ Sets the container host name that is available inside the container. 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`, +`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 +for the container in any other way will override the values that would have +been passed thru from the host. (Other ways to specify the proxy for the +container include passing the values with the `--env` flag, or hardcoding the +proxy environment at container build time.) + +For example, to disable passing these environment variables from host to +container: + +`--http-proxy=false` + +Defaults to `true` + **--image-volume**, **builtin-volume**=*bind*|*tmpfs*|*ignore* Tells podman how to handle the builtin image volumes. diff --git a/docs/podman-varlink.1.md b/docs/podman-varlink.1.md index 7eeb3dd66..0501d853f 100644 --- a/docs/podman-varlink.1.md +++ b/docs/podman-varlink.1.md @@ -7,8 +7,10 @@ podman\-varlink - Runs the varlink backend interface **podman varlink** [*options*] *uri* ## DESCRIPTION -Starts the varlink service listening on *uri* that allows varlink clients to interact with podman. This should generally be done -with systemd. See _Configuration_ below. +Starts the varlink service listening on *uri* that allows varlink clients to interact with podman. If no *uri* is provided, a default +URI will be used depending on the user calling the varlink service. The default for the root user is `unix:/run/podman/io.podman`. Regular +users will have a default *uri* of `$XDG_RUNTIME_DIR/podman/io.podman`. For example, `unix:/run/user/1000/podman/io.podman` +The varlink service should generally be done with systemd. See _Configuration_ below. ## GLOBAL OPTIONS @@ -23,16 +25,23 @@ second. A value of `0` means no timeout and the session will not expire. ## EXAMPLES -Run the podman varlink service manually and accept the default timeout. +Run the podman varlink service accepting all default options. ``` -$ podman varlink unix:/run/podman/io.podman +$ podman varlink +``` + + +Run the podman varlink service with an alternate URI and accept the default timeout. + +``` +$ podman varlink unix:/tmp/io.podman ``` Run the podman varlink service manually with a 5 second timeout. ``` -$ podman varlink --timeout 5000 unix:/run/podman/io.podman +$ podman varlink --timeout 5000 ``` ## CONFIGURATION diff --git a/hack/tree_status.sh b/hack/tree_status.sh index 78fb4c6a3..ac874a347 100755 --- a/hack/tree_status.sh +++ b/hack/tree_status.sh @@ -1,12 +1,14 @@ #!/bin/bash set -e +SUGGESTION="${SUGGESTION:-sync the vendor.conf and commit all changes.}" + STATUS=$(git status --porcelain) if [[ -z $STATUS ]] then echo "tree is clean" else - echo "tree is dirty, please commit all changes and sync the vendor.conf" + echo "tree is dirty, please $SUGGESTION" echo "" echo "$STATUS" exit 1 diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 0b4e5763e..e6ffaa6d7 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -516,7 +516,7 @@ func (c *Container) refresh() error { } // We need to pick up a new lock - lock, err := c.runtime.lockManager.RetrieveLock(c.config.LockID) + lock, err := c.runtime.lockManager.AllocateAndRetrieveLock(c.config.LockID) if err != nil { return errors.Wrapf(err, "error acquiring lock for container %s", c.ID()) } diff --git a/libpod/lock/in_memory_locks.go b/libpod/lock/in_memory_locks.go index 7c9605917..f3c842f89 100644 --- a/libpod/lock/in_memory_locks.go +++ b/libpod/lock/in_memory_locks.go @@ -90,6 +90,22 @@ func (m *InMemoryManager) RetrieveLock(id uint32) (Locker, error) { return m.locks[id], nil } +// AllocateAndRetrieveLock allocates a lock with the given ID (if not already in +// use) and returns it. +func (m *InMemoryManager) AllocateAndRetrieveLock(id uint32) (Locker, error) { + if id >= m.numLocks { + return nil, errors.Errorf("given lock ID %d is too large - this manager only supports lock indexes up to %d", id, m.numLocks) + } + + if m.locks[id].allocated { + return nil, errors.Errorf("given lock ID %d is already in use, cannot reallocate", id) + } + + m.locks[id].allocated = true + + return m.locks[id], nil +} + // FreeAllLocks frees all locks. // This function is DANGEROUS. Please read the full comment in locks.go before // trying to use it. diff --git a/libpod/lock/lock.go b/libpod/lock/lock.go index d6841646b..4e1e2e215 100644 --- a/libpod/lock/lock.go +++ b/libpod/lock/lock.go @@ -24,6 +24,13 @@ type Manager interface { // The underlying lock MUST be the same as another other lock with the // same UUID. RetrieveLock(id uint32) (Locker, error) + // AllocateAndRetrieveLock marks the lock with the given UUID as in use + // and retrieves it. + // RetrieveAndAllocateLock will error if the lock in question has + // already been allocated. + // This is mostly used after a system restart to repopulate the list of + // locks in use. + AllocateAndRetrieveLock(id uint32) (Locker, error) // PLEASE READ FULL DESCRIPTION BEFORE USING. // FreeAllLocks frees all allocated locks, in preparation for lock // reallocation. diff --git a/libpod/lock/shm/shm_lock.c b/libpod/lock/shm/shm_lock.c index d11fce71a..047d3c417 100644 --- a/libpod/lock/shm/shm_lock.c +++ b/libpod/lock/shm/shm_lock.c @@ -354,6 +354,61 @@ int64_t allocate_semaphore(shm_struct_t *shm) { return -1 * ENOSPC; } +// Allocate the semaphore with the given ID. +// Returns an error if the semaphore with this ID does not exist, or has already +// been allocated. +// Returns 0 on success, or negative errno values on failure. +int32_t allocate_given_semaphore(shm_struct_t *shm, uint32_t sem_index) { + int bitmap_index, index_in_bitmap, ret_code; + bitmap_t test_map; + + if (shm == NULL) { + return -1 * EINVAL; + } + + // Check if the lock index is valid + if (sem_index >= shm->num_locks) { + return -1 * EINVAL; + } + + bitmap_index = sem_index / BITMAP_SIZE; + index_in_bitmap = sem_index % BITMAP_SIZE; + + // This should never happen if the sem_index test above succeeded, but better + // safe than sorry + if (bitmap_index >= shm->num_bitmaps) { + return -1 * EFAULT; + } + + test_map = 0x1 << index_in_bitmap; + + // Lock the mutex controlling access to our shared memory + ret_code = take_mutex(&(shm->segment_lock)); + if (ret_code != 0) { + return -1 * ret_code; + } + + // Check if the semaphore is allocated + if ((test_map & shm->locks[bitmap_index].bitmap) != 0) { + ret_code = release_mutex(&(shm->segment_lock)); + if (ret_code != 0) { + return -1 * ret_code; + } + + return -1 * EEXIST; + } + + // The semaphore is not allocated, allocate it + shm->locks[bitmap_index].bitmap = shm->locks[bitmap_index].bitmap | test_map; + + ret_code = release_mutex(&(shm->segment_lock)); + if (ret_code != 0) { + return -1 * ret_code; + } + + return 0; +} + // Deallocate a given semaphore // Returns 0 on success, negative ERRNO values on failure int32_t deallocate_semaphore(shm_struct_t *shm, uint32_t sem_index) { diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go index e70ea8743..c21e9a221 100644 --- a/libpod/lock/shm/shm_lock.go +++ b/libpod/lock/shm/shm_lock.go @@ -134,6 +134,23 @@ func (locks *SHMLocks) AllocateSemaphore() (uint32, error) { return uint32(retCode), nil } +// AllocateGivenSemaphore allocates the given semaphore from the shared-memory +// segment for use by a container or pod. +// If the semaphore is already in use or the index is invalid an error will be +// returned. +func (locks *SHMLocks) AllocateGivenSemaphore(sem uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + retCode := C.allocate_given_semaphore(locks.lockStruct, C.uint32_t(sem)) + if retCode < 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + // DeallocateSemaphore frees a semaphore in a shared-memory segment so it can be // reallocated to another container or pod. // The given semaphore must be already allocated, or an error will be returned. diff --git a/libpod/lock/shm/shm_lock.h b/libpod/lock/shm/shm_lock.h index 58e4297e2..759f8178a 100644 --- a/libpod/lock/shm/shm_lock.h +++ b/libpod/lock/shm/shm_lock.h @@ -39,6 +39,7 @@ shm_struct_t *setup_lock_shm(char *path, uint32_t num_locks, int *error_code); shm_struct_t *open_lock_shm(char *path, uint32_t num_locks, int *error_code); int32_t close_lock_shm(shm_struct_t *shm); int64_t allocate_semaphore(shm_struct_t *shm); +int32_t allocate_given_semaphore(shm_struct_t *shm, uint32_t sem_index); int32_t deallocate_semaphore(shm_struct_t *shm, uint32_t sem_index); int32_t deallocate_all_semaphores(shm_struct_t *shm); int32_t lock_semaphore(shm_struct_t *shm, uint32_t sem_index); diff --git a/libpod/lock/shm_lock_manager_linux.go b/libpod/lock/shm_lock_manager_linux.go index 8678958ee..5f31939f8 100644 --- a/libpod/lock/shm_lock_manager_linux.go +++ b/libpod/lock/shm_lock_manager_linux.go @@ -57,6 +57,25 @@ func (m *SHMLockManager) AllocateLock() (Locker, error) { return lock, nil } +// AllocateAndRetrieveLock allocates the lock with the given ID and returns it. +// If the lock is already allocated, error. +func (m *SHMLockManager) AllocateAndRetrieveLock(id uint32) (Locker, error) { + lock := new(SHMLock) + lock.lockID = id + lock.manager = m + + if id >= m.locks.GetMaxLocks() { + return nil, errors.Wrapf(syscall.EINVAL, "lock ID %d is too large - max lock size is %d", + id, m.locks.GetMaxLocks()-1) + } + + if err := m.locks.AllocateGivenSemaphore(id); err != nil { + return nil, err + } + + return lock, nil +} + // RetrieveLock retrieves a lock from the manager given its ID. func (m *SHMLockManager) RetrieveLock(id uint32) (Locker, error) { lock := new(SHMLock) diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go index 25e4e77d7..1fcb5b1a6 100644 --- a/libpod/pod_internal.go +++ b/libpod/pod_internal.go @@ -56,7 +56,7 @@ func (p *Pod) refresh() error { } // Retrieve the pod's lock - lock, err := p.runtime.lockManager.RetrieveLock(p.config.LockID) + lock, err := p.runtime.lockManager.AllocateAndRetrieveLock(p.config.LockID) if err != nil { return errors.Wrapf(err, "error retrieving lock for pod %s", p.ID()) } diff --git a/libpod/runtime.go b/libpod/runtime.go index 34b6ac74f..e6b84014e 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -922,7 +922,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { if os.IsNotExist(errors.Cause(err)) { manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) if err != nil { - return err + return errors.Wrapf(err, "failed to get new shm lock manager") } } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { logrus.Debugf("Number of locks does not match - removing old locks") diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 0011c771a..5867b1f87 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -149,10 +149,10 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) } // Go through and lock all containers so we can operate on them all at once - dependencies := make(map[string][]string) for _, ctr := range ctrs { - ctr.lock.Lock() - defer ctr.lock.Unlock() + ctrLock := ctr.lock + ctrLock.Lock() + defer ctrLock.Unlock() // Sync all containers if err := ctr.syncContainer(); err != nil { @@ -177,23 +177,12 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) if len(ctr.state.ExecSessions) != 0 && !force { return errors.Wrapf(ErrCtrStateInvalid, "pod %s contains container %s which has active exec sessions", p.ID(), ctr.ID()) } - - deps, err := r.state.ContainerInUse(ctr) - if err != nil { - return err - } - dependencies[ctr.ID()] = deps } - // Check if containers have dependencies - // If they do, and the dependencies are not in the pod, error - for ctr, deps := range dependencies { - for _, dep := range deps { - if _, ok := dependencies[dep]; !ok { - return errors.Wrapf(ErrCtrExists, "container %s depends on container %s not in pod %s", ctr, dep, p.ID()) - } - } - } + // We maintain the invariant that container dependencies must all exist + // within the container's pod. + // No need to check dependencies as such - we're removing all containers + // in the pod at once, no dependency issues. // First loop through all containers and stop them // Do not remove in this loop to ensure that we don't remove unless all @@ -220,18 +209,40 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) } } - // Start removing containers - // We can remove containers even if they have dependencies now - // As we have guaranteed their dependencies are in the pod + // Remove all containers in the pod from the state. + if err := r.state.RemovePodContainers(p); err != nil { + return err + } + + var removalErr error + + // Clean up after our removed containers. + // Errors here are nonfatal - the containers have already been evicted. + // We'll do our best to clean up after them, but we have to keep going + // and remove the pod as well. + // From here until we remove the pod from the state, no error returns. for _, ctr := range ctrs { + // The container no longer exists in the state, mark invalid. + ctr.valid = false + + ctr.newContainerEvent(events.Remove) + // Clean up network namespace, cgroups, mounts if err := ctr.cleanup(ctx); err != nil { - return err + if removalErr == nil { + removalErr = err + } else { + logrus.Errorf("Unable to clean up container %s: %v", ctr.ID(), err) + } } // Stop container's storage if err := ctr.teardownStorage(); err != nil { - return err + if removalErr == nil { + removalErr = err + } else { + logrus.Errorf("Unable to tear down container %s storage: %v", ctr.ID(), err) + } } // Delete the container from runtime (only if we are not @@ -239,26 +250,24 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) if ctr.state.State != ContainerStateConfigured && ctr.state.State != ContainerStateExited { if err := ctr.delete(ctx); err != nil { - return err + if removalErr == nil { + removalErr = err + } else { + logrus.Errorf("Unable to remove container %s from OCI runtime: %v", ctr.ID(), err) + } } } // Free the container's lock if err := ctr.lock.Free(); err != nil { - return err + if removalErr == nil { + removalErr = errors.Wrapf(err, "error freeing container %s lock", ctr.ID()) + } else { + logrus.Errorf("Unable to free container %s lock: %v", ctr.ID(), err) + } } } - // Remove containers from the state - if err := r.state.RemovePodContainers(p); err != nil { - return err - } - - // Mark containers invalid - for _, ctr := range ctrs { - ctr.valid = false - } - // Remove pod cgroup, if present if p.state.CgroupPath != "" { logrus.Debugf("Removing pod cgroup %s", p.state.CgroupPath) @@ -266,10 +275,11 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) switch p.runtime.config.CgroupManager { case SystemdCgroupsManager: if err := deleteSystemdCgroup(p.state.CgroupPath); err != nil { - // The pod is already almost gone. - // No point in hard-failing if we fail - // this bit of cleanup. - logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err) + if removalErr == nil { + removalErr = errors.Wrapf(err, "error removing pod %s cgroup", p.ID()) + } else { + logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err) + } } case CgroupfsCgroupsManager: // Delete the cgroupfs cgroup @@ -280,34 +290,60 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") conmonCgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(conmonCgroupPath)) if err != nil && err != cgroups.ErrCgroupDeleted { - return err + if removalErr == nil { + removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup", p.ID()) + } else { + logrus.Debugf("Error retrieving pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err) + } } if err == nil { if err := conmonCgroup.Delete(); err != nil { - logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err) + if removalErr == nil { + removalErr = errors.Wrapf(err, "error removing pod %s conmon cgroup", p.ID()) + } else { + logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err) + } } } cgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(p.state.CgroupPath)) if err != nil && err != cgroups.ErrCgroupDeleted { - return err + if removalErr == nil { + removalErr = errors.Wrapf(err, "error retrieving pod %s cgroup", p.ID()) + } else { + logrus.Errorf("Error retrieving pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err) + } } if err == nil { if err := cgroup.Delete(); err != nil { - logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err) + if removalErr == nil { + removalErr = errors.Wrapf(err, "error removing pod %s cgroup", p.ID()) + } else { + logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err) + } } } default: - return errors.Wrapf(ErrInvalidArg, "unknown cgroups manager %s specified", p.runtime.config.CgroupManager) + // This should be caught much earlier, but let's still + // keep going so we make sure to evict the pod before + // ending up with an inconsistent state. + if removalErr == nil { + removalErr = errors.Wrapf(ErrInternal, "unrecognized cgroup manager %s when removing pod %s cgroups", p.runtime.config.CgroupManager, p.ID()) + } else { + logrus.Errorf("Unknown cgroups manager %s specified - cannot remove pod %s cgroup", p.runtime.config.CgroupManager, p.ID()) + } } } // Remove pod from state if err := r.state.RemovePod(p); err != nil { + if removalErr != nil { + logrus.Errorf("%v", removalErr) + } return err } // Mark pod invalid p.valid = false p.newPodEvent(events.Remove) - return nil + return removalErr } diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go index 6512a5952..f672a92a6 100644 --- a/pkg/adapter/client.go +++ b/pkg/adapter/client.go @@ -3,30 +3,45 @@ package adapter import ( + "fmt" "os" - "github.com/sirupsen/logrus" + "github.com/pkg/errors" "github.com/varlink/go/varlink" ) -// DefaultAddress is the default address of the varlink socket -const DefaultAddress = "unix:/run/podman/io.podman" +type VarlinkConnectionInfo struct { + RemoteUserName string + RemoteHost string + VarlinkAddress string +} // Connect provides a varlink connection func (r RemoteRuntime) Connect() (*varlink.Connection, error) { - var err error - var connection *varlink.Connection - if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" { - logrus.Infof("Connecting with varlink bridge") - logrus.Debugf("%s", bridge) + var ( + err error + connection *varlink.Connection + ) + + logLevel := r.cmd.LogLevel + + // I'm leaving this here for now as a document of the birdge format. It can be removed later once the bridge + // function is more flushed out. + //bridge := `ssh -T root@192.168.122.1 "/usr/bin/varlink -A '/usr/bin/podman varlink \$VARLINK_ADDRESS' bridge"` + if len(r.cmd.RemoteHost) > 0 { + // The user has provided a remote host endpoint + if len(r.cmd.RemoteUserName) < 1 { + return nil, errors.New("you must provide a username when providing a remote host name") + } + bridge := fmt.Sprintf(`ssh -T %s@%s /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, r.cmd.RemoteUserName, r.cmd.RemoteHost, logLevel) + connection, err = varlink.NewBridge(bridge) + } else if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" { connection, err = varlink.NewBridge(bridge) } else { address := os.Getenv("PODMAN_VARLINK_ADDRESS") if address == "" { address = DefaultAddress } - logrus.Infof("Connecting with varlink address") - logrus.Debugf("%s", address) connection, err = varlink.NewConnection(address) } if err != nil { diff --git a/pkg/adapter/client_config.go b/pkg/adapter/client_config.go new file mode 100644 index 000000000..d165ef1cc --- /dev/null +++ b/pkg/adapter/client_config.go @@ -0,0 +1,4 @@ +package adapter + +// DefaultAddress is the default address of the varlink socket +const DefaultAddress = "unix:/run/podman/io.podman" diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index f17050d42..ff7b6377a 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -1025,3 +1025,8 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri } return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, ctr.Config().StaticDir, timeout) } + +// GetNamespaces returns namespace information about a container for PS +func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace { + return shared.GetNamespaces(container.Pid) +} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 201249fc3..63b0f9d2f 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -234,15 +234,25 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) if err != nil { - return ok, failures, err + return ok, failures, TranslateError(err) } for _, id := range ids { - stopped, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)) - if err != nil { + if _, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)); err != nil { + transError := TranslateError(err) + if errors.Cause(transError) == libpod.ErrCtrStopped { + ok = append(ok, id) + continue + } + if errors.Cause(transError) == libpod.ErrCtrStateInvalid && cli.All { + ok = append(ok, id) + continue + } failures[id] = err } else { - ok = append(ok, stopped) + // We should be using ID here because in varlink, only successful returns + // include the string id + ok = append(ok, id) } } return ok, failures, nil @@ -310,7 +320,7 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) { ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) if err != nil { - return nil, nil, err + return nil, nil, TranslateError(err) } var ( @@ -961,3 +971,18 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { return iopodman.GenerateSystemd().Call(r.Conn, c.InputArgs[0], c.RestartPolicy, int64(c.StopTimeout), c.Name) } + +// GetNamespaces returns namespace information about a container for PS +func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace { + ns := shared.Namespace{ + PID: container.PID, + Cgroup: container.Cgroup, + IPC: container.IPC, + MNT: container.MNT, + NET: container.NET, + PIDNS: container.PIDNS, + User: container.User, + UTS: container.UTS, + } + return &ns +} diff --git a/pkg/adapter/errors.go b/pkg/adapter/errors.go new file mode 100644 index 000000000..7fbbabd93 --- /dev/null +++ b/pkg/adapter/errors.go @@ -0,0 +1,31 @@ +// +build remoteclient + +package adapter + +import ( + iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/libpod" + "github.com/pkg/errors" +) + +// TranslateMapErrors translates the errors a typical podman output struct +// from varlink errors to libpod errors +func TranslateMapErrors(failures map[string]error) map[string]error { + for k, v := range failures { + failures[k] = TranslateError(v) + } + return failures +} + +// TranslateError converts a single varlink error to a libpod error +func TranslateError(err error) error { + switch err.(type) { + case *iopodman.ContainerNotFound: + return errors.Wrap(libpod.ErrNoSuchCtr, err.Error()) + case *iopodman.ErrCtrStopped: + return errors.Wrap(libpod.ErrCtrStopped, err.Error()) + case *iopodman.InvalidState: + return errors.Wrap(libpod.ErrCtrStateInvalid, err.Error()) + } + return err +} diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go index 3b691ed17..3b2d02a5a 100644 --- a/pkg/adapter/info_remote.go +++ b/pkg/adapter/info_remote.go @@ -20,12 +20,7 @@ func (r RemoteRuntime) Info() ([]libpod.InfoData, error) { registries := make(map[string]interface{}) insecureRegistries := make(map[string]interface{}) - conn, err := r.Connect() - if err != nil { - return nil, err - } - defer conn.Close() - info, err := iopodman.GetInfo().Call(conn) + info, err := iopodman.GetInfo().Call(r.Conn) if err != nil { return nil, err } diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index 7cf38aac0..e2c97c36a 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -172,11 +172,15 @@ func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartVal // CreatePod creates a pod for the remote client over a varlink connection func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) { + var share []string + if cli.Share != "" { + share = strings.Split(cli.Share, ",") + } pc := iopodman.PodCreate{ Name: cli.Name, CgroupParent: cli.CgroupParent, Labels: labels, - Share: strings.Split(cli.Share, ","), + Share: share, Infra: cli.Infra, InfraCommand: cli.InfraCommand, InfraImage: cli.InfraCommand, diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 4986d16f7..8803a26fb 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -38,6 +38,7 @@ type RemoteImageRuntime struct{} type RemoteRuntime struct { Conn *varlink.Connection Remote bool + cmd cliconfig.MainFlags } // LocalRuntime describes a typical libpod runtime @@ -47,17 +48,17 @@ type LocalRuntime struct { // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { - runtime := RemoteRuntime{} + runtime := RemoteRuntime{ + Remote: true, + cmd: c.GlobalFlags, + } conn, err := runtime.Connect() if err != nil { return nil, err } - + runtime.Conn = conn return &LocalRuntime{ - &RemoteRuntime{ - Conn: conn, - Remote: true, - }, + &runtime, }, nil } diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go new file mode 100644 index 000000000..f2f49e750 --- /dev/null +++ b/pkg/systemdgen/systemdgen_test.go @@ -0,0 +1,120 @@ +package systemdgen + +import ( + "testing" +) + +func TestValidateRestartPolicy(t *testing.T) { + type args struct { + restart string + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"good-on", args{restart: "no"}, false}, + {"good-on-success", args{restart: "on-success"}, false}, + {"good-on-failure", args{restart: "on-failure"}, false}, + {"good-on-abnormal", args{restart: "on-abnormal"}, false}, + {"good-on-watchdog", args{restart: "on-watchdog"}, false}, + {"good-on-abort", args{restart: "on-abort"}, false}, + {"good-always", args{restart: "always"}, false}, + {"fail", args{restart: "foobar"}, true}, + {"failblank", args{restart: ""}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateRestartPolicy(tt.args.restart); (err != nil) != tt.wantErr { + t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCreateSystemdUnitAsString(t *testing.T) { + goodID := `[Unit] +Description=639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 Podman Container +[Service] +Restart=always +ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +KillMode=none +Type=forking +PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +[Install] +WantedBy=multi-user.target` + + goodName := `[Unit] +Description=foobar Podman Container +[Service] +Restart=always +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +KillMode=none +Type=forking +PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +[Install] +WantedBy=multi-user.target` + + type args struct { + name string + cid string + restart string + pidPath string + stopTimeout int + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + + {"good with id", + args{ + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "always", + "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + 10, + }, + goodID, + false, + }, + {"good with name", + args{ + "foobar", + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "always", + "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + 10, + }, + goodName, + false, + }, + {"bad restart policy", + args{ + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "never", + "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + 10, + }, + "", + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CreateSystemdUnitAsString(tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidPath, tt.args.stopTimeout) + if (err != nil) != tt.wantErr { + t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("CreateSystemdUnitAsString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 6c62d3514..2234899a5 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -60,6 +60,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st if !start && state != libpod.ContainerStateRunning { return call.ReplyErrorOccurred("container must be running to attach") } + call.Reply(nil) reader, writer, _, pw, streams := setupStreams(call) go func() { diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index c8be41636..8611a1a7d 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -119,6 +119,9 @@ func (i *LibpodAPI) GetContainersByContext(call iopodman.VarlinkCall, all, lates ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime) if err != nil { + if errors.Cause(err) == libpod.ErrNoSuchCtr { + return call.ReplyContainerNotFound("", err.Error()) + } return call.ReplyErrorOccurred(err.Error()) } @@ -359,7 +362,11 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused { return call.ReplyErrorOccurred("container is already running or paused") } - if err := ctr.Start(getContext(), false); err != nil { + 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()) @@ -386,7 +393,13 @@ func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeou if err != nil { return call.ReplyContainerNotFound(name, err.Error()) } - if err := ctr.StopWithTimeout(uint(timeout)); err != nil && err != libpod.ErrCtrStopped { + if err := ctr.StopWithTimeout(uint(timeout)); err != nil { + if errors.Cause(err) == libpod.ErrCtrStopped { + return call.ReplyErrCtrStopped(ctr.ID()) + } + if errors.Cause(err) == libpod.ErrCtrStateInvalid { + return call.ReplyInvalidState(ctr.ID(), err.Error()) + } return call.ReplyErrorOccurred(err.Error()) } return call.ReplyStopContainer(ctr.ID()) diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go index f34375bf5..c0fd8b1f7 100644 --- a/pkg/varlinkapi/pods.go +++ b/pkg/varlinkapi/pods.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "github.com/containers/libpod/pkg/adapter/shortcuts" - "github.com/containers/libpod/pkg/rootless" "syscall" "github.com/containers/libpod/cmd/podman/shared" @@ -37,12 +36,9 @@ func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCrea if !create.Infra { return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host") } - if rootless.IsRootless() { - return call.ReplyErrorOccurred("rootless networking does not allow port binding to the host") - } portBindings, err := shared.CreatePortBindings(create.Publish) if err != nil { - return err + return call.ReplyErrorOccurred(err.Error()) } options = append(options, libpod.WithInfraContainerPorts(portBindings)) diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 7f436a954..59bfec75b 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -86,15 +86,18 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { Graph_status: graphStatus, } - registriesInterface := info[2].Data["registries"] - insecureRegistriesInterface := info[3].Data["registries"] - if registriesInterface != nil { - registries = registriesInterface.([]string) + if len(info) > 2 { + registriesInterface := info[2].Data["registries"] + if registriesInterface != nil { + registries = registriesInterface.([]string) + } } - if insecureRegistriesInterface != nil { - insecureRegistries = insecureRegistriesInterface.([]string) + if len(info) > 3 { + insecureRegistriesInterface := info[3].Data["registries"] + if insecureRegistriesInterface != nil { + insecureRegistries = insecureRegistriesInterface.([]string) + } } - podmanInfo.Store = infoStore podmanInfo.Podman = pmaninfo podmanInfo.Registries = registries diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index a6fc211f6..3c7675b35 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -264,9 +264,14 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration { } if remote { p.PodmanTest.RemotePodmanBinary = podmanRemoteBinary + uuid := stringid.GenerateNonCryptoID() if !rootless.IsRootless() { - uuid := stringid.GenerateNonCryptoID() p.VarlinkEndpoint = 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) + p.VarlinkEndpoint = fmt.Sprintf("unix:%s", fqpath) } } diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 105cba37c..f3367337e 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/diff_test.go b/test/e2e/diff_test.go index fba65823e..920b920c0 100644 --- a/test/e2e/diff_test.go +++ b/test/e2e/diff_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( @@ -63,6 +61,7 @@ var _ = Describe("Podman diff", func() { }) It("podman diff container and committed image", func() { + SkipIfRemote() session := podmanTest.Podman([]string{"run", "--name=diff-test", ALPINE, "touch", "/tmp/diff-test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go index c960fb311..ca4012dde 100644 --- a/test/e2e/info_test.go +++ b/test/e2e/info_test.go @@ -24,6 +24,7 @@ var _ = Describe("Podman Info", func() { } podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() + podmanTest.DelayForVarlink() }) AfterEach(func() { diff --git a/test/e2e/kill_test.go b/test/e2e/kill_test.go index 618ca5aa0..3286180a4 100644 --- a/test/e2e/kill_test.go +++ b/test/e2e/kill_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/libpod_suite_remoteclient_test.go b/test/e2e/libpod_suite_remoteclient_test.go index 1e477fe2f..05c355711 100644 --- a/test/e2e/libpod_suite_remoteclient_test.go +++ b/test/e2e/libpod_suite_remoteclient_test.go @@ -3,13 +3,17 @@ package integration import ( + "bytes" "fmt" "github.com/containers/libpod/pkg/rootless" "io/ioutil" "os" "os/exec" "path/filepath" + "strconv" "strings" + "syscall" + "time" "github.com/onsi/ginkgo" ) @@ -50,33 +54,76 @@ func PodmanTestCreate(tempDir string) *PodmanTestIntegration { 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.VarlinkEndpoint - if addr := os.Getenv("PODMAN_VARLINK_ADDRESS"); addr != "" { - varlinkEndpoint = addr - } + p.SetVarlinkAddress(p.VarlinkEndpoint) args := []string{"varlink", "--timeout", "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.VarlinkCommand = command p.VarlinkSession = command.Process } func (p *PodmanTestIntegration) StopVarlink() { + var out bytes.Buffer + var pids []int varlinkSession := p.VarlinkSession - varlinkSession.Kill() - varlinkSession.Wait() if !rootless.IsRootless() { - socket := strings.Split(p.VarlinkEndpoint, ":")[1] - if err := os.Remove(socket); err != nil { - fmt.Println(err) + 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) } } @@ -110,3 +157,14 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error { 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) + } +} diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index 10ca9ac47..0a85c625d 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -79,4 +79,5 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error { restore.Wait(90) return nil } -func (p *PodmanTestIntegration) StopVarlink() {} +func (p *PodmanTestIntegration) StopVarlink() {} +func (p *PodmanTestIntegration) DelayForVarlink() {} diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index d051e3dba..2c82182cf 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/negative_test.go b/test/e2e/negative_test.go new file mode 100644 index 000000000..3cb54a20a --- /dev/null +++ b/test/e2e/negative_test.go @@ -0,0 +1,38 @@ +package integration + +import ( + "os" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman negative command-line", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman snuffleupagus exits non-zero", func() { + session := podmanTest.Podman([]string{"snuffleupagus"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) +}) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index de0734e9f..84966f77b 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 671d203a6..d1a023153 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_kill_test.go b/test/e2e/pod_kill_test.go index c1f7503e3..23a8ea97e 100644 --- a/test/e2e/pod_kill_test.go +++ b/test/e2e/pod_kill_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go index 59a4da176..ab828853b 100644 --- a/test/e2e/pod_pause_test.go +++ b/test/e2e/pod_pause_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_prune_test.go b/test/e2e/pod_prune_test.go index c20f602ad..8a4ba2399 100644 --- a/test/e2e/pod_prune_test.go +++ b/test/e2e/pod_prune_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index 2fa26d7ad..8513c6c2e 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_restart_test.go b/test/e2e/pod_restart_test.go index ffb6cb94c..7b19ecc94 100644 --- a/test/e2e/pod_restart_test.go +++ b/test/e2e/pod_restart_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 5da3d563b..7417a1298 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_start_test.go b/test/e2e/pod_start_test.go index de52af2a0..967fbc2da 100644 --- a/test/e2e/pod_start_test.go +++ b/test/e2e/pod_start_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go index fa285fa80..9fd9e3ef4 100644 --- a/test/e2e/pod_stop_test.go +++ b/test/e2e/pod_stop_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 957c69aa8..7edb350f3 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/refresh_test.go b/test/e2e/refresh_test.go deleted file mode 100644 index 56c1d255e..000000000 --- a/test/e2e/refresh_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// +build !remoteclient - -package integration - -import ( - "os" - "time" - - . "github.com/containers/libpod/test/utils" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Podman refresh", func() { - var ( - tmpdir string - err error - podmanTest *PodmanTestIntegration - ) - - BeforeEach(func() { - tmpdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } - podmanTest = PodmanTestCreate(tmpdir) - podmanTest.Setup() - podmanTest.RestoreAllArtifacts() - }) - - AfterEach(func() { - podmanTest.Cleanup() - f := CurrentGinkgoTestDescription() - processTestResult(f) - - }) - - Specify("Refresh with no containers succeeds", func() { - session := podmanTest.Podman([]string{"container", "refresh"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - }) - - Specify("Refresh with created container succeeds", func() { - createSession := podmanTest.Podman([]string{"create", ALPINE, "ls"}) - createSession.WaitWithDefaultTimeout() - Expect(createSession.ExitCode()).To(Equal(0)) - Expect(podmanTest.NumberOfContainers()).To(Equal(1)) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) - - refreshSession := podmanTest.Podman([]string{"container", "refresh"}) - refreshSession.WaitWithDefaultTimeout() - Expect(refreshSession.ExitCode()).To(Equal(0)) - Expect(podmanTest.NumberOfContainers()).To(Equal(1)) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) - }) - - Specify("Refresh with running container restarts container", func() { - createSession := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"}) - createSession.WaitWithDefaultTimeout() - Expect(createSession.ExitCode()).To(Equal(0)) - Expect(podmanTest.NumberOfContainers()).To(Equal(1)) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) - - // HACK: ensure container starts before we move on - time.Sleep(1 * time.Second) - - refreshSession := podmanTest.Podman([]string{"container", "refresh"}) - refreshSession.WaitWithDefaultTimeout() - Expect(refreshSession.ExitCode()).To(Equal(0)) - Expect(podmanTest.NumberOfContainers()).To(Equal(1)) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) - }) -}) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 9bf742a63..29150d67c 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 030722b47..0e1f0d865 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -273,6 +273,7 @@ var _ = Describe("Podman run", func() { }) It("podman run notify_socket", func() { + SkipIfRemote() host := GetHostDistributionInfo() if host.Distribution != "rhel" && host.Distribution != "centos" && host.Distribution != "fedora" { Skip("this test requires a working runc") diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go index c3edc7c7e..ffb5182d6 100644 --- a/test/e2e/save_test.go +++ b/test/e2e/save_test.go @@ -4,6 +4,7 @@ import ( "os" "path/filepath" + "github.com/containers/libpod/pkg/rootless" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -75,6 +76,9 @@ var _ = Describe("Podman save", func() { }) It("podman save to directory with oci format", func() { + if rootless.IsRootless() && podmanTest.RemoteTest { + Skip("Requires a fix in containers image for chown/lchown") + } outdir := filepath.Join(podmanTest.TempDir, "save") save := podmanTest.Podman([]string{"save", "--format", "oci-dir", "-o", outdir, ALPINE}) @@ -83,6 +87,9 @@ var _ = Describe("Podman save", func() { }) It("podman save to directory with v2s2 docker format", func() { + if rootless.IsRootless() && podmanTest.RemoteTest { + Skip("Requires a fix in containers image for chown/lchown") + } outdir := filepath.Join(podmanTest.TempDir, "save") save := podmanTest.Podman([]string{"save", "--format", "docker-dir", "-o", outdir, ALPINE}) @@ -91,6 +98,9 @@ var _ = Describe("Podman save", func() { }) It("podman save to directory with docker format and compression", func() { + if rootless.IsRootless() && podmanTest.RemoteTest { + Skip("Requires a fix in containers image for chown/lchown") + } outdir := filepath.Join(podmanTest.TempDir, "save") save := podmanTest.Podman([]string{"save", "--compress", "--format", "docker-dir", "-o", outdir, ALPINE}) diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 717eea441..e201204df 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/tag_test.go b/test/e2e/tag_test.go index ff0ac31c4..26d6dfa75 100644 --- a/test/e2e/tag_test.go +++ b/test/e2e/tag_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go index 5bf0331e5..28a4c1e40 100644 --- a/test/e2e/wait_test.go +++ b/test/e2e/wait_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/utils/utils.go b/test/utils/utils.go index 1e0391d2e..beadab549 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -4,7 +4,6 @@ import ( "bufio" "encoding/json" "fmt" - "github.com/containers/libpod/pkg/rootless" "io/ioutil" "os" "os/exec" @@ -42,6 +41,7 @@ type PodmanTest struct { RemotePodmanBinary string VarlinkSession *os.Process VarlinkEndpoint string + VarlinkCommand *exec.Cmd } // PodmanSession wraps the gexec.session so we can extend it @@ -69,9 +69,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string podmanBinary := p.PodmanBinary if p.RemoteTest { podmanBinary = p.RemotePodmanBinary - if !rootless.IsRootless() { - env = append(env, fmt.Sprintf("PODMAN_VARLINK_ADDRESS=%s", p.VarlinkEndpoint)) - } + env = append(env, fmt.Sprintf("PODMAN_VARLINK_ADDRESS=%s", p.VarlinkEndpoint)) } if env == nil { diff --git a/vendor.conf b/vendor.conf index d5e2b60bd..2f7e36d85 100644 --- a/vendor.conf +++ b/vendor.conf @@ -93,8 +93,8 @@ k8s.io/api kubernetes-1.10.13-beta.0 https://github.com/kubernetes/api k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7 -github.com/varlink/go 64e07fabffa33e385817b41971cf2674f692f391 github.com/containers/buildah v1.8.2 +github.com/varlink/go 0f1d566d194b9d6d48e0d47c5e4d822628919066 # TODO: Gotty has not been updated since 2012. Can we find replacement? github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512 github.com/fsouza/go-dockerclient v1.3.0 diff --git a/vendor/github.com/varlink/go/varlink/bridge.go b/vendor/github.com/varlink/go/varlink/bridge.go index 0ea5de682..c478dcd88 100644 --- a/vendor/github.com/varlink/go/varlink/bridge.go +++ b/vendor/github.com/varlink/go/varlink/bridge.go @@ -6,6 +6,7 @@ import ( "bufio" "io" "net" + "os" "os/exec" ) @@ -30,12 +31,13 @@ func (p PipeCon) Close() error { return nil } -// NewConnection returns a new connection to the given address. -func NewBridge(bridge string) (*Connection, error) { +// 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 @@ -56,3 +58,8 @@ func NewBridge(bridge string) (*Connection, error) { 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 index 751224ec8..42953b871 100644 --- a/vendor/github.com/varlink/go/varlink/bridge_windows.go +++ b/vendor/github.com/varlink/go/varlink/bridge_windows.go @@ -4,6 +4,7 @@ import ( "bufio" "io" "net" + "os" "os/exec" ) @@ -28,12 +29,13 @@ func (p PipeCon) Close() error { return nil } -// NewConnection returns a new connection to the given address. -func NewBridge(bridge string) (*Connection, error) { +// 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 @@ -54,3 +56,8 @@ func NewBridge(bridge string) (*Connection, error) { 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/version/version.go b/version/version.go index 29a576317..c63f8b820 100644 --- a/version/version.go +++ b/version/version.go @@ -4,7 +4,7 @@ package version // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -const Version = "1.3.0-dev" +const Version = "1.3.1-dev" // RemoteAPIVersion is the version for the remote // client API. It is used to determine compatibility |