diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | cmd/podman/attach.go | 2 | ||||
-rw-r--r-- | cmd/podman/build.go | 2 | ||||
-rw-r--r-- | cmd/podman/common.go | 4 | ||||
-rw-r--r-- | cmd/podman/export.go | 7 | ||||
-rw-r--r-- | cmd/podman/load.go | 7 | ||||
-rw-r--r-- | cmd/podman/login.go | 2 | ||||
-rw-r--r-- | cmd/podman/main.go | 2 | ||||
-rw-r--r-- | cmd/podman/play_kube.go | 2 | ||||
-rw-r--r-- | cmd/podman/pull.go | 2 | ||||
-rw-r--r-- | cmd/podman/push.go | 2 | ||||
-rw-r--r-- | cmd/podman/run.go | 2 | ||||
-rw-r--r-- | cmd/podman/runlabel.go | 2 | ||||
-rw-r--r-- | cmd/podman/save.go | 8 | ||||
-rw-r--r-- | cmd/podman/search.go | 2 | ||||
-rw-r--r-- | cmd/podman/start.go | 10 | ||||
-rw-r--r-- | completions/zsh/_podman | 385 | ||||
-rw-r--r-- | contrib/spec/podman.spec.in | 1 | ||||
-rw-r--r-- | libpod/container_internal.go | 12 | ||||
-rw-r--r-- | libpod/events.go | 4 | ||||
-rw-r--r-- | libpod/runtime.go | 156 | ||||
-rw-r--r-- | test/test_podman_build.sh | 7 |
22 files changed, 509 insertions, 116 deletions
@@ -27,6 +27,7 @@ CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker) OCI_RUNTIME ?= "" BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions +ZSHINSTALLDIR=${PREFIX}/share/zsh/site-functions SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z) PACKAGES ?= $(shell $(GO) list -tags "${BUILDTAGS}" ./... | grep -v github.com/containers/libpod/vendor | grep -v e2e | grep -v system ) @@ -247,6 +248,8 @@ install.config: install.completions: install ${SELINUXOPT} -d -m 755 ${BASHINSTALLDIR} install ${SELINUXOPT} -m 644 completions/bash/podman ${BASHINSTALLDIR} + install ${SELINUXOPT} -d -m 755 ${ZSHINSTALLDIR} + install ${SELINUXOPT} -m 644 completions/zsh/_podman ${ZSHINSTALLDIR} install.cni: install ${SELINUXOPT} -d -m 755 ${ETCDIR}/cni/net.d/ @@ -332,6 +335,7 @@ API.md: cmd/podman/varlink/io.podman.varlink validate.completions: completions/bash/podman . completions/bash/podman + if [ -x /bin/zsh ]; then /bin/zsh completions/zsh/_podman; fi validate: gofmt .gitvalidation validate.completions diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index 08976cdaa..86e89cfd7 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -35,7 +35,7 @@ func init() { flags := attachCommand.Flags() flags.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") - flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true)") + flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 72d78aff9..f0a67791a 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -52,7 +52,7 @@ func init() { buildCommand.SetHelpTemplate(HelpTemplate()) buildCommand.SetUsageTemplate(UsageTemplate()) flags := buildCommand.Flags() - flags.SetInterspersed(false) + flags.SetInterspersed(true) budFlags := buildahcli.GetBudFlags(&budFlagsValues) flag := budFlags.Lookup("pull") diff --git a/cmd/podman/common.go b/cmd/podman/common.go index b76037297..43dfdb837 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -313,7 +313,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "image-volume", "bind", - "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore' (default 'bind')", + "Tells podman how to handle the builtin image volumes. The options are: 'bind', 'tmpfs', or 'ignore'", ) createFlags.Bool( "init", false, @@ -374,7 +374,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.Int64( "memory-swappiness", -1, - "Tune container memory swappiness (0 to 100) (default -1)", + "Tune container memory swappiness (0 to 100, or -1 for system default)", ) createFlags.String( "name", "", diff --git a/cmd/podman/export.go b/cmd/podman/export.go index e5dc410a7..92633facd 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -36,7 +36,7 @@ func init() { exportCommand.SetHelpTemplate(HelpTemplate()) exportCommand.SetUsageTemplate(UsageTemplate()) flags := exportCommand.Flags() - flags.StringVarP(&exportCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") + flags.StringVarP(&exportCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") } // exportCmd saves a container to a tarball on disk @@ -60,15 +60,16 @@ func exportCmd(c *cliconfig.ExportValues) error { } output := c.Output - if runtime.Remote && (output == "/dev/stdout" || len(output) == 0) { + if runtime.Remote && len(output) == 0 { return errors.New("remote client usage must specify an output file (-o)") } - if output == "/dev/stdout" { + if len(output) == 0 { file := os.Stdout if logrus.IsTerminal(file) { return errors.Errorf("refusing to export to terminal. Use -o flag or redirect") } + output = "/dev/stdout" } if err := parse.ValidateFileName(output); err != nil { diff --git a/cmd/podman/load.go b/cmd/podman/load.go index 303c23bc7..46add699e 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -34,7 +34,7 @@ func init() { loadCommand.SetHelpTemplate(HelpTemplate()) loadCommand.SetUsageTemplate(UsageTemplate()) flags := loadCommand.Flags() - flags.StringVarP(&loadCommand.Input, "input", "i", "/dev/stdin", "Read from archive file, default is STDIN") + flags.StringVarP(&loadCommand.Input, "input", "i", "", "Read from specified archive file (default: stdin)") flags.BoolVarP(&loadCommand.Quiet, "quiet", "q", false, "Suppress the output") flags.StringVar(&loadCommand.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file (not usually used)") @@ -64,7 +64,10 @@ func loadCmd(c *cliconfig.LoadValues) error { if runtime.Remote && len(input) == 0 { return errors.New("the remote client requires you to load via -i and a tarball") } - if input == "/dev/stdin" { + if len(input) == 0 { + input = "/dev/stdin" + c.Input = input + fi, err := os.Stdin.Stat() if err != nil { return err diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 43a7d246e..4e96b43cb 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -45,7 +45,7 @@ func init() { flags.StringVar(&loginCommand.CertDir, "cert-dir", "", "Pathname of a directory containing TLS certificates and keys used to connect to the registry") flags.BoolVar(&loginCommand.GetLogin, "get-login", true, "Return the current login user for the registry") flags.StringVarP(&loginCommand.Password, "password", "p", "", "Password for registry") - flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.StringVarP(&loginCommand.Username, "username", "u", "", "Username for registry") flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin") diff --git a/cmd/podman/main.go b/cmd/podman/main.go index af6731d96..1717e0624 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -115,7 +115,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") rootCmd.PersistentFlags().StringSliceVar(&MainGlobalOpts.HooksDir, "hooks-dir", []string{}, "Set the OCI hooks directory path (may be set multiple times)") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error (default), fatal or panic") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic") rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") rootCmd.PersistentFlags().MarkHidden("max-workers") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Namespace, "namespace", "", "Set the libpod namespace, used to create separate views of the containers and pods on the system") diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index 44aa4776b..eeb1aad64 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -59,7 +59,7 @@ func init() { flags.StringVar(&playKubeCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error { diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 7986d5530..8888c5e28 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -52,7 +52,7 @@ func init() { flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } diff --git a/cmd/podman/push.go b/cmd/podman/push.go index afc385527..a1dac24ae 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -54,7 +54,7 @@ func init() { flags.BoolVar(&pushCommand.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image") flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.StringVar(&pushCommand.SignBy, "sign-by", "", "Add a signature at the destination using the specified key") - flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&pushCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } func pushCmd(c *cliconfig.PushValues) error { diff --git a/cmd/podman/run.go b/cmd/podman/run.go index a92d5d3db..32e7b3510 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -44,7 +44,7 @@ func init() { runCommand.SetUsageTemplate(UsageTemplate()) flags := runCommand.Flags() flags.SetInterspersed(false) - flags.Bool("sig-proxy", true, "Proxy received signals to the process (default true)") + flags.Bool("sig-proxy", true, "Proxy received signals to the process") getCreateFlags(&runCommand.PodmanCommand) } diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index 68621e095..229ff1201 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -57,7 +57,7 @@ func init() { flags.BoolP("pull", "p", false, "Pull the image if it does not exist locally prior to executing the label contents") flags.BoolVarP(&runlabelCommand.Quiet, "quiet", "q", false, "Suppress output information when installing images") flags.StringVar(&runlabelCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") - flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") flags.MarkDeprecated("pull", "podman will pull if not found in local storage") } diff --git a/cmd/podman/save.go b/cmd/podman/save.go index df016b069..c10679740 100644 --- a/cmd/podman/save.go +++ b/cmd/podman/save.go @@ -58,7 +58,7 @@ func init() { flags := saveCommand.Flags() flags.BoolVar(&saveCommand.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&saveCommand.Format, "format", v2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)") - flags.StringVarP(&saveCommand.Output, "output", "o", "/dev/stdout", "Write to a file, default is STDOUT") + flags.StringVarP(&saveCommand.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") flags.BoolVarP(&saveCommand.Quiet, "quiet", "q", false, "Suppress the output") } @@ -79,14 +79,14 @@ func saveCmd(c *cliconfig.SaveValues) error { return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'") } - output := c.Output - if output == "/dev/stdout" { + if len(c.Output) == 0 { fi := os.Stdout if logrus.IsTerminal(fi) { return errors.Errorf("refusing to save to terminal. Use -o flag or redirect") } + c.Output = "/dev/stdout" } - if err := parse.ValidateFileName(output); err != nil { + if err := parse.ValidateFileName(c.Output); err != nil { return err } return runtime.SaveImage(getContext(), c) diff --git a/cmd/podman/search.go b/cmd/podman/search.go index 25f5a98b7..5997e144a 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -46,7 +46,7 @@ func init() { flags.StringVar(&searchCommand.Format, "format", "", "Change the output format to a Go template") flags.IntVar(&searchCommand.Limit, "limit", 0, "Limit the number of results") flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") - flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") + flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } func searchCmd(c *cliconfig.SearchValues) error { diff --git a/cmd/podman/start.go b/cmd/podman/start.go index e942c1ccd..cf406cf66 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -41,7 +41,7 @@ func init() { flags.StringVar(&startCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") - flags.BoolVar(&startCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process (default true if attaching, false otherwise)") + flags.BoolVar(&startCommand.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") markFlagHiddenForRemoteClient("latest", flags) } @@ -62,14 +62,10 @@ func startCmd(c *cliconfig.StartValues) error { return errors.Errorf("you cannot start and attach multiple containers at once") } - sigProxy := c.SigProxy + sigProxy := c.SigProxy || attach if sigProxy && !attach { - if c.Flag("sig-proxy").Changed { - return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") - } else { - sigProxy = false - } + return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach") } runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) diff --git a/completions/zsh/_podman b/completions/zsh/_podman new file mode 100644 index 000000000..530649c0c --- /dev/null +++ b/completions/zsh/_podman @@ -0,0 +1,385 @@ +#compdef podman + +# To get zsh to reread this file: unset -f _podman;rm -f ~/.zcompdump;compinit + +# On rereads, reset cache. (Not that the cacheing works, but some day it might) +unset -m '_podman_*' + +############################################################################### +# BEGIN 'podman help' parsers -- for options, subcommands, and usage + +# Run 'podman XX --help', set _podman_commands to a formatted list of cmds +_read_podman_commands() { + local line + + # Cache: the intention here is to run each 'podman help' only once. + # Unfortunately it doesn't seem to actually be working: even though + # I can see the var_ref in my shell, it's not visible here. + local _var_ref=_podman_commands_"${*// /_}" + typeset -ga _podman_commands + _podman_commands=(${(P)_var_ref}) + (( $#_podman_commands )) && return + + _call_program podman podman "$@" --help |\ + sed -n -e '0,/^Available Commands/d' -e '/^[A-Z]/q;p' |\ + sed -e 's/^ \+\([^ ]\+\) \+/\1:/' |\ + egrep . | while read line; do + _podman_commands+=($line) + done + + eval "typeset -ga $_var_ref" + eval "$_var_ref=(\$_podman_commands)" +} + +# Run 'podman XX --help', set _podman_flag_list to a formatted list +# of flag options for XX +_read_podman_flags() { + local line + + local _var_ref=_podman_flags_"${*// /_}" + eval "typeset -ga ${_var_ref}" + typeset -ga _podman_flag_list + _podman_flag_list=(${(P)_var_ref}) + (( $#_podman_flag_list )) && return + + # Extract the Flags; strip leading whitespace; pack '-f, --foo' + # as '-f,--foo' (no space); then add '=' to '--foo string'. + # The result will be, e.g. '-f,--foo=string Description of Option' + _call_program podman podman "$@" --help |\ + sed -n -e '0,/^Flags:/d' -e '/^$/q;p' |\ + sed -e 's/^ *//' -e 's/^\(-.,\) --/\1--/' |\ + sed -e 's/^\(-[^ ]\+\) \([^ ]\+\) /\1=\2 /' |\ + while read flags desc;do + # flags like --foo=string: split into --foo & string + local -a tmpa + local optval= + tmpa=(${(s.=.)flags}) + if [ -n "$tmpa[2]" ]; then + flags=$tmpa[1] + optval=$tmpa[2] + fi + + # 'podman attach --detach-keys' includes ']' in help msg + desc=${desc//\]/\\]} + + for flag in ${(s:,:)flags}; do + if [ -n "$optval" ]; then + _podman_flag_list+=("${flag}[$desc]:$(_podman_find_helper ${flags} ${optval} ${desc})") + else + _podman_flag_list+=("${flag}[$desc]") + fi + done + done + + eval "typeset -ga $_var_ref=(\$_podman_flag_list)" +} + +# Run 'podman XXX --help', set _podman_usage to the line after "Usage:" +_read_podman_usage() { + local _var_ref=_podman_usage_"${*// /_}" + typeset -ga _podman_usage + _podman_usage=${(P)_var_ref} + (( $#_podman_usage )) && return + + _podman_usage=$(_call_program podman podman "$@" --help |\ + grep -A1 'Usage:'|\ + tail -1 |\ + sed -e 's/^ *//') + + eval "typeset -ga $_var_ref" + eval "$_var_ref=\$_podman_usage" +} + +# END 'podman help' parsers +############################################################################### +# BEGIN custom helpers for individual option arguments + +# Find a zsh helper for a given flag or command-line option +_podman_find_helper() { + local flags=$1 + local optval=$2 + local desc=$3 + local helper= + + # Yes, this is a lot of hardcoding. IMHO it's still better than + # hardcoding every possible podman option. + # FIXME: there are many more options that could use helpers. + if expr "$desc" : ".*[Dd]irectory" >/dev/null; then + optval="directory" + helper="_files -/" + elif expr "$desc" : ".*[Pp]ath" >/dev/null; then + optval="path" + helper=_files + elif [ "$flags" = "--cgroup-manager" ]; then + optval="cgroup manager" + helper="(cgroupfs systemd)" + elif [ "$flags" = "--log-level" ]; then + optval="log level" + # 'Log messages above specified level: debug, ... (default "...")' + # Strip off the description and all 'default' strings + desc=${desc/Log*:/} # debug, info, ... (default "...") + desc=${(S)desc//\(*\)/} # debug, info, ... or panic + desc=${desc//,/} # debug info ... or panic + desc=${desc// or / } # debug info ... panic + desc=${desc// / } # collapse multiple spaces + # FIXME: how to present values _in order_, not sorted alphabetically? + helper="($desc)" + fi + echo "$optval:$helper" +} + +# END custom helpers for individual option arguments +############################################################################### +# BEGIN helpers for command-line args (containers, images) + +__podman_helper_generic() { + local expl line + local -a results + + local foo1=$1; shift + local name=$2; shift + + _call_program $foo1 podman "$@" |\ + while read line; do + results+=(${=line}) + done + + _wanted $foo1 expl $name compadd ${(u)results} +} + +_podman_helper_image() { + __podman_helper_generic podman-images 'images' \ + images --format '{{.ID}}\ {{.Repository}}:{{.Tag}}' +} + +# FIXME: at some point, distinguish between running & stopped containers +_podman_helper_container() { + __podman_helper_generic podman-containers 'containers' \ + ps -a --format '{{.Names}}\ {{.ID}}' +} + +_podman_helper_pod() { + __podman_helper_generic podman-pods 'pods' pod list --format '{{.Name}}' +} + +_podman_helper_volume() { + __podman_helper_generic podman-volumes 'volumes' volume ls --format '{{.Name}}' +} + +# Combinations. This one seen in diff & inspect +_podman_helper_container-or-image() { + _podman_helper_image + _podman_helper_container +} + +# Seen in generate-kube +_podman_helper_container-or-pod() { + _podman_helper_container + _podman_helper_pod +} + +# For top and pod-top +_podman_helper_format-descriptors() { + __podman_helper_generic top-format-descriptors 'format descriptors' \ + top --list-descriptors +} + +# for push, login/logout, and trust +# FIXME: some day, use this to define a helper for IMAGE-PATH (in 'pull') +_podman_helper_registry() { + local expl + local -a registries + + # Suggestions for improvement more than welcome. + python3 -c 'from configparser import ConfigParser;cp=ConfigParser();cp.read("/etc/containers/registries.conf");registries=eval(cp.get("registries.search","registries"));[print(r) for r in registries]' 2>/dev/null | while read line; do + registries+=($line) + done + + if (( $#registries )); then + _wanted podman-registry expl "registry" compadd ${(u)registries} + else + _hosts + fi +} + +# END helpers for command-line args +############################################################################### +# BEGIN figure out completion helpers for a given (sub)command + +# Read Usage string for this subcommand, set up helpers for its subargs +_set_up_podman_args() { + _read_podman_usage "$@" + + typeset -ga _podman_args=() + # E.g. 'podman exec [flags] CONTAINER [...' -> 'CONTAINER [....' + local usage_rhs=$(expr "$_podman_usage" : ".*\[flags\] \+\(.*\)") + + # e.g. podman pod ps which takes no further args + if [ -z "$usage_rhs" ]; then + return + fi + + # podman diff & inspect accept 'CONTAINER | IMAGE'; make into one keyword. + usage_rhs=${usage_rhs// | /-OR-} + + # Arg parsing. There are three possibilities in Usage messages: + # + # [IMAGE] - optional image arg (zero or one) + # IMAGE - exactly one image arg + # IMAGE [IMAGE...] - one or more image args + # and, theoretically: + # [IMAGE...] - zero or more? Haven't seen it in practice. Defer. + # + # For completion purposes, we only need to provide two options: + # one, or more than one? That is: continue offering completion + # suggestions after the first one? For that, we make two passes; + # in the first, mark an option as either '' (only one) or + + # Parse each command-line arg seen in usage message + local word + local -A _seen=() + for word in ${=usage_rhs}; do + local unbracketed=$(expr "$word" : "\[\(.*\)\]") + + if [ -n "$unbracketed" ]; then + # Remove all dots; assume(!?) that they'll all be at the end + unbracketed=${unbracketed//./} + + if (( $_seen[$unbracketed] )); then + # Is this the same word as the previous arg? + if expr "$_podman_args[-1]" : ":$unbracketed:" >/dev/null; then + # Yes. Make it '*:...' instead of ':...', indicating >1 + _podman_args[-1]="*$_podman_args[-1]" + fi + continue + fi + + word=$unbracketed + fi + + # As of 2019-03 all such instances are '[COMMAND [ARG...]]' and are, + # of course, at the end of the line. We can't offer completion for + # these, because the container will have different commands than + # the host system... but try anyway. + if [ "$word" = '[COMMAND' ]; then + # e.g. podman create, exec, run + _podman_args+=( + ":command: _command_names -e" + "*::arguments: _normal" + ) + return + fi + + # Look for an existing helper, e.g. IMAGE -> _podman_helper_image + local helper="_podman_helper_${(L)word}" + if (( $+functions[$helper] )); then + : + else + # No defined helper. Reset, but check for known expressions. + helper= + case "$word" in + KUBEFILE) helper='_files -g "*.y(|a)ml(-.)"' ;; + PATH) helper='_files' ;; + esac + fi + + # Another special case: 'top' actually takes multiple options + local multi= + if [ "$word" = "FORMAT-DESCRIPTORS" ]; then + multi='*' + fi + _podman_args+=("$multi:${(L)word}:$helper") + _seen[$word]=1 + done +} + +# For an endpoint command, i.e. not a subcommand. +_podman_terminus() { + typeset -A opt_args + typeset -ga _podman_flag_list + typeset -ga _podman_args + integer ret=1 + + # Find out what args it takes (e.g. image(s), container(s)) and see + # if we have helpers for them. + _set_up_podman_args "$@" + _arguments -C $_podman_flag_list $_podman_args && ret=0 + + return ret +} + +# END figure out completion helpers for a given (sub)command +################################################################################ +# BEGIN actual entry point + +# This is the main entry point; it's also where we (recursively) come in +# to handle nested subcommands such as 'podman container' or even 3-level +# ones like 'podman generate kube'. Nesting is complicated, so here's +# my best understanding as of 2019-03-12: +# +# Easy first: when you do "podman <TAB>" zsh calls us, we run 'podman --help', +# figure out the global options and subcommands, and run the magic _arguments +# command. That will offer those options/subcommands, and complete, etc. +# +# Where it gets harder is when you do "podman container mount <TAB>". +# zsh first calls us with words=(podman container mount) but we don't +# want all that full context yet! We want to go a piece at a time, +# handling 'container' first, then 'mount'; ending up with our +# final 'podman container mount --help' giving us suitable flags +# and no subcommands; from which we determine that it's a terminus +# and jump to a function that handles non-subcommand arguments. +# +# This is the closest I've yet come to understanding zsh completion; +# it is still incomplete and may in fact be incorrect. But it works +# better than anything I've played with so far. +_podman_subcommand() { + local curcontext="$curcontext" state line + typeset -A opt_args + integer ret=1 + + # Run 'podman --help' / 'podman system --help' for our context (initially + # empty, then possibly under subcommands); from those, get a list of + # flags appropriate for this context and, if applicable, subcommands. + _read_podman_flags "$@" + _read_podman_commands "$@" + + # Now, is this a sub-subcommand, or does it have args? + if (( $#_podman_commands )); then + # Subcommands required (podman, podman system, etc) + local cmd=${words// /_} + _arguments -C $_podman_flag_list \ + "(-): :->command" \ + "(-)*:: :->option-or-argument" \ + && ret=0 + + case $state in + (command) + _describe -t command "podman $* command" _podman_commands && ret=0 + ;; + (option-or-argument) + # I think this is when we have a _completed_ subcommand. + # Recurse back into here, offering options for that subcommand. + curcontext=${curcontext%:*:*}:podman-${words[1]}: + _podman_subcommand "$@" ${words[1]} && ret=0 + ;; + esac + else + # At a terminus, i.e. podman info, podman history; find out + # what args it takes. + _podman_terminus "$@" && ret=0 + fi + + return ret +} + +_podman() { + _podman_subcommand +} + +# Local Variables: +# mode: shell-script +# sh-indentation: 4 +# indent-tabs-mode: nil +# sh-basic-offset: 4 +# End: +# vim: ft=zsh sw=4 ts=4 et diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 703b942b6..3324ee8f9 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -472,6 +472,7 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %{_mandir}/man1/*.1* %{_mandir}/man5/*.5* %{_datadir}/bash-completion/completions/* +%{_datadir}/zsh/site-functions/* %{_libexecdir}/%{name}/conmon %config(noreplace) %{_sysconfdir}/cni/net.d/87-%{name}-bridge.conflist %{_datadir}/containers/%{repo}.conf diff --git a/libpod/container_internal.go b/libpod/container_internal.go index bea7acd69..872802016 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -337,11 +337,13 @@ func (c *Container) setupStorage(ctx context.Context) error { } // Set the default Entrypoint and Command - if c.config.Entrypoint == nil { - c.config.Entrypoint = containerInfo.Config.Config.Entrypoint - } - if c.config.Command == nil { - c.config.Command = containerInfo.Config.Config.Cmd + if containerInfo.Config != nil { + if c.config.Entrypoint == nil { + c.config.Entrypoint = containerInfo.Config.Config.Entrypoint + } + if c.config.Command == nil { + c.config.Command = containerInfo.Config.Config.Cmd + } } artifacts := filepath.Join(c.config.StaticDir, artifactsDir) diff --git a/libpod/events.go b/libpod/events.go index 879aeb6c5..f09529a05 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -1,6 +1,8 @@ package libpod import ( + "os" + "github.com/containers/libpod/libpod/events" "github.com/hpcloud/tail" "github.com/pkg/errors" @@ -85,7 +87,7 @@ func (r *Runtime) Events(fromStart, stream bool, options []events.EventFilter, e func (r *Runtime) getTail(fromStart, stream bool) (*tail.Tail, error) { reopen := true - seek := tail.SeekInfo{Offset: 0, Whence: 2} + seek := tail.SeekInfo{Offset: 0, Whence: os.SEEK_END} if fromStart || !stream { seek.Whence = 0 reopen = false diff --git a/libpod/runtime.go b/libpod/runtime.go index fa208a2ca..9836b7aab 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -241,6 +241,12 @@ type runtimeConfiguredFrom struct { libpodStaticDirSet bool libpodTmpDirSet bool volPathSet bool + conmonPath bool + conmonEnvVars bool + ociRuntimes bool + runtimePath bool + cniPluginDir bool + noPivotRoot bool } var ( @@ -324,6 +330,22 @@ func SetXdgRuntimeDir(val string) error { // NewRuntime creates a new container runtime // Options can be passed to override the default configuration for the runtime func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { + return newRuntimeFromConfig("", options...) +} + +// NewRuntimeFromConfig creates a new container runtime using the given +// configuration file for its default configuration. Passed RuntimeOption +// functions can be used to mutate this configuration further. +// An error will be returned if the configuration file at the given path does +// not exist or cannot be loaded +func NewRuntimeFromConfig(userConfigPath string, options ...RuntimeOption) (runtime *Runtime, err error) { + if userConfigPath == "" { + return nil, errors.New("invalid configuration file specified") + } + return newRuntimeFromConfig(userConfigPath, options...) +} + +func newRuntimeFromConfig(userConfigPath string, options ...RuntimeOption) (runtime *Runtime, err error) { runtime = new(Runtime) runtime.config = new(RuntimeConfig) runtime.configuredFrom = new(runtimeConfiguredFrom) @@ -358,11 +380,6 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { rootlessConfigPath = filepath.Join(home, ".config/containers/libpod.conf") - configPath = rootlessConfigPath - if _, err := os.Stat(configPath); err != nil { - foundConfig = false - } - runtimeDir, err := util.GetRootlessRuntimeDir() if err != nil { return nil, err @@ -374,6 +391,20 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") } + } + + if userConfigPath != "" { + configPath = userConfigPath + if _, err := os.Stat(configPath); err != nil { + // If the user specified a config file, we must fail immediately + // when it doesn't exist + return nil, errors.Wrapf(err, "cannot stat %s", configPath) + } + } else if rootless.IsRootless() { + configPath = rootlessConfigPath + if _, err := os.Stat(configPath); err != nil { + foundConfig = false + } } else if _, err := os.Stat(OverrideConfigPath); err == nil { // Use the override configuration path configPath = OverrideConfigPath @@ -409,6 +440,24 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { if tmpConfig.VolumePath != "" { runtime.configuredFrom.volPathSet = true } + if tmpConfig.ConmonPath != nil { + runtime.configuredFrom.conmonPath = true + } + if tmpConfig.ConmonEnvVars != nil { + runtime.configuredFrom.conmonEnvVars = true + } + if tmpConfig.OCIRuntimes != nil { + runtime.configuredFrom.ociRuntimes = true + } + if tmpConfig.RuntimePath != nil { + runtime.configuredFrom.runtimePath = true + } + if tmpConfig.CNIPluginDir != nil { + runtime.configuredFrom.cniPluginDir = true + } + if tmpConfig.NoPivotRoot { + runtime.configuredFrom.noPivotRoot = true + } if _, err := toml.Decode(string(contents), runtime.config); err != nil { return nil, errors.Wrapf(err, "error decoding configuration file %s", configPath) @@ -428,12 +477,24 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { } // Cherry pick the settings we want from the global configuration - runtime.config.ConmonPath = tmpConfig.ConmonPath - runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars - runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes - runtime.config.RuntimePath = tmpConfig.RuntimePath - runtime.config.CNIPluginDir = tmpConfig.CNIPluginDir - runtime.config.NoPivotRoot = tmpConfig.NoPivotRoot + if !runtime.configuredFrom.conmonPath { + runtime.config.ConmonPath = tmpConfig.ConmonPath + } + if !runtime.configuredFrom.conmonEnvVars { + runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars + } + if !runtime.configuredFrom.ociRuntimes { + runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes + } + if !runtime.configuredFrom.runtimePath { + runtime.config.RuntimePath = tmpConfig.RuntimePath + } + if !runtime.configuredFrom.cniPluginDir { + runtime.config.CNIPluginDir = tmpConfig.CNIPluginDir + } + if !runtime.configuredFrom.noPivotRoot { + runtime.config.NoPivotRoot = tmpConfig.NoPivotRoot + } break } } @@ -465,80 +526,9 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { return runtime, nil } -// NewRuntimeFromConfig creates a new container runtime using the given -// configuration file for its default configuration. Passed RuntimeOption -// functions can be used to mutate this configuration further. -// An error will be returned if the configuration file at the given path does -// not exist or cannot be loaded -func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime *Runtime, err error) { - runtime = new(Runtime) - runtime.config = new(RuntimeConfig) - runtime.configuredFrom = new(runtimeConfiguredFrom) - - // Set three fields not in the TOML config - runtime.config.StateType = defaultRuntimeConfig.StateType - runtime.config.OCIRuntime = defaultRuntimeConfig.OCIRuntime - - storageConf, err := util.GetDefaultStoreOptions() - if err != nil { - return nil, errors.Wrapf(err, "error retrieving storage config") - } - runtime.config.StorageConfig = storageConf - runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod") - runtime.config.VolumePath = filepath.Join(storageConf.GraphRoot, "volumes") - - tmpDir, err := getDefaultTmpDir() - if err != nil { - return nil, err - } - runtime.config.TmpDir = tmpDir - if rootless.IsRootless() { - runtimeDir, err := util.GetRootlessRuntimeDir() - if err != nil { - return nil, err - } - // containers/image uses XDG_RUNTIME_DIR to locate the auth file. - // So make sure the env variable is set. - if err := SetXdgRuntimeDir(runtimeDir); err != nil { - return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") - } - } - - // Check to see if the given configuration file exists - if _, err := os.Stat(configPath); err != nil { - return nil, errors.Wrapf(err, "error checking existence of configuration file %s", configPath) - } - - // Read contents of the config file - contents, err := ioutil.ReadFile(configPath) - if err != nil { - return nil, errors.Wrapf(err, "error reading configuration file %s", configPath) - } - - // Decode configuration file - if _, err := toml.Decode(string(contents), runtime.config); err != nil { - return nil, errors.Wrapf(err, "error decoding configuration from file %s", configPath) - } - - // Overwrite the config with user-given configuration options - for _, opt := range options { - if err := opt(runtime); err != nil { - return nil, errors.Wrapf(err, "error configuring runtime") - } - } - - if err := makeRuntime(runtime); err != nil { - return nil, err - } - - return runtime, nil -} - // Make a new runtime based on the given configuration // Sets up containers/storage, state store, OCI runtime func makeRuntime(runtime *Runtime) (err error) { - runtime.config.EventsLogFilePath = filepath.Join(runtime.config.TmpDir, "events", "events.log") - // Backward compatibility for `runtime_path` if runtime.config.RuntimePath != nil { // Don't print twice in rootless mode. @@ -697,6 +687,8 @@ func makeRuntime(runtime *Runtime) (err error) { runtime.config.VolumePath = dbConfig.VolumePath } + runtime.config.EventsLogFilePath = filepath.Join(runtime.config.TmpDir, "events", "events.log") + logrus.Debugf("Using graph driver %s", runtime.config.StorageConfig.GraphDriverName) logrus.Debugf("Using graph root %s", runtime.config.StorageConfig.GraphRoot) logrus.Debugf("Using run root %s", runtime.config.StorageConfig.RunRoot) diff --git a/test/test_podman_build.sh b/test/test_podman_build.sh index 9faefc78a..39f1e784d 100644 --- a/test/test_podman_build.sh +++ b/test/test_podman_build.sh @@ -34,6 +34,13 @@ echo ######################################################## echo ######################################################## +echo test "build directory before other options create a tag" +echo ######################################################## +TARGET=tagged-image +podman build $HOME/test/build/from-scratch --quiet=True -t $TARGET +podman images | grep tagged-image + +echo ######################################################## echo test "build-preserve-subvolumes" echo ######################################################## TARGET=volume-image |