summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml19
-rw-r--r--cmd/podman/common/create.go26
-rw-r--r--cmd/podman/common/create_opts.go38
-rw-r--r--cmd/podman/containers/create.go27
-rw-r--r--cmd/podman/containers/ps.go8
-rw-r--r--cmd/podman/containers/rm.go4
-rw-r--r--cmd/podman/images/build.go10
-rw-r--r--cmd/podman/images/history.go2
-rw-r--r--cmd/podman/images/pull.go28
-rw-r--r--cmd/podman/manifest/add.go4
-rw-r--r--cmd/podman/manifest/annotate.go4
-rw-r--r--cmd/podman/utils/alias.go6
-rw-r--r--completions/fish/podman-remote.fish30
-rw-r--r--completions/fish/podman.fish30
-rwxr-xr-xcontrib/cirrus/runner.sh54
-rwxr-xr-xcontrib/cirrus/setup_environment.sh2
-rw-r--r--docs/source/Introduction.rst4
-rw-r--r--docs/source/_static/api.html2
-rw-r--r--docs/source/index.rst4
-rw-r--r--docs/source/markdown/podman-create.1.md16
-rw-r--r--docs/source/markdown/podman-network-create.1.md3
-rw-r--r--docs/source/markdown/podman-pull.1.md22
-rw-r--r--docs/source/markdown/podman-run.1.md16
-rw-r--r--docs/source/markdown/podman.1.md2
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--libpod/container_api.go29
-rw-r--r--libpod/container_internal_linux.go63
-rw-r--r--libpod/container_path_resolution.go166
-rw-r--r--libpod/image/search.go28
-rw-r--r--libpod/network/create.go9
-rw-r--r--libpod/networking_linux.go8
-rw-r--r--libpod/oci_conmon_exec_linux.go48
-rw-r--r--libpod/runtime.go14
-rw-r--r--libpod/shutdown/handler.go10
-rw-r--r--libpod/util.go17
-rw-r--r--pkg/api/handlers/compat/containers.go2
-rw-r--r--pkg/api/handlers/compat/images_search.go84
-rw-r--r--pkg/api/handlers/libpod/containers.go17
-rw-r--r--pkg/api/handlers/libpod/images.go86
-rw-r--r--pkg/api/handlers/libpod/images_pull.go18
-rw-r--r--pkg/api/handlers/swagger/swagger.go3
-rw-r--r--pkg/api/server/register_containers.go5
-rw-r--r--pkg/api/server/register_images.go15
-rw-r--r--pkg/api/server/server.go8
-rw-r--r--pkg/bindings/containers/types.go1
-rw-r--r--pkg/bindings/containers/types_list_options.go16
-rw-r--r--pkg/bindings/images/types.go12
-rw-r--r--pkg/bindings/images/types_pull_options.go54
-rw-r--r--pkg/domain/entities/containers.go2
-rw-r--r--pkg/domain/entities/images.go12
-rw-r--r--pkg/domain/infra/abi/archive.go14
-rw-r--r--pkg/domain/infra/abi/containers_stat.go140
-rw-r--r--pkg/domain/infra/abi/images.go6
-rw-r--r--pkg/domain/infra/tunnel/containers.go33
-rw-r--r--pkg/domain/infra/tunnel/images.go4
-rw-r--r--pkg/ps/ps.go2
-rw-r--r--pkg/specgen/generate/container.go14
-rw-r--r--pkg/specgen/generate/container_create.go12
-rw-r--r--pkg/terminal/console_windows.go2
-rw-r--r--test/apiv2/20-containers.at9
-rw-r--r--test/e2e/common_test.go4
-rw-r--r--test/e2e/create_test.go12
-rw-r--r--test/e2e/history_test.go17
-rw-r--r--test/e2e/inspect_test.go23
-rw-r--r--test/e2e/network_create_test.go17
-rw-r--r--test/e2e/ps_test.go7
-rw-r--r--test/e2e/pull_test.go10
-rw-r--r--test/e2e/rm_test.go2
-rw-r--r--test/e2e/run_test.go2
-rw-r--r--test/e2e/run_working_dir_test.go6
-rw-r--r--test/e2e/search_test.go13
-rw-r--r--test/python/docker/test_images.py12
-rw-r--r--test/system/030-run.bats21
-rw-r--r--test/system/040-ps.bats17
-rw-r--r--test/system/050-stop.bats43
-rw-r--r--test/system/070-build.bats58
-rw-r--r--test/system/075-exec.bats2
-rw-r--r--test/system/400-unprivileged-access.bats11
-rw-r--r--test/system/410-selinux.bats11
-rw-r--r--vendor/github.com/google/uuid/version4.go8
-rw-r--r--vendor/modules.txt2
82 files changed, 1003 insertions, 623 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index b3f43238e..5b0d9ee6c 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -254,20 +254,27 @@ bindings_task:
always: *runner_stats
-# Build the "libpod" API documentation `swagger.yaml` for eventual
-# publishing along side the official podman documentation.
+# Build the "libpod" API documentation `swagger.yaml` and
+# publish it to google-cloud-storage (GCS).
swagger_task:
name: "Test Swagger"
alias: swagger
depends_on:
- build
- container: *smallcontainer
+ gce_instance: *standardvm
env:
<<: *stdenvars
TEST_FLAVOR: swagger
- TEST_ENVIRON: container
- CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
- clone_script: *full_clone # build-cache not available to container tasks
+ # TODO: Due to podman 3.0 activity (including new images), avoid
+ # disturbing the status-quo just to incorporate this one new
+ # container image. Uncomment line below when CI activities normalize.
+ #CTR_FQIN: 'quay.io/libpod/gcsupld:${IMAGE_SUFFIX}'
+ CTR_FQIN: 'quay.io/libpod/gcsupld:c4813063494828032'
+ GCPJSON: ENCRYPTED[927dc01e755eaddb4242b0845cf86c9098d1e3dffac38c70aefb1487fd8b4fe6dd6ae627b3bffafaba70e2c63172664e]
+ GCPNAME: ENCRYPTED[c145e9c16b6fb88d476944a454bf4c1ccc84bb4ecaca73bdd28bdacef0dfa7959ebc8171a27b2e4064d66093b2cdba49]
+ GCPPROJECT: 'libpod-218412'
+ gopath_cache: *ro_gopath_cache
+ clone_script: *noop # Comes from cache
setup_script: *setup
main_script: *main
always: *binary_artifacts
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 17fba5427..915ff63b6 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -474,29 +474,29 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
)
_ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone)
- overrideArchFlagName := "override-arch"
+ archFlagName := "arch"
createFlags.StringVar(
- &cf.OverrideArch,
- overrideArchFlagName, "",
+ &cf.Arch,
+ archFlagName, "",
"use `ARCH` instead of the architecture of the machine for choosing images",
)
- _ = cmd.RegisterFlagCompletionFunc(overrideArchFlagName, completion.AutocompleteNone)
+ _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch)
- overrideOSFlagName := "override-os"
+ osFlagName := "os"
createFlags.StringVar(
- &cf.OverrideOS,
- overrideOSFlagName, "",
+ &cf.OS,
+ osFlagName, "",
"use `OS` instead of the running OS for choosing images",
)
- _ = cmd.RegisterFlagCompletionFunc(overrideOSFlagName, completion.AutocompleteNone)
+ _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS)
- overrideVariantFlagName := "override-variant"
+ variantFlagName := "variant"
createFlags.StringVar(
- &cf.OverrideVariant,
- overrideVariantFlagName, "",
+ &cf.Variant,
+ variantFlagName, "",
"Use _VARIANT_ instead of the running architecture variant for choosing images",
)
- _ = cmd.RegisterFlagCompletionFunc(overrideVariantFlagName, completion.AutocompleteNone)
+ _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
pidFlagName := "pid"
createFlags.String(
@@ -516,7 +516,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
createFlags.StringVar(
&cf.Platform,
platformFlagName, "",
- "Specify the platform for selecting the image. (Conflicts with override-arch and override-os)",
+ "Specify the platform for selecting the image. (Conflicts with --arch and --os)",
)
_ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone)
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index f252618ce..d86a6d364 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -3,6 +3,7 @@ package common
import (
"fmt"
"net"
+ "path/filepath"
"strconv"
"strings"
@@ -73,9 +74,9 @@ type ContainerCLIOpts struct {
NoHealthCheck bool
OOMKillDisable bool
OOMScoreAdj int
- OverrideArch string
- OverrideOS string
- OverrideVariant string
+ Arch string
+ OS string
+ Variant string
PID string
PIDsLimit *int64
Platform string
@@ -346,9 +347,9 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
Name: cc.Name,
OOMScoreAdj: cc.HostConfig.OomScoreAdj,
- OverrideArch: "",
- OverrideOS: "",
- OverrideVariant: "",
+ Arch: "",
+ OS: "",
+ Variant: "",
PID: string(cc.HostConfig.PidMode),
PIDsLimit: cc.HostConfig.PidsLimit,
Privileged: cc.HostConfig.Privileged,
@@ -383,8 +384,29 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
}
// volumes
- if volumes := cc.HostConfig.Binds; len(volumes) > 0 {
- cliOpts.Volume = volumes
+ volDestinations := make(map[string]bool)
+ for _, vol := range cc.HostConfig.Binds {
+ cliOpts.Volume = append(cliOpts.Volume, vol)
+ // Extract the destination so we don't add duplicate mounts in
+ // the volumes phase.
+ splitVol := strings.SplitN(vol, ":", 3)
+ switch len(splitVol) {
+ case 1:
+ volDestinations[vol] = true
+ default:
+ volDestinations[splitVol[1]] = true
+ }
+ }
+ // Anonymous volumes are added differently from other volumes, in their
+ // own special field, for reasons known only to Docker. Still use the
+ // format of `-v` so we can just append them in there.
+ // Unfortunately, these may be duplicates of existing mounts in Binds.
+ // So... We need to catch that.
+ for vol := range cc.Volumes {
+ if _, ok := volDestinations[filepath.Clean(vol)]; ok {
+ continue
+ }
+ cliOpts.Volume = append(cliOpts.Volume, vol)
}
if len(cc.HostConfig.BlkioWeightDevice) > 0 {
devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice))
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 420813ba9..5c6c773eb 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -237,17 +237,20 @@ func pullImage(imageName string) (string, error) {
imageMissing = !br.Value
}
- if cliVals.Platform != "" {
- if cliVals.OverrideArch != "" || cliVals.OverrideOS != "" {
- return "", errors.Errorf("--platform option can not be specified with --override-arch or --override-os")
- }
- split := strings.SplitN(cliVals.Platform, "/", 2)
- cliVals.OverrideOS = split[0]
- if len(split) > 1 {
- cliVals.OverrideArch = split[1]
+ if cliVals.Platform != "" || cliVals.Arch != "" || cliVals.OS != "" {
+ if cliVals.Platform != "" {
+ if cliVals.Arch != "" || cliVals.OS != "" {
+ return "", errors.Errorf("--platform option can not be specified with --arch or --os")
+ }
+ split := strings.SplitN(cliVals.Platform, "/", 2)
+ cliVals.OS = split[0]
+ if len(split) > 1 {
+ cliVals.Arch = split[1]
+ }
}
+
if pullPolicy != config.PullImageAlways {
- logrus.Info("--platform causes the pull policy to be \"always\"")
+ logrus.Info("--platform --arch and --os causes the pull policy to be \"always\"")
pullPolicy = config.PullImageAlways
}
}
@@ -259,9 +262,9 @@ func pullImage(imageName string) (string, error) {
pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{
Authfile: cliVals.Authfile,
Quiet: cliVals.Quiet,
- OverrideArch: cliVals.OverrideArch,
- OverrideOS: cliVals.OverrideOS,
- OverrideVariant: cliVals.OverrideVariant,
+ Arch: cliVals.Arch,
+ OS: cliVals.OS,
+ Variant: cliVals.Variant,
SignaturePolicy: cliVals.SignaturePolicy,
PullPolicy: pullPolicy,
})
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index d23771fc5..31f44d92f 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -78,7 +78,7 @@ func listFlagSet(cmd *cobra.Command) {
flags := cmd.Flags()
flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers")
- flags.BoolVar(&listOpts.Storage, "external", false, "Show containers in storage not controlled by Podman")
+ flags.BoolVar(&listOpts.External, "external", false, "Show containers in storage not controlled by Podman")
filterFlagName := "filter"
flags.StringSliceVarP(&filters, filterFlagName, "f", []string{}, "Filter output based on conditions given")
@@ -132,10 +132,10 @@ func checkFlags(c *cobra.Command) error {
}
cfg := registry.PodmanConfig()
if cfg.Engine.Namespace != "" {
- if c.Flag("storage").Changed && listOpts.Storage {
- return errors.New("--namespace and --storage flags can not both be set")
+ if c.Flag("storage").Changed && listOpts.External {
+ return errors.New("--namespace and --external flags can not both be set")
}
- listOpts.Storage = false
+ listOpts.External = false
}
return nil
diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go
index ea616b6e5..884ad05f4 100644
--- a/cmd/podman/containers/rm.go
+++ b/cmd/podman/containers/rm.go
@@ -140,6 +140,10 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit
}
func setExitCode(err error) {
+ // If error is set to no such container, do not reset
+ if registry.GetExitCode() == 1 {
+ return
+ }
cause := errors.Cause(err)
switch {
case cause == define.ErrNoSuchCtr:
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index 1029e03d1..957c0ac2d 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -266,7 +266,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
pullPolicy := imagebuildah.PullIfMissing
if c.Flags().Changed("pull") && flags.Pull {
- pullPolicy = imagebuildah.PullAlways
+ pullPolicy = imagebuildah.PullIfNewer
}
if flags.PullAlways {
pullPolicy = imagebuildah.PullAlways
@@ -423,10 +423,10 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
Ulimit: flags.Ulimit,
Volumes: flags.Volumes,
},
- Compression: compression,
- ConfigureNetwork: networkPolicy,
- ContextDirectory: contextDir,
- // DefaultMountsFilePath: FIXME: this requires global flags to be working!
+ Compression: compression,
+ ConfigureNetwork: networkPolicy,
+ ContextDirectory: contextDir,
+ DefaultMountsFilePath: containerConfig.Containers.DefaultMountsFile,
Devices: flags.Devices,
DropCapabilities: flags.CapDrop,
Err: stderr,
diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go
index 964c7a975..af40dd73a 100644
--- a/cmd/podman/images/history.go
+++ b/cmd/podman/images/history.go
@@ -162,7 +162,7 @@ func (h historyReporter) Size() string {
}
func (h historyReporter) CreatedBy() string {
- if len(h.ImageHistoryLayer.CreatedBy) > 45 {
+ if !opts.noTrunc && len(h.ImageHistoryLayer.CreatedBy) > 45 {
return h.ImageHistoryLayer.CreatedBy[:45-3] + "..."
}
return h.ImageHistoryLayer.CreatedBy
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index 2d881a906..fe92baebe 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -84,20 +84,20 @@ func pullFlags(cmd *cobra.Command) {
flags.StringVar(&pullOptions.CredentialsCLI, credsFlagName, "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
_ = cmd.RegisterFlagCompletionFunc(credsFlagName, completion.AutocompleteNone)
- overrideArchFlagName := "override-arch"
- flags.StringVar(&pullOptions.OverrideArch, overrideArchFlagName, "", "Use `ARCH` instead of the architecture of the machine for choosing images")
- _ = cmd.RegisterFlagCompletionFunc(overrideArchFlagName, completion.AutocompleteNone)
+ archFlagName := "arch"
+ flags.StringVar(&pullOptions.Arch, archFlagName, "", "Use `ARCH` instead of the architecture of the machine for choosing images")
+ _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch)
- overrideOsFlagName := "override-os"
- flags.StringVar(&pullOptions.OverrideOS, overrideOsFlagName, "", "Use `OS` instead of the running OS for choosing images")
- _ = cmd.RegisterFlagCompletionFunc(overrideOsFlagName, completion.AutocompleteNone)
+ osFlagName := "os"
+ flags.StringVar(&pullOptions.OS, osFlagName, "", "Use `OS` instead of the running OS for choosing images")
+ _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS)
- overrideVariantFlagName := "override-variant"
- flags.StringVar(&pullOptions.OverrideVariant, overrideVariantFlagName, "", " use VARIANT instead of the running architecture variant for choosing images")
- _ = cmd.RegisterFlagCompletionFunc(overrideVariantFlagName, completion.AutocompleteNone)
+ variantFlagName := "variant"
+ flags.StringVar(&pullOptions.Variant, variantFlagName, "", " use VARIANT instead of the running architecture variant for choosing images")
+ _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
platformFlagName := "platform"
- flags.String(platformFlagName, "", "Specify the platform for selecting the image. (Conflicts with override-arch and override-os)")
+ flags.String(platformFlagName, "", "Specify the platform for selecting the image. (Conflicts with arch and os)")
_ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone)
flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP")
@@ -138,13 +138,13 @@ func imagePull(cmd *cobra.Command, args []string) error {
return err
}
if platform != "" {
- if pullOptions.OverrideArch != "" || pullOptions.OverrideOS != "" {
- return errors.Errorf("--platform option can not be specified with --override-arch or --override-os")
+ if pullOptions.Arch != "" || pullOptions.OS != "" {
+ return errors.Errorf("--platform option can not be specified with --arch or --os")
}
split := strings.SplitN(platform, "/", 2)
- pullOptions.OverrideOS = split[0]
+ pullOptions.OS = split[0]
if len(split) > 1 {
- pullOptions.OverrideArch = split[1]
+ pullOptions.Arch = split[1]
}
}
diff --git a/cmd/podman/manifest/add.go b/cmd/podman/manifest/add.go
index cb0838eeb..b33f01c10 100644
--- a/cmd/podman/manifest/add.go
+++ b/cmd/podman/manifest/add.go
@@ -52,7 +52,7 @@ func init() {
archFlagName := "arch"
flags.StringVar(&manifestAddOpts.Arch, archFlagName, "", "override the `architecture` of the specified image")
- _ = addCmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteNone)
+ _ = addCmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch)
authfileFlagName := "authfile"
flags.StringVar(&manifestAddOpts.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
@@ -72,7 +72,7 @@ func init() {
osFlagName := "os"
flags.StringVar(&manifestAddOpts.OS, osFlagName, "", "override the `OS` of the specified image")
- _ = addCmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteNone)
+ _ = addCmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS)
osVersionFlagName := "os-version"
flags.StringVar(&manifestAddOpts.OSVersion, osVersionFlagName, "", "override the OS `version` of the specified image")
diff --git a/cmd/podman/manifest/annotate.go b/cmd/podman/manifest/annotate.go
index 71017e0ec..7c4f5ad01 100644
--- a/cmd/podman/manifest/annotate.go
+++ b/cmd/podman/manifest/annotate.go
@@ -39,7 +39,7 @@ func init() {
archFlagName := "arch"
flags.StringVar(&manifestAnnotateOpts.Arch, archFlagName, "", "override the `architecture` of the specified image")
- _ = annotateCmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteNone)
+ _ = annotateCmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch)
featuresFlagName := "features"
flags.StringSliceVar(&manifestAnnotateOpts.Features, featuresFlagName, nil, "override the `features` of the specified image")
@@ -47,7 +47,7 @@ func init() {
osFlagName := "os"
flags.StringVar(&manifestAnnotateOpts.OS, osFlagName, "", "override the `OS` of the specified image")
- _ = annotateCmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteNone)
+ _ = annotateCmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS)
osFeaturesFlagName := "os-features"
flags.StringSliceVar(&manifestAnnotateOpts.OSFeatures, osFeaturesFlagName, nil, "override the OS `features` of the specified image")
diff --git a/cmd/podman/utils/alias.go b/cmd/podman/utils/alias.go
index 469233b59..8d089920b 100644
--- a/cmd/podman/utils/alias.go
+++ b/cmd/podman/utils/alias.go
@@ -25,6 +25,12 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
name = "external"
case "purge":
name = "rm"
+ case "override-arch":
+ name = "arch"
+ case "override-os":
+ name = "os"
+ case "override-variant":
+ name = "variant"
}
return pflag.NormalizedName(name)
}
diff --git a/completions/fish/podman-remote.fish b/completions/fish/podman-remote.fish
index e0216844d..b3a4a173a 100644
--- a/completions/fish/podman-remote.fish
+++ b/completions/fish/podman-remote.fish
@@ -10,7 +10,7 @@ end
function __podman_remote_perform_completion
__podman_remote_debug "Starting __podman_remote_perform_completion"
- set args (string split -- " " (commandline -c))
+ set args (string split -- " " (string trim -l (commandline -c)))
set lastArg "$args[-1]"
__podman_remote_debug "args: $args"
@@ -23,16 +23,13 @@ function __podman_remote_perform_completion
end
__podman_remote_debug "emptyArg: $emptyArg"
- if not type -q "$args[1]"
- # This can happen when "complete --do-complete podman-remote" is called when running this script.
- __podman_remote_debug "Cannot find $args[1]. No completions."
- return
- end
-
set requestComp "$args[1] __complete $args[2..-1] $emptyArg"
__podman_remote_debug "Calling $requestComp"
- set results (eval $requestComp 2> /dev/null)
+ # Call the command as a sub-shell so that we can redirect any errors
+ # For example, if $requestComp has an unmatched quote
+ # https://github.com/spf13/cobra/issues/1214
+ set results (fish -c "$requestComp" 2> /dev/null)
set comps $results[1..-2]
set directiveLine $results[-1]
@@ -81,8 +78,6 @@ function __podman_remote_prepare_completions
set shellCompDirectiveNoFileComp 4
set shellCompDirectiveFilterFileExt 8
set shellCompDirectiveFilterDirs 16
- set shellCompDirectiveLegacyCustomComp 32
- set shellCompDirectiveLegacyCustomArgsComp 64
if test -z "$directive"
set directive 0
@@ -95,15 +90,6 @@ function __podman_remote_prepare_completions
return 1
end
- set legacyCustom (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomComp) % 2)
- set legacyCustomArgs (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomArgsComp) % 2)
- if test $legacyCustom -eq 1; or test $legacyCustomArgs -eq 1
- __podman_remote_debug "Legacy bash custom completion not applicable to fish"
- # Do full file completion instead
- set --global __podman_remote_comp_do_file_comp 1
- return 1
- end
-
set filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2)
set dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2)
if test $filefilter -eq 1; or test $dirfilter -eq 1
@@ -121,14 +107,14 @@ function __podman_remote_prepare_completions
# we need to count the number of valid completions.
# To do so, we will filter on prefix as the completions we have received
# may not already be filtered so as to allow fish to match on different
- # criteria than prefix.
+ # criteria than the prefix.
if test $nospace -ne 0; or test $nofiles -eq 0
set prefix (commandline -t)
__podman_remote_debug "prefix: $prefix"
set completions
for comp in $__podman_remote_comp_results
- if test (string match -e -r "^$prefix" "$comp")
+ if test (string match -e -r -- "^$prefix" "$comp")
set -a completions $comp
end
end
@@ -166,7 +152,7 @@ end
# Since Fish completions are only loaded once the user triggers them, we trigger them ourselves
# so we can properly delete any completions provided by another script.
-# The space after the the program name is essential to trigger completion for the program
+# The space after the program name is essential to trigger completion for the program
# and not completion of the program name itself.
complete --do-complete "podman-remote " > /dev/null 2>&1
# Using '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
diff --git a/completions/fish/podman.fish b/completions/fish/podman.fish
index 2a89e7118..05dd2d72b 100644
--- a/completions/fish/podman.fish
+++ b/completions/fish/podman.fish
@@ -10,7 +10,7 @@ end
function __podman_perform_completion
__podman_debug "Starting __podman_perform_completion"
- set args (string split -- " " (commandline -c))
+ set args (string split -- " " (string trim -l (commandline -c)))
set lastArg "$args[-1]"
__podman_debug "args: $args"
@@ -23,16 +23,13 @@ function __podman_perform_completion
end
__podman_debug "emptyArg: $emptyArg"
- if not type -q "$args[1]"
- # This can happen when "complete --do-complete podman" is called when running this script.
- __podman_debug "Cannot find $args[1]. No completions."
- return
- end
-
set requestComp "$args[1] __complete $args[2..-1] $emptyArg"
__podman_debug "Calling $requestComp"
- set results (eval $requestComp 2> /dev/null)
+ # Call the command as a sub-shell so that we can redirect any errors
+ # For example, if $requestComp has an unmatched quote
+ # https://github.com/spf13/cobra/issues/1214
+ set results (fish -c "$requestComp" 2> /dev/null)
set comps $results[1..-2]
set directiveLine $results[-1]
@@ -81,8 +78,6 @@ function __podman_prepare_completions
set shellCompDirectiveNoFileComp 4
set shellCompDirectiveFilterFileExt 8
set shellCompDirectiveFilterDirs 16
- set shellCompDirectiveLegacyCustomComp 32
- set shellCompDirectiveLegacyCustomArgsComp 64
if test -z "$directive"
set directive 0
@@ -95,15 +90,6 @@ function __podman_prepare_completions
return 1
end
- set legacyCustom (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomComp) % 2)
- set legacyCustomArgs (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomArgsComp) % 2)
- if test $legacyCustom -eq 1; or test $legacyCustomArgs -eq 1
- __podman_debug "Legacy bash custom completion not applicable to fish"
- # Do full file completion instead
- set --global __podman_comp_do_file_comp 1
- return 1
- end
-
set filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2)
set dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2)
if test $filefilter -eq 1; or test $dirfilter -eq 1
@@ -121,14 +107,14 @@ function __podman_prepare_completions
# we need to count the number of valid completions.
# To do so, we will filter on prefix as the completions we have received
# may not already be filtered so as to allow fish to match on different
- # criteria than prefix.
+ # criteria than the prefix.
if test $nospace -ne 0; or test $nofiles -eq 0
set prefix (commandline -t)
__podman_debug "prefix: $prefix"
set completions
for comp in $__podman_comp_results
- if test (string match -e -r "^$prefix" "$comp")
+ if test (string match -e -r -- "^$prefix" "$comp")
set -a completions $comp
end
end
@@ -166,7 +152,7 @@ end
# Since Fish completions are only loaded once the user triggers them, we trigger them ourselves
# so we can properly delete any completions provided by another script.
-# The space after the the program name is essential to trigger completion for the program
+# The space after the program name is essential to trigger completion for the program
# and not completion of the program name itself.
complete --do-complete "podman " > /dev/null 2>&1
# Using '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index 915c70045..3292cea84 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -130,20 +130,70 @@ exec_container() {
}
function _run_swagger() {
+ local upload_filename
+ local upload_bucket
local download_url
+ local envvarsfile
+ req_env_vars GCPJSON GCPNAME GCPPROJECT CTR_FQIN
+
# Building this is a PITA, just grab binary for use in automation
# Ref: https://goswagger.io/install.html#static-binary
download_url=$(\
curl -s https://api.github.com/repos/go-swagger/go-swagger/releases/latest | \
jq -r '.assets[] | select(.name | contains("linux_amd64")) | .browser_download_url')
- curl -o /usr/local/bin/swagger -L'#' "$download_url"
+
+ # The filename and bucket depend on the automation context
+ #shellcheck disable=SC2154,SC2153
+ if [[ -n "$CIRRUS_PR" ]]; then
+ upload_bucket="libpod-pr-releases"
+ upload_filename="swagger-pr$CIRRUS_PR.yaml"
+ elif [[ -n "$CIRRUS_TAG" ]]; then
+ upload_bucket="libpod-master-releases"
+ upload_filename="swagger-$CIRRUS_TAG.yaml"
+ elif [[ "$CIRRUS_BRANCH" == "master" ]]; then
+ upload_bucket="libpod-master-releases"
+ # readthedocs versioning uses "latest" for "master" (default) branch
+ upload_filename="swagger-latest.yaml"
+ elif [[ -n "$CIRRUS_BRANCH" ]]; then
+ upload_bucket="libpod-master-releases"
+ upload_filename="swagger-$CIRRUS_BRANCH.yaml"
+ else
+ die "Unknown execution context, expected a non-empty value for \$CIRRUS_TAG, \$CIRRUS_BRANCH, or \$CIRRUS_PR"
+ fi
+
+ curl -s -o /usr/local/bin/swagger -L'#' "$download_url"
chmod +x /usr/local/bin/swagger
+ # Swagger validation takes a significant amount of time
+ msg "Pulling \$CTR_FQIN '$CTR_FQIN' (background process)"
+ podman pull --quiet $CTR_FQIN &
+
cd $GOSRC
make swagger
# Cirrus-CI Artifact instruction expects file here
- cp -v $GOSRC/pkg/api/swagger.yaml $GOSRC/
+ cp -v $GOSRC/pkg/api/swagger.yaml ./
+
+ envvarsfile=$(mktemp -p '' .tmp_$(basename $0)_XXXXXXXX)
+ trap "rm -f $envvarsfile" EXIT # contains secrets
+ # Warning: These values must _not_ be quoted, podman will not remove them.
+ #shellcheck disable=SC2154
+ cat <<eof>>$envvarsfile
+GCPJSON=$GCPJSON
+GCPNAME=$GCPNAME
+GCPPROJECT=$GCPPROJECT
+FROM_FILEPATH=$GOSRC/swagger.yaml
+TO_GCSURI=gs://$upload_bucket/$upload_filename
+eof
+
+ msg "Waiting for backgrounded podman pull to complete..."
+ wait %%
+ podman run -it --rm --security-opt label=disable \
+ --env-file=$envvarsfile \
+ -v $GOSRC:$GOSRC:ro \
+ --workdir $GOSRC \
+ $CTR_FQIN
+ rm -f $envvarsfile
}
function _run_consistency() {
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index 7b49caba0..9267b8a1c 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -202,7 +202,6 @@ case "$TEST_FLAVOR" in
int) ;&
sys) ;&
bindings) ;&
- swagger) ;&
endpoint)
# Use existing host bits when testing is to happen inside a container
# since this script will run again in that environment.
@@ -214,6 +213,7 @@ case "$TEST_FLAVOR" in
install_test_configs
;;
+ swagger) ;& # use next item
consistency) make clean ;;
release) ;;
*) die_unknown TEST_FLAVOR
diff --git a/docs/source/Introduction.rst b/docs/source/Introduction.rst
index 9dcae8a83..9fdce6962 100644
--- a/docs/source/Introduction.rst
+++ b/docs/source/Introduction.rst
@@ -34,7 +34,7 @@ Sometimes we can find a publicly available container image for the exact workloa
Container Images aren’t actually images, they’re repositories often made up of multiple layers. These layers can easily be added, saved, and shared with others by using a Containerfile (Dockerfile). This single file often contains all the instructions needed to build the new and can easily be shared with others publicly using tools like GitHub.
-Here's an example of how to build an Nginx web server on top of a Debian base image using the Dockerfile maintained by Nginx and published in GitHub::
+Here's an example of how to build a Nginx web server on top of a Debian base image using the Dockerfile maintained by Nginx and published in GitHub::
podman build -t nginx https://git.io/Jf8ol
@@ -62,7 +62,7 @@ Input::
Password: ********
Login Succeeded!
-Nex, tag the image so that we can push it into our user account::
+Next, tag the image so that we can push it into our user account::
podman tag localhost/nginx quay.io/USERNAME/nginx
diff --git a/docs/source/_static/api.html b/docs/source/_static/api.html
index 11fbb85a0..fbc945d87 100644
--- a/docs/source/_static/api.html
+++ b/docs/source/_static/api.html
@@ -18,7 +18,7 @@
</style>
</head>
<body>
- <redoc spec-url='https://storage.googleapis.com/libpod-master-releases/swagger-latest-master.yaml' sort-props-alphabetically></redoc>
+ <redoc spec-url='https://storage.googleapis.com/libpod-master-releases/swagger-latest.yaml' sort-props-alphabetically></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 9a1381080..3f3fa10d5 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -6,7 +6,9 @@ Podman_ is a daemonless, open source, Linux native tool designed to make it easy
Containers under the control of Podman can either be run by root or by a non-privileged user. Podman manages the entire container ecosystem which includes pods, containers, container images, and container volumes using the libpod_ library. Podman specializes in all of the commands and functions that help you to maintain and modify OCI container images, such as pulling and tagging. It allows you to create, run, and maintain those containers and container images in a production environment.
-The Podman service runs only on Linux platforms, however a REST API and clients are currently under development which will allow Mac and Windows platforms to call the service. There is currently a RESTful based remote client which runs on Mac or Windows platforms that allows the remote client to talk to the Podman server on a Linux platform. In addition to those clients, there is also a Mac client.
+There is a RESTFul API to manage containers. We also have a remote Podman client that can interact with
+the RESTFul service. We currently support clients on Linux, Mac, and Windows. The RESTFul service is only
+supported on Linux.
If you are completely new to containers, we recommend that you check out the :doc:`Introduction`. For power users or those coming from Docker, check out our :doc:`Tutorials`. For advanced users and contributors, you can get very detailed information about the Podman CLI by looking at our :doc:`Commands` page. Finally, for Developers looking at how to interact with the Podman API, please see our API documentation :doc:`Reference`.
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 35d8474a6..b3ac8b6ee 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -77,6 +77,9 @@ option can be set multiple times.
Add an annotation to the container. The format is key=value.
The **--annotation** option can be set multiple times.
+#### **--arch**=*ARCH*
+Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`.
+
#### **--attach**, **-a**=*location*
Attach to STDIN, STDOUT or STDERR.
@@ -667,15 +670,9 @@ Whether to disable OOM Killer for the container or not.
Tune the host's OOM preferences for containers (accepts -1000 to 1000)
-#### **--override-arch**=*ARCH*
-Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`.
-
-#### **--override-os**=*OS*
+#### **--os**=*OS*
Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`.
-#### **--override-variant**=*VARIANT*
-Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7.
-
#### **--pid**=*pid*
Set the PID mode for the container
@@ -691,7 +688,7 @@ Tune the container's pids limit. Set `0` to have unlimited pids for the containe
#### **--platform**=*OS/ARCH*
-Specify the platform for selecting the image. (Conflicts with override-arch and override-os)
+Specify the platform for selecting the image. (Conflicts with --arch and --os)
The `--platform` option can be used to override the current architecture and operating system.
#### **--pod**=*name*
@@ -1007,6 +1004,9 @@ Set the UTS namespace mode for the container. The following values are supported
- **ns:[path]**: run the container in the given existing UTS namespace.
- **container:[container]**: join the UTS namespace of the specified container.
+#### **--variant**=*VARIANT*
+Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7.
+
#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman
diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md
index 16e4e3bdb..2fafd1e31 100644
--- a/docs/source/markdown/podman-network-create.1.md
+++ b/docs/source/markdown/podman-network-create.1.md
@@ -41,7 +41,8 @@ Define a gateway for the subnet. If you want to provide a gateway address, you m
#### **--internal**
-Restrict external access of this network
+Restrict external access of this network. Note when using this option, the dnsname plugin will be
+automatically disabled.
#### **--ip-range**
diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md
index 44a7e83b6..af91a59e9 100644
--- a/docs/source/markdown/podman-pull.1.md
+++ b/docs/source/markdown/podman-pull.1.md
@@ -71,6 +71,9 @@ All tagged images in the repository will be pulled.
Note: When using the all-tags flag, Podman will not iterate over the search registries in the containers-registries.conf(5) but will always use docker.io for unqualified image names.
+#### **--arch**=*ARCH*
+Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`.
+
#### **--authfile**=*path*
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`.
@@ -96,19 +99,16 @@ This is a Docker specific option to disable image verification to a Docker
registry and is not supported by Podman. This flag is a NOOP and provided
solely for scripting compatibility.
-#### **--override-arch**=*ARCH*
-Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`.
-
-#### **--override-os**=*OS*
-Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`.
+#### **--help**, **-h**
-#### **--override-variant**=*VARIANT*
+Print usage statement
-Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7.
+#### **--os**=*OS*
+Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`.
#### **--platform**=*OS/ARCH*
-Specify the platform for selecting the image. (Conflicts with override-arch and override-os)
+Specify the platform for selecting the image. (Conflicts with --arch and --os)
The `--platform` option can be used to override the current architecture and operating system.
#### **--quiet**, **-q**
@@ -121,9 +121,9 @@ Require HTTPS and verify certificates when contacting registries (default: true)
then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified,
TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf.
-#### **--help**, **-h**
+#### **--variant**=*VARIANT*
-Print usage statement
+Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7.
## EXAMPLES
@@ -189,7 +189,7 @@ Storing signatures
```
```
-$ podman pull --override-arch=arm arm32v7/debian:stretch
+$ podman pull --arch=arm arm32v7/debian:stretch
Trying to pull docker.io/arm32v7/debian:stretch...
Getting image source signatures
Copying blob b531ae4a3925 done
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 89f05c308..e606c456f 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -93,6 +93,9 @@ This option can be set multiple times.
Add an annotation to the container.
This option can be set multiple times.
+#### **--arch**=*ARCH*
+Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`.
+
#### **--attach**, **-a**=**stdin**|**stdout**|**stderr**
Attach to STDIN, STDOUT or STDERR.
@@ -704,15 +707,9 @@ Whether to disable OOM Killer for the container or not.
Tune the host's OOM preferences for containers (accepts values from **-1000** to **1000**).
-#### **--override-arch**=*ARCH*
-Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`.
-
-#### **--override-os**=*OS*
+#### **--os**=*OS*
Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`.
-#### **--override-variant**=*VARIANT*
-Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7.
-
#### **--pid**=*mode*
Set the PID namespace mode for the container.
@@ -729,7 +726,7 @@ Tune the container's pids limit. Set to **0** to have unlimited pids for the con
#### **--platform**=*OS/ARCH*
-Specify the platform for selecting the image. (Conflicts with override-arch and override-os)
+Specify the platform for selecting the image. (Conflicts with --arch and --os)
The `--platform` option can be used to override the current architecture and operating system.
#### **--pod**=*name*
@@ -1082,6 +1079,9 @@ Set the UTS namespace mode for the container. The following values are supported
- **ns:[path]**: run the container in the given existing UTS namespace.
- **container:[container]**: join the UTS namespace of the specified container.
+#### **--variant**=*VARIANT*
+Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7.
+
#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify _/HOST-DIR_:_/CONTAINER-DIR_, Podman
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 733fadc30..0bb8e387b 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -328,7 +328,7 @@ Currently the slirp4netns package is required to be installed to create a networ
### **NOTE:** Unsupported file systems in rootless mode
-The Overlay file system (OverlayFS) is not supported in rootless mode. The fuse-overlayfs package is a tool that provides the functionality of OverlayFS in user namespace that allows mounting file systems in rootless environments. It is recommended to install the fuse-overlayfs package and to enable it by adding `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in the `$HOME/.config/containers/storage.conf` file.
+The Overlay file system (OverlayFS) is not supported in rootless mode. The fuse-overlayfs package is a tool that provides the functionality of OverlayFS in user namespace that allows mounting file systems in rootless environments. It is recommended to install the fuse-overlayfs package. In rootless mode Podman will automatically use the fuse-overlafs program as the mount_program if installed, as long as the $HOME/.config/containers/storage.conf file was not previously created. If storage.conf exists in the homedir, add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options.overlay]` to enable this feature.
The Network File System (NFS) and other distributed file systems (for example: Lustre, Spectrum Scale, the General Parallel File System (GPFS)) are not supported when running in rootless mode as these file systems do not understand user namespace. However, rootless Podman can make use of an NFS Homedir by modifying the `$HOME/.config/containers/storage.conf` to have the `graphroot` option point to a directory stored on local (Non NFS) storage.
diff --git a/go.mod b/go.mod
index d2bcc3e96..1f8ad9ece 100644
--- a/go.mod
+++ b/go.mod
@@ -29,7 +29,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/godbus/dbus/v5 v5.0.3
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
- github.com/google/uuid v1.1.5
+ github.com/google/uuid v1.2.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/hashicorp/go-multierror v1.1.0
diff --git a/go.sum b/go.sum
index 35cb463af..5bbd30ba3 100644
--- a/go.sum
+++ b/go.sum
@@ -263,6 +263,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 0d62a2dd7..951227a4f 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -776,3 +776,32 @@ func (c *Container) ShouldRestart(ctx context.Context) bool {
}
return c.shouldRestart()
}
+
+// ResolvePath resolves the specified path on the root for the container. The
+// root must either be the mounted image of the container or the already
+// mounted container storage.
+//
+// It returns the resolved root and the resolved path. Note that the path may
+// resolve to the container's mount point or to a volume or bind mount.
+func (c *Container) ResolvePath(ctx context.Context, root string, path string) (string, string, error) {
+ logrus.Debugf("Resolving path %q (root %q) on container %s", path, root, c.ID())
+
+ // Minimal sanity checks.
+ if len(root)*len(path) == 0 {
+ return "", "", errors.Wrapf(define.ErrInternal, "ResolvePath: root (%q) and path (%q) must be non empty", root, path)
+ }
+ if _, err := os.Stat(root); err != nil {
+ return "", "", errors.Wrapf(err, "cannot locate root to resolve path on container %s", c.ID())
+ }
+
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return "", "", err
+ }
+ }
+
+ return c.resolvePath(root, path)
+}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index b41a3fa38..6c9489a08 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -174,25 +174,60 @@ func (c *Container) prepare() error {
return err
}
- // Ensure container entrypoint is created (if required)
- if c.config.CreateWorkingDir {
- workdir, err := securejoin.SecureJoin(c.state.Mountpoint, c.WorkingDir())
- if err != nil {
- return errors.Wrapf(err, "error creating path to container %s working dir", c.ID())
- }
- rootUID := c.RootUID()
- rootGID := c.RootGID()
+ // Make sure the workdir exists
+ if err := c.resolveWorkDir(); err != nil {
+ return err
+ }
- if err := os.MkdirAll(workdir, 0755); err != nil {
- if os.IsExist(err) {
- return nil
+ return nil
+}
+
+// resolveWorkDir resolves the container's workdir and, depending on the
+// configuration, will create it, or error out if it does not exist.
+// Note that the container must be mounted before.
+func (c *Container) resolveWorkDir() error {
+ workdir := c.WorkingDir()
+
+ // If the specified workdir is a subdir of a volume or mount,
+ // we don't need to do anything. The runtime is taking care of
+ // that.
+ if isPathOnVolume(c, workdir) || isPathOnBindMount(c, workdir) {
+ logrus.Debugf("Workdir %q resolved to a volume or mount", workdir)
+ return nil
+ }
+
+ _, resolvedWorkdir, err := c.resolvePath(c.state.Mountpoint, workdir)
+ if err != nil {
+ return err
+ }
+ logrus.Debugf("Workdir %q resolved to host path %q", workdir, resolvedWorkdir)
+
+ // No need to create it (e.g., `--workdir=/foo`), so let's make sure
+ // the path exists on the container.
+ if !c.config.CreateWorkingDir {
+ if _, err := os.Stat(resolvedWorkdir); err != nil {
+ if os.IsNotExist(err) {
+ return errors.Errorf("workdir %q does not exist on container %s", workdir, c.ID())
}
- return errors.Wrapf(err, "error creating container %s working dir", c.ID())
+ // This might be a serious error (e.g., permission), so
+ // we need to return the full error.
+ return errors.Wrapf(err, "error detecting workdir %q on container %s", workdir, c.ID())
}
+ }
+
+ // Ensure container entrypoint is created (if required).
+ rootUID := c.RootUID()
+ rootGID := c.RootGID()
- if err := os.Chown(workdir, rootUID, rootGID); err != nil {
- return errors.Wrapf(err, "error chowning container %s working directory to container root", c.ID())
+ if err := os.MkdirAll(resolvedWorkdir, 0755); err != nil {
+ if os.IsExist(err) {
+ return nil
}
+ return errors.Wrapf(err, "error creating container %s workdir", c.ID())
+ }
+
+ if err := os.Chown(resolvedWorkdir, rootUID, rootGID); err != nil {
+ return errors.Wrapf(err, "error chowning container %s workdir to container root", c.ID())
}
return nil
diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go
new file mode 100644
index 000000000..805b3b947
--- /dev/null
+++ b/libpod/container_path_resolution.go
@@ -0,0 +1,166 @@
+package libpod
+
+import (
+ "path/filepath"
+ "strings"
+
+ securejoin "github.com/cyphar/filepath-securejoin"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// resolveContainerPaths resolves the container's mount point and the container
+// path as specified by the user. Both may resolve to paths outside of the
+// container's mount point when the container path hits a volume or bind mount.
+//
+// It returns a bool, indicating whether containerPath resolves outside of
+// mountPoint (e.g., via a mount or volume), the resolved root (e.g., container
+// mount, bind mount or volume) and the resolved path on the root (absolute to
+// the host).
+func (container *Container) resolvePath(mountPoint string, containerPath string) (string, string, error) {
+ // Let's first make sure we have a path relative to the mount point.
+ pathRelativeToContainerMountPoint := containerPath
+ if !filepath.IsAbs(containerPath) {
+ // If the containerPath is not absolute, it's relative to the
+ // container's working dir. To be extra careful, let's first
+ // join the working dir with "/", and the add the containerPath
+ // to it.
+ pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath)
+ }
+ resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint)
+ pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint)
+ pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint)
+
+ // Now we have an "absolute container Path" but not yet resolved on the
+ // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to
+ // check if "/foo/bar/file.txt" is on a volume or bind mount. To do
+ // that, we need to walk *down* the paths to the root. Assuming
+ // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar",
+ // we must select "/foo/bar". Once selected, we need to rebase the
+ // remainder (i.e, "/file.txt") on the volume's mount point on the
+ // host. Same applies to bind mounts.
+
+ searchPath := pathRelativeToContainerMountPoint
+ for {
+ volume, err := findVolume(container, searchPath)
+ if err != nil {
+ return "", "", err
+ }
+ if volume != nil {
+ logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath)
+
+ // TODO: We really need to force the volume to mount
+ // before doing this, but that API is not exposed
+ // externally right now and doing so is beyond the scope
+ // of this commit.
+ mountPoint, err := volume.MountPoint()
+ if err != nil {
+ return "", "", err
+ }
+ if mountPoint == "" {
+ return "", "", errors.Errorf("volume %s is not mounted, cannot copy into it", volume.Name())
+ }
+
+ // We found a matching volume for searchPath. We now
+ // need to first find the relative path of our input
+ // path to the searchPath, and then join it with the
+ // volume's mount point.
+ pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
+ absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(mountPoint, pathRelativeToVolume)
+ if err != nil {
+ return "", "", err
+ }
+ return mountPoint, absolutePathOnTheVolumeMount, nil
+ }
+
+ if mount := findBindMount(container, searchPath); mount != nil {
+ logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath)
+ // We found a matching bind mount for searchPath. We
+ // now need to first find the relative path of our
+ // input path to the searchPath, and then join it with
+ // the source of the bind mount.
+ pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
+ absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount)
+ if err != nil {
+ return "", "", err
+ }
+ return mount.Source, absolutePathOnTheBindMount, nil
+
+ }
+
+ if searchPath == "/" {
+ // Cannot go beyond "/", so we're done.
+ break
+ }
+
+ // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar").
+ searchPath = filepath.Dir(searchPath)
+ }
+
+ // No volume, no bind mount but just a normal path on the container.
+ return mountPoint, resolvedPathOnTheContainerMountPoint, nil
+}
+
+// findVolume checks if the specified containerPath matches the destination
+// path of a Volume. Returns a matching Volume or nil.
+func findVolume(c *Container, containerPath string) (*Volume, error) {
+ runtime := c.Runtime()
+ cleanedContainerPath := filepath.Clean(containerPath)
+ for _, vol := range c.Config().NamedVolumes {
+ if cleanedContainerPath == filepath.Clean(vol.Dest) {
+ return runtime.GetVolume(vol.Name)
+ }
+ }
+ return nil, nil
+}
+
+// isPathOnVolume returns true if the specified containerPath is a subdir of any
+// Volume's destination.
+func isPathOnVolume(c *Container, containerPath string) bool {
+ cleanedContainerPath := filepath.Clean(containerPath)
+ for _, vol := range c.Config().NamedVolumes {
+ if cleanedContainerPath == filepath.Clean(vol.Dest) {
+ return true
+ }
+ for dest := vol.Dest; dest != "/"; dest = filepath.Dir(dest) {
+ if cleanedContainerPath == dest {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// findBindMounts checks if the specified containerPath matches the destination
+// path of a Mount. Returns a matching Mount or nil.
+func findBindMount(c *Container, containerPath string) *specs.Mount {
+ cleanedPath := filepath.Clean(containerPath)
+ for _, m := range c.Config().Spec.Mounts {
+ if m.Type != "bind" {
+ continue
+ }
+ if cleanedPath == filepath.Clean(m.Destination) {
+ mount := m
+ return &mount
+ }
+ }
+ return nil
+}
+
+/// isPathOnBindMount returns true if the specified containerPath is a subdir of any
+// Mount's destination.
+func isPathOnBindMount(c *Container, containerPath string) bool {
+ cleanedContainerPath := filepath.Clean(containerPath)
+ for _, m := range c.Config().Spec.Mounts {
+ if cleanedContainerPath == filepath.Clean(m.Destination) {
+ return true
+ }
+ for dest := m.Destination; dest != "/"; dest = filepath.Dir(dest) {
+ if cleanedContainerPath == dest {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/libpod/image/search.go b/libpod/image/search.go
index 6020fbca9..c5799219a 100644
--- a/libpod/image/search.go
+++ b/libpod/image/search.go
@@ -102,8 +102,8 @@ func SearchImages(term string, options SearchOptions) ([]SearchResult, error) {
searchImageInRegistryHelper := func(index int, registry string) {
defer sem.Release(1)
defer wg.Done()
- searchOutput := searchImageInRegistry(term, registry, options)
- data[index] = searchOutputData{data: searchOutput}
+ searchOutput, err := searchImageInRegistry(term, registry, options)
+ data[index] = searchOutputData{data: searchOutput, err: err}
}
ctx := context.Background()
@@ -116,13 +116,21 @@ func SearchImages(term string, options SearchOptions) ([]SearchResult, error) {
wg.Wait()
results := []SearchResult{}
+ var lastError error
for _, d := range data {
if d.err != nil {
- return nil, d.err
+ if lastError != nil {
+ logrus.Errorf("%v", lastError)
+ }
+ lastError = d.err
+ continue
}
results = append(results, d.data...)
}
- return results, nil
+ if len(results) > 0 {
+ return results, nil
+ }
+ return results, lastError
}
// getRegistries returns the list of registries to search, depending on an optional registry specification
@@ -140,7 +148,7 @@ func getRegistries(registry string) ([]string, error) {
return registries, nil
}
-func searchImageInRegistry(term string, registry string, options SearchOptions) []SearchResult {
+func searchImageInRegistry(term string, registry string, options SearchOptions) ([]SearchResult, error) {
// Max number of queries by default is 25
limit := maxQueries
if options.Limit > 0 {
@@ -156,16 +164,14 @@ func searchImageInRegistry(term string, registry string, options SearchOptions)
if options.ListTags {
results, err := searchRepositoryTags(registry, term, sc, options)
if err != nil {
- logrus.Errorf("error listing registry tags %q: %v", registry, err)
- return []SearchResult{}
+ return []SearchResult{}, err
}
- return results
+ return results, nil
}
results, err := docker.SearchRegistry(context.TODO(), sc, registry, term, limit)
if err != nil {
- logrus.Errorf("error searching registry %q: %v", registry, err)
- return []SearchResult{}
+ return []SearchResult{}, err
}
index := registry
arr := strings.Split(registry, ".")
@@ -219,7 +225,7 @@ func searchImageInRegistry(term string, registry string, options SearchOptions)
}
paramsArr = append(paramsArr, params)
}
- return paramsArr
+ return paramsArr, nil
}
func searchRepositoryTags(registry, term string, sc *types.SystemContext, options SearchOptions) ([]SearchResult, error) {
diff --git a/libpod/network/create.go b/libpod/network/create.go
index e7f65358b..a8f985af9 100644
--- a/libpod/network/create.go
+++ b/libpod/network/create.go
@@ -14,6 +14,7 @@ import (
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// Create the CNI network
@@ -226,8 +227,12 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon
// if we find the dnsname plugin or are rootless, we add configuration for it
// the rootless-cni-infra container has the dnsname plugin always installed
if (HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) || rootless.IsRootless()) && !options.DisableDNS {
- // Note: in the future we might like to allow for dynamic domain names
- plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName))
+ if options.Internal {
+ logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name)
+ } else {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName))
+ }
}
ncList["plugins"] = plugins
b, err := json.MarshalIndent(ncList, "", " ")
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index ef2f034ab..737dbf935 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -977,7 +977,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
if c.state.NetNS == nil {
// We still want to make dummy configurations for each CNI net
// the container joined.
- if len(networks) > 0 && !isDefault {
+ if len(networks) > 0 {
settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks))
for _, net := range networks {
cniNet := new(define.InspectAdditionalNetwork)
@@ -998,7 +998,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
}
// If we have CNI networks - handle that here
- if len(networks) > 0 && !isDefault {
+ if len(networks) > 0 {
if len(networks) != len(c.state.NetworkStatus) {
return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI network(s) %v, but have information on %d network(s)", len(networks), networks, len(c.state.NetworkStatus))
}
@@ -1028,7 +1028,9 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
settings.Networks[name] = addedNet
}
- return settings, nil
+ if !isDefault {
+ return settings, nil
+ }
}
// If not joining networks, we should have at most 1 result
diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index dc5dd03df..faf86ea5b 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -126,20 +126,25 @@ func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, o
}()
attachChan := make(chan error)
+ conmonPipeDataChan := make(chan conmonPipeData)
go func() {
// attachToExec is responsible for closing pipes
- attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen)
+ attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog)
close(attachChan)
}()
- // Wait for conmon to succeed, when return.
- if err := execCmd.Wait(); err != nil {
- return -1, nil, errors.Wrapf(err, "cannot run conmon")
- }
+ // NOTE: the channel is needed to communicate conmon's data. In case
+ // of an error, the error will be written on the hijacked http
+ // connection such that remote clients will receive the error.
+ pipeData := <-conmonPipeDataChan
- pid, err := readConmonPipeData(pipes.syncPipe, ociLog)
+ return pipeData.pid, attachChan, pipeData.err
+}
- return pid, attachChan, err
+// conmonPipeData contains the data when reading from conmon's pipe.
+type conmonPipeData struct {
+ pid int
+ err error
}
// ExecContainerDetached executes a command in a running container, but does
@@ -488,9 +493,16 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex
}
// Attach to a container over HTTP
-func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (deferredErr error) {
+func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string) (deferredErr error) {
+ // NOTE: As you may notice, the attach code is quite complex.
+ // Many things happen concurrently and yet are interdependent.
+ // If you ever change this function, make sure to write to the
+ // conmonPipeDataChan in case of an error.
+
if pipes == nil || pipes.startPipe == nil || pipes.attachPipe == nil {
- return errors.Wrapf(define.ErrInvalidArg, "must provide a start and attach pipe to finish an exec attach")
+ err := errors.Wrapf(define.ErrInvalidArg, "must provide a start and attach pipe to finish an exec attach")
+ conmonPipeDataChan <- conmonPipeData{-1, err}
+ return err
}
defer func() {
@@ -509,17 +521,20 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
// set up the socket path, such that it is the correct length and location for exec
sockPath, err := c.execAttachSocketPath(sessionID)
if err != nil {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
return err
}
// 2: read from attachFd that the parent process has set up the console socket
if _, err := readConmonPipeData(pipes.attachPipe, ""); err != nil {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
return err
}
// 2: then attach
conn, err := openUnixSocket(sockPath)
if err != nil {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath)
}
defer func() {
@@ -540,11 +555,13 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
// Perform hijack
hijacker, ok := w.(http.Hijacker)
if !ok {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
return errors.Errorf("unable to hijack connection")
}
httpCon, httpBuf, err := hijacker.Hijack()
if err != nil {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
return errors.Wrapf(err, "error hijacking connection")
}
@@ -555,10 +572,23 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
// Force a flush after the header is written.
if err := httpBuf.Flush(); err != nil {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
return errors.Wrapf(err, "error flushing HTTP hijack header")
}
go func() {
+ // Wait for conmon to succeed, when return.
+ if err := execCmd.Wait(); err != nil {
+ conmonPipeDataChan <- conmonPipeData{-1, err}
+ } else {
+ pid, err := readConmonPipeData(pipes.syncPipe, ociLog)
+ if err != nil {
+ hijackWriteError(err, c.ID(), isTerminal, httpBuf)
+ conmonPipeDataChan <- conmonPipeData{pid, err}
+ } else {
+ conmonPipeDataChan <- conmonPipeData{pid, err}
+ }
+ }
// We need to hold the connection open until the complete exec
// function has finished. This channel will be closed in a defer
// in that function, so we can wait for it here.
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 34c737a67..0dc220b52 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -180,6 +180,13 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R
}
}
+ if err := shutdown.Register("libpod", func(sig os.Signal) error {
+ os.Exit(1)
+ return nil
+ }); err != nil && errors.Cause(err) != shutdown.ErrHandlerExists {
+ logrus.Errorf("Error registering shutdown handler for libpod: %v", err)
+ }
+
if err := shutdown.Start(); err != nil {
return nil, errors.Wrapf(err, "error starting shutdown signal handler")
}
@@ -188,13 +195,6 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R
return nil, err
}
- if err := shutdown.Register("libpod", func(sig os.Signal) error {
- os.Exit(1)
- return nil
- }); err != nil && errors.Cause(err) != shutdown.ErrHandlerExists {
- logrus.Errorf("Error registering shutdown handler for libpod: %v", err)
- }
-
return runtime, nil
}
diff --git a/libpod/shutdown/handler.go b/libpod/shutdown/handler.go
index f0f228b19..ac1d33910 100644
--- a/libpod/shutdown/handler.go
+++ b/libpod/shutdown/handler.go
@@ -18,6 +18,8 @@ var (
stopped bool
sigChan chan os.Signal
cancelChan chan bool
+ // Syncronize accesses to the map
+ handlerLock sync.Mutex
// Definitions of all on-shutdown handlers
handlers map[string]func(os.Signal) error
// Ordering that on-shutdown handlers will be invoked.
@@ -50,6 +52,7 @@ func Start() error {
case sig := <-sigChan:
logrus.Infof("Received shutdown signal %v, terminating!", sig)
shutdownInhibit.Lock()
+ handlerLock.Lock()
for _, name := range handlerOrder {
handler, ok := handlers[name]
if !ok {
@@ -61,6 +64,7 @@ func Start() error {
logrus.Errorf("Error running shutdown handler %s: %v", name, err)
}
}
+ handlerLock.Unlock()
shutdownInhibit.Unlock()
return
}
@@ -97,6 +101,9 @@ func Uninhibit() {
// by a signal. Handlers are invoked LIFO - the last handler registered is the
// first run.
func Register(name string, handler func(os.Signal) error) error {
+ handlerLock.Lock()
+ defer handlerLock.Unlock()
+
if handlers == nil {
handlers = make(map[string]func(os.Signal) error)
}
@@ -113,6 +120,9 @@ func Register(name string, handler func(os.Signal) error) error {
// Unregister un-registers a given shutdown handler.
func Unregister(name string) error {
+ handlerLock.Lock()
+ defer handlerLock.Unlock()
+
if handlers == nil {
return nil
}
diff --git a/libpod/util.go b/libpod/util.go
index bf9bf2542..391208fb9 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -235,20 +235,16 @@ func checkDependencyContainer(depCtr, ctr *Container) error {
return nil
}
-// hijackWriteErrorAndClose writes an error to a hijacked HTTP session and
-// closes it. Intended to HTTPAttach function.
-// If error is nil, it will not be written; we'll only close the connection.
-func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon io.Closer, httpBuf *bufio.ReadWriter) {
+// hijackWriteError writes an error to a hijacked HTTP session.
+func hijackWriteError(toWrite error, cid string, terminal bool, httpBuf *bufio.ReadWriter) {
if toWrite != nil {
- errString := []byte(fmt.Sprintf("%v\n", toWrite))
+ errString := []byte(fmt.Sprintf("Error: %v\n", toWrite))
if !terminal {
// We need a header.
header := makeHTTPAttachHeader(2, uint32(len(errString)))
if _, err := httpBuf.Write(header); err != nil {
logrus.Errorf("Error writing header for container %s attach connection error: %v", cid, err)
}
- // TODO: May want to return immediately here to avoid
- // writing garbage to the socket?
}
if _, err := httpBuf.Write(errString); err != nil {
logrus.Errorf("Error writing error to container %s HTTP attach connection: %v", cid, err)
@@ -257,6 +253,13 @@ func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon
logrus.Errorf("Error flushing HTTP buffer for container %s HTTP attach connection: %v", cid, err)
}
}
+}
+
+// hijackWriteErrorAndClose writes an error to a hijacked HTTP session and
+// closes it. Intended to HTTPAttach function.
+// If error is nil, it will not be written; we'll only close the connection.
+func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon io.Closer, httpBuf *bufio.ReadWriter) {
+ hijackWriteError(toWrite, cid, terminal, httpBuf)
if err := httpCon.Close(); err != nil {
logrus.Errorf("Error closing container %s HTTP attach connection: %v", cid, err)
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index aa12afc82..0f91a4362 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -73,7 +73,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- if report[0].Err != nil {
+ if len(report) > 0 && report[0].Err != nil {
utils.InternalServerError(w, report[0].Err)
return
}
diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go
index 6808cdad5..885112b31 100644
--- a/pkg/api/handlers/compat/images_search.go
+++ b/pkg/api/handlers/compat/images_search.go
@@ -1,25 +1,30 @@
package compat
import (
+ "fmt"
"net/http"
- "strconv"
"github.com/containers/image/v5/types"
- "github.com/containers/podman/v2/libpod/image"
+ "github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/auth"
- "github.com/docker/docker/api/types/registry"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
func SearchImages(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Term string `json:"term"`
Limit int `json:"limit"`
+ NoTrunc bool `json:"noTrunc"`
Filters map[string][]string `json:"filters"`
TLSVerify bool `json:"tlsVerify"`
+ ListTags bool `json:"listTags"`
}{
// This is where you can override the golang default value for one of fields
}
@@ -29,67 +34,40 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
return
}
- filter := image.SearchFilter{}
- if len(query.Filters) > 0 {
- if len(query.Filters["stars"]) > 0 {
- stars, err := strconv.Atoi(query.Filters["stars"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.Stars = stars
- }
- if len(query.Filters["is-official"]) > 0 {
- isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsOfficial = types.NewOptionalBool(isOfficial)
- }
- if len(query.Filters["is-automated"]) > 0 {
- isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsAutomated = types.NewOptionalBool(isAutomated)
- }
- }
- options := image.SearchOptions{
- Filter: filter,
- Limit: query.Limit,
- }
-
- if _, found := r.URL.Query()["tlsVerify"]; found {
- options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
- }
-
_, authfile, key, err := auth.GetCredentials(r)
if err != nil {
utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
- options.Authfile = authfile
- results, err := image.SearchImages(query.Term, options)
+ filters := []string{}
+ for key, val := range query.Filters {
+ filters = append(filters, fmt.Sprintf("%s=%s", key, val[0]))
+ }
+
+ options := entities.ImageSearchOptions{
+ Authfile: authfile,
+ Limit: query.Limit,
+ NoTrunc: query.NoTrunc,
+ ListTags: query.ListTags,
+ Filters: filters,
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+ ir := abi.ImageEngine{Libpod: runtime}
+ reports, err := ir.Search(r.Context(), query.Term, options)
if err != nil {
- utils.BadRequest(w, "term", query.Term, err)
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
-
- compatResults := make([]registry.SearchResult, 0, len(results))
- for _, result := range results {
- compatResult := registry.SearchResult{
- Name: result.Name,
- Description: result.Description,
- StarCount: result.Stars,
- IsAutomated: result.Automated == "[OK]",
- IsOfficial: result.Official == "[OK]",
+ if !utils.IsLibpodRequest(r) {
+ if len(reports) == 0 {
+ utils.ImageNotFound(w, query.Term, define.ErrNoSuchImage)
+ return
}
- compatResults = append(compatResults, compatResult)
}
- utils.WriteResponse(w, http.StatusOK, compatResults)
+ utils.WriteResponse(w, http.StatusOK, reports)
}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 6b07b1cc5..a0cb1d49e 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/infra/abi"
- "github.com/containers/podman/v2/pkg/ps"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -63,6 +62,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
All bool `schema:"all"`
+ External bool `schema:"external"`
Filters map[string][]string `schema:"filters"`
Last int `schema:"last"` // alias for limit
Limit int `schema:"limit"`
@@ -90,17 +90,22 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
opts := entities.ContainerListOptions{
All: query.All,
+ External: query.External,
Filters: query.Filters,
Last: limit,
- Size: query.Size,
- Sort: "",
Namespace: query.Namespace,
- Pod: true,
- Sync: query.Sync,
+ // Always return Pod, should not be part of the API.
+ // https://github.com/containers/podman/pull/7223
+ Pod: true,
+ Size: query.Size,
+ Sync: query.Sync,
}
- pss, err := ps.GetContainerLists(runtime, opts)
+ pss, err := containerEngine.ContainerList(r.Context(), opts)
if err != nil {
utils.InternalServerError(w, err)
return
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 97cd5a65e..3b531652f 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -589,92 +589,6 @@ func UntagImage(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusCreated, "")
}
-func SearchImages(w http.ResponseWriter, r *http.Request) {
- decoder := r.Context().Value("decoder").(*schema.Decoder)
- query := struct {
- Term string `json:"term"`
- Limit int `json:"limit"`
- NoTrunc bool `json:"noTrunc"`
- Filters map[string][]string `json:"filters"`
- TLSVerify bool `json:"tlsVerify"`
- ListTags bool `json:"listTags"`
- }{
- // This is where you can override the golang default value for one of fields
- }
-
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
- return
- }
-
- filter := image.SearchFilter{}
- if len(query.Filters) > 0 {
- if len(query.Filters["stars"]) > 0 {
- stars, err := strconv.Atoi(query.Filters["stars"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.Stars = stars
- }
- if len(query.Filters["is-official"]) > 0 {
- isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsOfficial = types.NewOptionalBool(isOfficial)
- }
- if len(query.Filters["is-automated"]) > 0 {
- isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0])
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filter.IsAutomated = types.NewOptionalBool(isAutomated)
- }
- }
- options := image.SearchOptions{
- Limit: query.Limit,
- NoTrunc: query.NoTrunc,
- ListTags: query.ListTags,
- Filter: filter,
- }
-
- if _, found := r.URL.Query()["tlsVerify"]; found {
- options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
- }
-
- _, authfile, key, err := auth.GetCredentials(r)
- if err != nil {
- utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String()))
- return
- }
- defer auth.RemoveAuthfile(authfile)
- options.Authfile = authfile
-
- searchResults, err := image.SearchImages(query.Term, options)
- if err != nil {
- utils.BadRequest(w, "term", query.Term, err)
- return
- }
- // Convert from image.SearchResults to entities.ImageSearchReport. We don't
- // want to leak any low-level packages into the remote client, which
- // requires converting.
- reports := make([]entities.ImageSearchReport, len(searchResults))
- for i := range searchResults {
- reports[i].Index = searchResults[i].Index
- reports[i].Name = searchResults[i].Name
- reports[i].Description = searchResults[i].Description
- reports[i].Stars = searchResults[i].Stars
- reports[i].Official = searchResults[i].Official
- reports[i].Automated = searchResults[i].Automated
- reports[i].Tag = searchResults[i].Tag
- }
-
- utils.WriteResponse(w, http.StatusOK, reports)
-}
-
// ImagesBatchRemove is the endpoint for batch image removal.
func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go
index bacba006d..efb15b14d 100644
--- a/pkg/api/handlers/libpod/images_pull.go
+++ b/pkg/api/handlers/libpod/images_pull.go
@@ -30,12 +30,12 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Reference string `schema:"reference"`
- OverrideOS string `schema:"overrideOS"`
- OverrideArch string `schema:"overrideArch"`
- OverrideVariant string `schema:"overrideVariant"`
- TLSVerify bool `schema:"tlsVerify"`
- AllTags bool `schema:"allTags"`
+ Reference string `schema:"reference"`
+ OS string `schema:"OS"`
+ Arch string `schema:"Arch"`
+ Variant string `schema:"Variant"`
+ TLSVerify bool `schema:"tlsVerify"`
+ AllTags bool `schema:"allTags"`
}{
TLSVerify: true,
}
@@ -83,9 +83,9 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
// Setup the registry options
dockerRegistryOptions := image.DockerRegistryOptions{
DockerRegistryCreds: authConf,
- OSChoice: query.OverrideOS,
- ArchitectureChoice: query.OverrideArch,
- VariantChoice: query.OverrideVariant,
+ OSChoice: query.OS,
+ ArchitectureChoice: query.Arch,
+ VariantChoice: query.Variant,
}
if _, found := r.URL.Query()["tlsVerify"]; found {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go
index 22670d795..32f041dd3 100644
--- a/pkg/api/handlers/swagger/swagger.go
+++ b/pkg/api/handlers/swagger/swagger.go
@@ -1,7 +1,6 @@
package swagger
import (
- "github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/api/handlers"
@@ -166,7 +165,7 @@ type swagInspectPodResponse struct {
type swagInspectVolumeResponse struct {
// in:body
Body struct {
- libpod.InspectVolumeData
+ define.InspectVolumeData
}
}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 74a04b2e6..e30747800 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -48,6 +48,11 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// default: false
// description: Return all containers. By default, only running containers are shown
// - in: query
+ // name: external
+ // type: boolean
+ // default: false
+ // description: Return containers in storage not controlled by Podman
+ // - in: query
// name: limit
// description: Return this number of most recently created containers, including non-running ones.
// type: integer
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 8d0c0800b..d76f811e9 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -837,13 +837,16 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// name: url
// description: Load image from the specified URL
// type: string
- // - in: formData
+ // - in: body
// name: upload
- // type: file
// required: true
// description: tarball for imported image
+ // schema:
+ // type: "string"
// produces:
// - application/json
+ // consumes:
+ // - application/x-tar
// responses:
// 200:
// $ref: "#/responses/DocsLibpodImagesImportResponse"
@@ -930,15 +933,15 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: "username:password for the registry"
// type: string
// - in: query
- // name: overrideArch
+ // name: Arch
// description: Pull image for the specified architecture.
// type: string
// - in: query
- // name: overrideOS
+ // name: OS
// description: Pull image for the specified operating system.
// type: string
// - in: query
- // name: overrideVariant
+ // name: Variant
// description: Pull image for the specified variant.
// type: string
// - in: query
@@ -1019,7 +1022,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: "#/responses/DocsSearchResponse"
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(libpod.SearchImages)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet)
// swagger:operation GET /libpod/images/{name:.*}/get libpod libpodExportImage
// ---
// tags:
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 046f6561c..d612041f6 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -179,15 +179,15 @@ func setupSystemd() {
func (s *APIServer) Serve() error {
setupSystemd()
- // Start the shutdown signal handler.
- if err := shutdown.Start(); err != nil {
- return err
- }
if err := shutdown.Register("server", func(sig os.Signal) error {
return s.Shutdown()
}); err != nil {
return err
}
+ // Start the shutdown signal handler.
+ if err := shutdown.Start(); err != nil {
+ return err
+ }
errChan := make(chan error, 1)
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index 24604fa83..3fb1ab733 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -106,6 +106,7 @@ type MountedContainerPathsOptions struct{}
// ListOptions are optional options for listing containers
type ListOptions struct {
All *bool
+ External *bool
Filters map[string][]string
Last *int
Namespace *bool
diff --git a/pkg/bindings/containers/types_list_options.go b/pkg/bindings/containers/types_list_options.go
index 43326fa59..c363dcd32 100644
--- a/pkg/bindings/containers/types_list_options.go
+++ b/pkg/bindings/containers/types_list_options.go
@@ -103,6 +103,22 @@ func (o *ListOptions) GetAll() bool {
return *o.All
}
+// WithExternal
+func (o *ListOptions) WithExternal(value bool) *ListOptions {
+ v := &value
+ o.External = v
+ return o
+}
+
+// GetExternal
+func (o *ListOptions) GetExternal() bool {
+ var external bool
+ if o.External == nil {
+ return external
+ }
+ return *o.External
+}
+
// WithFilters
func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions {
v := value
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
index f216dd073..0248f2fa6 100644
--- a/pkg/bindings/images/types.go
+++ b/pkg/bindings/images/types.go
@@ -171,13 +171,13 @@ type PullOptions struct {
Username *string
// Password for authenticating against the registry.
Password *string
- // OverrideArch will overwrite the local architecture for image pulls.
- OverrideArch *string
- // OverrideOS will overwrite the local operating system (OS) for image
+ // Arch will overwrite the local architecture for image pulls.
+ Arch *string
+ // OS will overwrite the local operating system (OS) for image
// pulls.
- OverrideOS *string
- // OverrideVariant will overwrite the local variant for image pulls.
- OverrideVariant *string
+ OS *string
+ // Variant will overwrite the local variant for image pulls.
+ Variant *string
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet *bool
diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go
index 5163a6341..2bdf2b66e 100644
--- a/pkg/bindings/images/types_pull_options.go
+++ b/pkg/bindings/images/types_pull_options.go
@@ -168,52 +168,52 @@ func (o *PullOptions) GetPassword() string {
return *o.Password
}
-// WithOverrideArch
-func (o *PullOptions) WithOverrideArch(value string) *PullOptions {
+// WithArch
+func (o *PullOptions) WithArch(value string) *PullOptions {
v := &value
- o.OverrideArch = v
+ o.Arch = v
return o
}
-// GetOverrideArch
-func (o *PullOptions) GetOverrideArch() string {
- var overrideArch string
- if o.OverrideArch == nil {
- return overrideArch
+// GetArch
+func (o *PullOptions) GetArch() string {
+ var arch string
+ if o.Arch == nil {
+ return arch
}
- return *o.OverrideArch
+ return *o.Arch
}
-// WithOverrideOS
-func (o *PullOptions) WithOverrideOS(value string) *PullOptions {
+// WithOS
+func (o *PullOptions) WithOS(value string) *PullOptions {
v := &value
- o.OverrideOS = v
+ o.OS = v
return o
}
-// GetOverrideOS
-func (o *PullOptions) GetOverrideOS() string {
- var overrideOS string
- if o.OverrideOS == nil {
- return overrideOS
+// GetOS
+func (o *PullOptions) GetOS() string {
+ var oS string
+ if o.OS == nil {
+ return oS
}
- return *o.OverrideOS
+ return *o.OS
}
-// WithOverrideVariant
-func (o *PullOptions) WithOverrideVariant(value string) *PullOptions {
+// WithVariant
+func (o *PullOptions) WithVariant(value string) *PullOptions {
v := &value
- o.OverrideVariant = v
+ o.Variant = v
return o
}
-// GetOverrideVariant
-func (o *PullOptions) GetOverrideVariant() string {
- var overrideVariant string
- if o.OverrideVariant == nil {
- return overrideVariant
+// GetVariant
+func (o *PullOptions) GetVariant() string {
+ var variant string
+ if o.Variant == nil {
+ return variant
}
- return *o.OverrideVariant
+ return *o.Variant
}
// WithQuiet
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 4c1bd6a7d..2c32f792f 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -297,8 +297,8 @@ type ContainerListOptions struct {
Pod bool
Quiet bool
Size bool
+ External bool
Sort string
- Storage bool
Sync bool
Watch uint
}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 78a7d8aa7..ef40d5490 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -133,13 +133,13 @@ type ImagePullOptions struct {
Username string
// Password for authenticating against the registry.
Password string
- // OverrideArch will overwrite the local architecture for image pulls.
- OverrideArch string
- // OverrideOS will overwrite the local operating system (OS) for image
+ // Arch will overwrite the local architecture for image pulls.
+ Arch string
+ // OS will overwrite the local operating system (OS) for image
// pulls.
- OverrideOS string
- // OverrideVariant will overwrite the local variant for image pulls.
- OverrideVariant string
+ OS string
+ // Variant will overwrite the local variant for image pulls.
+ Variant string
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet bool
diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go
index 809813756..c64dfb02a 100644
--- a/pkg/domain/infra/abi/archive.go
+++ b/pkg/domain/infra/abi/archive.go
@@ -26,13 +26,18 @@ func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrI
return nil, err
}
+ containerMountPoint, err := container.Mount()
+ if err != nil {
+ return nil, err
+ }
+
unmount := func() {
if err := container.Unmount(false); err != nil {
logrus.Errorf("Error unmounting container: %v", err)
}
}
- _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath)
+ _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, containerPath)
if err != nil {
unmount()
return nil, err
@@ -71,6 +76,11 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID
return nil, err
}
+ containerMountPoint, err := container.Mount()
+ if err != nil {
+ return nil, err
+ }
+
unmount := func() {
if err := container.Unmount(false); err != nil {
logrus.Errorf("Error unmounting container: %v", err)
@@ -83,7 +93,7 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID
containerPath = "/."
}
- _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath)
+ _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, containerPath)
if err != nil {
unmount()
return nil, err
diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go
index 931e77026..f3d0799a0 100644
--- a/pkg/domain/infra/abi/containers_stat.go
+++ b/pkg/domain/infra/abi/containers_stat.go
@@ -10,18 +10,11 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/copy"
"github.com/containers/podman/v2/pkg/domain/entities"
- securejoin "github.com/cyphar/filepath-securejoin"
- "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
-func (ic *ContainerEngine) containerStat(container *libpod.Container, containerPath string) (*entities.ContainerStatReport, string, string, error) {
- containerMountPoint, err := container.Mount()
- if err != nil {
- return nil, "", "", err
- }
-
+func (ic *ContainerEngine) containerStat(container *libpod.Container, containerMountPoint string, containerPath string) (*entities.ContainerStatReport, string, string, error) {
// Make sure that "/" copies the *contents* of the mount point and not
// the directory.
if containerPath == "/" {
@@ -30,7 +23,7 @@ func (ic *ContainerEngine) containerStat(container *libpod.Container, containerP
// Now resolve the container's path. It may hit a volume, it may hit a
// bind mount, it may be relative.
- resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath)
+ resolvedRoot, resolvedContainerPath, err := container.ResolvePath(context.Background(), containerMountPoint, containerPath)
if err != nil {
return nil, "", "", err
}
@@ -94,138 +87,21 @@ func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, c
return nil, err
}
+ containerMountPoint, err := container.Mount()
+ if err != nil {
+ return nil, err
+ }
+
defer func() {
if err := container.Unmount(false); err != nil {
logrus.Errorf("Error unmounting container: %v", err)
}
}()
- statReport, _, _, err := ic.containerStat(container, containerPath)
+ statReport, _, _, err := ic.containerStat(container, containerMountPoint, containerPath)
return statReport, err
}
-// resolveContainerPaths resolves the container's mount point and the container
-// path as specified by the user. Both may resolve to paths outside of the
-// container's mount point when the container path hits a volume or bind mount.
-//
-// NOTE: We must take volumes and bind mounts into account as, regrettably, we
-// can copy to/from stopped containers. In that case, the volumes and bind
-// mounts are not present. For running containers, the runtime (e.g., runc or
-// crun) takes care of these mounts. For stopped ones, we need to do quite
-// some dance, as done below.
-func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) {
- // Let's first make sure we have a path relative to the mount point.
- pathRelativeToContainerMountPoint := containerPath
- if !filepath.IsAbs(containerPath) {
- // If the containerPath is not absolute, it's relative to the
- // container's working dir. To be extra careful, let's first
- // join the working dir with "/", and the add the containerPath
- // to it.
- pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath)
- }
- resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint)
- pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint)
- pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint)
-
- // Now we have an "absolute container Path" but not yet resolved on the
- // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to
- // check if "/foo/bar/file.txt" is on a volume or bind mount. To do
- // that, we need to walk *down* the paths to the root. Assuming
- // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar",
- // we must select "/foo/bar". Once selected, we need to rebase the
- // remainder (i.e, "/file.txt") on the volume's mount point on the
- // host. Same applies to bind mounts.
-
- searchPath := pathRelativeToContainerMountPoint
- for {
- volume, err := findVolume(container, searchPath)
- if err != nil {
- return "", "", err
- }
- if volume != nil {
- logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath)
-
- // TODO: We really need to force the volume to mount
- // before doing this, but that API is not exposed
- // externally right now and doing so is beyond the scope
- // of this commit.
- mountPoint, err := volume.MountPoint()
- if err != nil {
- return "", "", err
- }
- if mountPoint == "" {
- return "", "", errors.Errorf("volume %s is not mounted, cannot copy into it", volume.Name())
- }
-
- // We found a matching volume for searchPath. We now
- // need to first find the relative path of our input
- // path to the searchPath, and then join it with the
- // volume's mount point.
- pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
- absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(mountPoint, pathRelativeToVolume)
- if err != nil {
- return "", "", err
- }
- return mountPoint, absolutePathOnTheVolumeMount, nil
- }
-
- if mount := findBindMount(container, searchPath); mount != nil {
- logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath)
- // We found a matching bind mount for searchPath. We
- // now need to first find the relative path of our
- // input path to the searchPath, and then join it with
- // the source of the bind mount.
- pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath)
- absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount)
- if err != nil {
- return "", "", err
- }
- return mount.Source, absolutePathOnTheBindMount, nil
-
- }
-
- if searchPath == "/" {
- // Cannot go beyond "/", so we're done.
- break
- }
-
- // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar").
- searchPath = filepath.Dir(searchPath)
- }
-
- // No volume, no bind mount but just a normal path on the container.
- return mountPoint, resolvedPathOnTheContainerMountPoint, nil
-}
-
-// findVolume checks if the specified container path matches a volume inside
-// the container. It returns a matching volume or nil.
-func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) {
- runtime := c.Runtime()
- cleanedContainerPath := filepath.Clean(containerPath)
- for _, vol := range c.Config().NamedVolumes {
- if cleanedContainerPath == filepath.Clean(vol.Dest) {
- return runtime.GetVolume(vol.Name)
- }
- }
- return nil, nil
-}
-
-// findBindMount checks if the specified container path matches a bind mount
-// inside the container. It returns a matching mount or nil.
-func findBindMount(c *libpod.Container, containerPath string) *specs.Mount {
- cleanedPath := filepath.Clean(containerPath)
- for _, m := range c.Config().Spec.Mounts {
- if m.Type != "bind" {
- continue
- }
- if cleanedPath == filepath.Clean(m.Destination) {
- mount := m
- return &mount
- }
- }
- return nil
-}
-
// secureStat extracts file info for path in a chroot'ed environment in root.
func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) {
var glob string
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 1288ab09b..8ca93e770 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -241,9 +241,9 @@ func pull(ctx context.Context, runtime *image.Runtime, rawImage string, options
dockerRegistryOptions := image.DockerRegistryOptions{
DockerRegistryCreds: registryCreds,
DockerCertPath: options.CertDir,
- OSChoice: options.OverrideOS,
- ArchitectureChoice: options.OverrideArch,
- VariantChoice: options.OverrideVariant,
+ OSChoice: options.OS,
+ ArchitectureChoice: options.Arch,
+ VariantChoice: options.Variant,
DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 524b29553..0c61714c3 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -173,19 +173,32 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st
}
func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) {
- ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds)
- if err != nil {
- return nil, err
- }
// TODO there is no endpoint for container eviction. Need to discuss
- reports := make([]*entities.RmReport, 0, len(ctrs))
- options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes)
- for _, c := range ctrs {
+ options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes).WithIgnore(opts.Ignore)
+
+ if opts.All {
+ ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds)
+ if err != nil {
+ return nil, err
+ }
+ reports := make([]*entities.RmReport, 0, len(ctrs))
+ for _, c := range ctrs {
+ reports = append(reports, &entities.RmReport{
+ Id: c.ID,
+ Err: containers.Remove(ic.ClientCtx, c.ID, options),
+ })
+ }
+ return reports, nil
+ }
+
+ reports := make([]*entities.RmReport, 0, len(namesOrIds))
+ for _, name := range namesOrIds {
reports = append(reports, &entities.RmReport{
- Id: c.ID,
- Err: containers.Remove(ic.ClientCtx, c.ID, options),
+ Id: name,
+ Err: containers.Remove(ic.ClientCtx, name, options),
})
}
+
return reports, nil
}
@@ -601,7 +614,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
func (ic *ContainerEngine) ContainerList(ctx context.Context, opts entities.ContainerListOptions) ([]entities.ListContainer, error) {
options := new(containers.ListOptions).WithFilters(opts.Filters).WithAll(opts.All).WithLast(opts.Last)
- options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync)
+ options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync).WithExternal(opts.External)
return containers.List(ic.ClientCtx, options)
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 2d686b2aa..0de756756 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -106,8 +106,8 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) {
options := new(images.PullOptions)
- options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithOverrideArch(opts.OverrideArch).WithOverrideOS(opts.OverrideOS)
- options.WithOverrideVariant(opts.OverrideVariant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy)
+ options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithArch(opts.Arch).WithOS(opts.OS)
+ options.WithVariant(opts.Variant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
if s == types.OptionalBoolTrue {
options.WithSkipTLSVerify(true)
diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go
index dc577890a..42f9e1d39 100644
--- a/pkg/ps/ps.go
+++ b/pkg/ps/ps.go
@@ -69,7 +69,7 @@ func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOp
pss = append(pss, listCon)
}
- if options.All && options.Storage {
+ if options.All && options.External {
externCons, err := runtime.StorageContainers()
if err != nil {
return nil, err
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index cc3f7928c..31d317bf8 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -203,20 +203,6 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
}
s.Annotations = annotations
- // workdir
- if s.WorkDir == "" {
- if newImage != nil {
- workingDir, err := newImage.WorkingDir(ctx)
- if err != nil {
- return nil, err
- }
- s.WorkDir = workingDir
- }
- }
- if s.WorkDir == "" {
- s.WorkDir = "/"
- }
-
if len(s.SeccompProfilePath) < 1 {
p, err := libpod.DefaultSeccompPath()
if err != nil {
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 4f36744ca..1bc050b00 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -272,10 +272,18 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.Entrypoint != nil {
options = append(options, libpod.WithEntrypoint(s.Entrypoint))
}
- // If the user did not set an workdir but the image did, ensure it is
- // created.
+ // If the user did not specify a workdir on the CLI, let's extract it
+ // from the image.
if s.WorkDir == "" && img != nil {
options = append(options, libpod.WithCreateWorkingDir())
+ wd, err := img.WorkingDir(ctx)
+ if err != nil {
+ return nil, err
+ }
+ s.WorkDir = wd
+ }
+ if s.WorkDir == "" {
+ s.WorkDir = "/"
}
if s.StopSignal != nil {
options = append(options, libpod.WithStopSignal(*s.StopSignal))
diff --git a/pkg/terminal/console_windows.go b/pkg/terminal/console_windows.go
index c7691857c..08e66cb3a 100644
--- a/pkg/terminal/console_windows.go
+++ b/pkg/terminal/console_windows.go
@@ -30,7 +30,7 @@ func setConsoleMode(handle windows.Handle, flags uint32) error {
if err := windows.SetConsoleMode(handle, mode|flags); err != nil {
// In similar code, it is not considered an error if we cannot set the
// console mode. Following same line of thinking here.
- logrus.WithError(err).Error("Failed to set console mode for cli")
+ logrus.WithError(err).Debug("Failed to set console mode for cli")
}
return nil
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index decdc4754..0da196e46 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -237,3 +237,12 @@ t GET containers/$cid/json 200 \
t DELETE containers/$cid 204
t DELETE images/${MultiTagName}?force=true 200
# vim: filetype=sh
+
+# Test Volumes field adds an anonymous volume
+t POST containers/create '"Image":"'$IMAGE'","Volumes":{"/test":{}}' 201 \
+ .Id~[0-9a-f]\\{64\\}
+cid=$(jq -r '.Id' <<<"$output")
+t GET containers/$cid/json 200 \
+ .Mounts[0].Destination="/test"
+
+t DELETE containers/$cid?v=true 204
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 2668b1e7b..781bbb6d2 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -10,6 +10,7 @@ import (
"sort"
"strconv"
"strings"
+ "sync"
"testing"
"time"
@@ -84,6 +85,7 @@ type testResultsSortedLength struct{ testResultsSorted }
func (a testResultsSorted) Less(i, j int) bool { return a[i].length < a[j].length }
var testResults []testResult
+var testResultsMutex sync.Mutex
func TestMain(m *testing.M) {
if reexec.Init() {
@@ -349,7 +351,9 @@ func (p *PodmanTestIntegration) InspectContainer(name string) []define.InspectCo
func processTestResult(f GinkgoTestDescription) {
tr := testResult{length: f.Duration.Seconds(), name: f.TestText}
+ testResultsMutex.Lock()
testResults = append(testResults, tr)
+ testResultsMutex.Unlock()
}
func GetPortLock(port string) storage.Locker {
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index a4931ff2d..73d92e5a0 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -282,7 +282,7 @@ var _ = Describe("Podman create", func() {
})
It("podman create using image list by tag", func() {
- session := podmanTest.Podman([]string{"create", "--pull=always", "--override-arch=arm64", "--name=foo", ALPINELISTTAG})
+ session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINELISTTAG})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To((Equal(0)))
session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"})
@@ -296,7 +296,7 @@ var _ = Describe("Podman create", func() {
})
It("podman create using image list by digest", func() {
- session := podmanTest.Podman([]string{"create", "--pull=always", "--override-arch=arm64", "--name=foo", ALPINELISTDIGEST})
+ session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINELISTDIGEST})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To((Equal(0)))
session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"})
@@ -310,7 +310,7 @@ var _ = Describe("Podman create", func() {
})
It("podman create using image list instance by digest", func() {
- session := podmanTest.Podman([]string{"create", "--pull=always", "--override-arch=arm64", "--name=foo", ALPINEARM64DIGEST})
+ session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINEARM64DIGEST})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To((Equal(0)))
session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"})
@@ -324,7 +324,7 @@ var _ = Describe("Podman create", func() {
})
It("podman create using cross-arch image list instance by digest", func() {
- session := podmanTest.Podman([]string{"create", "--pull=always", "--override-arch=arm64", "--name=foo", ALPINEARM64DIGEST})
+ session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINEARM64DIGEST})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To((Equal(0)))
session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"})
@@ -652,10 +652,10 @@ var _ = Describe("Podman create", func() {
expectedError := "no image found in manifest list for architecture bogus"
Expect(session.ErrorToString()).To(ContainSubstring(expectedError))
- session = podmanTest.Podman([]string{"create", "--platform=linux/arm64", "--override-os", "windows", ALPINE})
+ session = podmanTest.Podman([]string{"create", "--platform=linux/arm64", "--os", "windows", ALPINE})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(125))
- expectedError = "--platform option can not be specified with --override-arch or --override-os"
+ expectedError = "--platform option can not be specified with --arch or --os"
Expect(session.ErrorToString()).To(ContainSubstring(expectedError))
session = podmanTest.Podman([]string{"create", "-q", "--platform=linux/arm64", ALPINE})
diff --git a/test/e2e/history_test.go b/test/e2e/history_test.go
index fea3f4d43..1c57c60de 100644
--- a/test/e2e/history_test.go
+++ b/test/e2e/history_test.go
@@ -65,6 +65,23 @@ var _ = Describe("Podman history", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0))
+
+ session = podmanTest.Podman([]string{"history", "--no-trunc", "--format", "{{.ID}}", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ lines := session.OutputToStringArray()
+ Expect(len(lines)).To(BeNumerically(">", 0))
+ // the image id must be 64 chars long
+ Expect(len(lines[0])).To(BeNumerically("==", 64))
+
+ session = podmanTest.Podman([]string{"history", "--no-trunc", "--format", "{{.CreatedBy}}", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ lines = session.OutputToStringArray()
+ Expect(len(lines)).To(BeNumerically(">", 0))
+ Expect(session.OutputToString()).ToNot(ContainSubstring("..."))
+ // the second line in the alpine history contains a command longer than 45 chars
+ Expect(len(lines[1])).To(BeNumerically(">", 45))
})
It("podman history with json flag", func() {
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
index 97f77414e..8fc9721f9 100644
--- a/test/e2e/inspect_test.go
+++ b/test/e2e/inspect_test.go
@@ -443,4 +443,27 @@ var _ = Describe("Podman inspect", func() {
Expect(inspect.OutputToString()).To(Equal(`"{"80/tcp":[{"HostIp":"","HostPort":"8080"}]}"`))
})
+ It("Verify container inspect has default network", func() {
+ SkipIfRootless("Requires root CNI networking")
+ ctrName := "testctr"
+ session := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ inspect := podmanTest.InspectContainer(ctrName)
+ Expect(len(inspect)).To(Equal(1))
+ Expect(len(inspect[0].NetworkSettings.Networks)).To(Equal(1))
+ })
+
+ It("Verify stopped container still has default network in inspect", func() {
+ SkipIfRootless("Requires root CNI networking")
+ ctrName := "testctr"
+ session := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ inspect := podmanTest.InspectContainer(ctrName)
+ Expect(len(inspect)).To(Equal(1))
+ Expect(len(inspect[0].NetworkSettings.Networks)).To(Equal(1))
+ })
})
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index 73e18cbce..1bf2a2691 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -375,4 +375,21 @@ var _ = Describe("Podman network create", func() {
Expect(nc).To(ExitWithError())
})
+ It("podman network create with internal should not have dnsname", func() {
+ net := "internal-test" + stringid.GenerateNonCryptoID()
+ nc := podmanTest.Podman([]string{"network", "create", "--internal", net})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net)
+ Expect(nc.ExitCode()).To(BeZero())
+ // Not performing this check on remote tests because it is a logrus error which does
+ // not come back via stderr on the remote client.
+ if !IsRemote() {
+ Expect(nc.ErrorToString()).To(ContainSubstring("dnsname and --internal networks are incompatible"))
+ }
+ nc = podmanTest.Podman([]string{"network", "inspect", net})
+ nc.WaitWithDefaultTimeout()
+ Expect(nc.ExitCode()).To(BeZero())
+ Expect(nc.OutputToString()).ToNot(ContainSubstring("dnsname"))
+ })
+
})
diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go
index 13701fc3b..d12534219 100644
--- a/test/e2e/ps_test.go
+++ b/test/e2e/ps_test.go
@@ -396,11 +396,14 @@ var _ = Describe("Podman ps", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"})
-
+ session = podmanTest.Podman([]string{"ps", "--no-trunc"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Not(ContainSubstring(podid)))
+ session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(podid))
})
diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go
index 7099a2904..4b73004da 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -92,7 +92,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull by digest (image list)", func() {
- session := podmanTest.Podman([]string{"pull", "--override-arch=arm64", ALPINELISTDIGEST})
+ session := podmanTest.Podman([]string{"pull", "--arch=arm64", ALPINELISTDIGEST})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
// inspect using the digest of the list
@@ -135,7 +135,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull by instance digest (image list)", func() {
- session := podmanTest.Podman([]string{"pull", "--override-arch=arm64", ALPINEARM64DIGEST})
+ session := podmanTest.Podman([]string{"pull", "--arch=arm64", ALPINEARM64DIGEST})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
// inspect using the digest of the list
@@ -175,7 +175,7 @@ var _ = Describe("Podman pull", func() {
})
It("podman pull by tag (image list)", func() {
- session := podmanTest.Podman([]string{"pull", "--override-arch=arm64", ALPINELISTTAG})
+ session := podmanTest.Podman([]string{"pull", "--arch=arm64", ALPINELISTTAG})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
// inspect using the tag we used for pulling
@@ -503,10 +503,10 @@ var _ = Describe("Podman pull", func() {
expectedError := "no image found in manifest list for architecture bogus"
Expect(session.ErrorToString()).To(ContainSubstring(expectedError))
- session = podmanTest.Podman([]string{"pull", "--platform=linux/arm64", "--override-os", "windows", ALPINE})
+ session = podmanTest.Podman([]string{"pull", "--platform=linux/arm64", "--os", "windows", ALPINE})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(125))
- expectedError = "--platform option can not be specified with --override-arch or --override-os"
+ expectedError = "--platform option can not be specified with --arch or --os"
Expect(session.ErrorToString()).To(ContainSubstring(expectedError))
session = podmanTest.Podman([]string{"pull", "-q", "--platform=linux/arm64", ALPINE})
diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go
index ca142d7f3..4c50a61ef 100644
--- a/test/e2e/rm_test.go
+++ b/test/e2e/rm_test.go
@@ -132,7 +132,7 @@ var _ = Describe("Podman rm", func() {
latest := "-l"
if IsRemote() {
- latest = "test1"
+ latest = cid
}
result := podmanTest.Podman([]string{"rm", latest})
result.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 19060ecdc..caeaf190e 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -669,7 +669,7 @@ USER bin`
})
It("podman run with secrets", func() {
- SkipIfRemote("--default-mount-file option is not supported in podman-remote")
+ SkipIfRemote("--default-mounts-file option is not supported in podman-remote")
containersDir := filepath.Join(podmanTest.TempDir, "containers")
err := os.MkdirAll(containersDir, 0755)
Expect(err).To(BeNil())
diff --git a/test/e2e/run_working_dir_test.go b/test/e2e/run_working_dir_test.go
index 7d8db361c..59538448e 100644
--- a/test/e2e/run_working_dir_test.go
+++ b/test/e2e/run_working_dir_test.go
@@ -2,7 +2,6 @@ package integration
import (
"os"
- "strings"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
@@ -41,12 +40,9 @@ var _ = Describe("Podman run", func() {
})
It("podman run a container using non existing --workdir", func() {
- if !strings.Contains(podmanTest.OCIRuntime, "crun") {
- Skip("Test only works on crun")
- }
session := podmanTest.Podman([]string{"run", "--workdir", "/home/foobar", ALPINE, "pwd"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(127))
+ Expect(session.ExitCode()).To(Equal(126))
})
It("podman run a container on an image with a workdir", func() {
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index 1d86ae744..4a11802c3 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -299,7 +299,6 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if force secure is true", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -324,15 +323,11 @@ registries = ['{{.Host}}:{{.Port}}']`
registryFileTmpl.Execute(&buffer, registryEndpoints[5])
podmanTest.setRegistriesConfigEnv(buffer.Bytes())
ioutil.WriteFile(fmt.Sprintf("%s/registry5.conf", tempdir), buffer.Bytes(), 0644)
- if IsRemote() {
- podmanTest.RestartRemoteService()
- defer podmanTest.RestartRemoteService()
- }
search := podmanTest.Podman([]string{"search", image, "--tls-verify=true"})
search.WaitWithDefaultTimeout()
- Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.ExitCode()).To(Equal(125))
Expect(search.OutputToString()).Should(BeEmpty())
match, _ := search.ErrorGrepString("error")
Expect(match).Should(BeTrue())
@@ -342,7 +337,6 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if registry is not listed as insecure", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -376,7 +370,7 @@ registries = ['{{.Host}}:{{.Port}}']`
search := podmanTest.Podman([]string{"search", image})
search.WaitWithDefaultTimeout()
- Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.ExitCode()).To(Equal(125))
Expect(search.OutputToString()).Should(BeEmpty())
match, _ := search.ErrorGrepString("error")
Expect(match).Should(BeTrue())
@@ -386,7 +380,6 @@ registries = ['{{.Host}}:{{.Port}}']`
})
It("podman search doesn't attempt HTTP if one registry is not listed as insecure", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -431,7 +424,7 @@ registries = ['{{.Host}}:{{.Port}}']`
search := podmanTest.Podman([]string{"search", "my-alpine"})
search.WaitWithDefaultTimeout()
- Expect(search.ExitCode()).To(Equal(0))
+ Expect(search.ExitCode()).To(Equal(125))
Expect(search.OutputToString()).Should(BeEmpty())
match, _ := search.ErrorGrepString("error")
Expect(match).Should(BeTrue())
diff --git a/test/python/docker/test_images.py b/test/python/docker/test_images.py
index 1fa4aade9..f2b6a5190 100644
--- a/test/python/docker/test_images.py
+++ b/test/python/docker/test_images.py
@@ -82,8 +82,16 @@ class TestImages(unittest.TestCase):
def test_search_image(self):
"""Search for image"""
- for r in self.client.images.search("libpod/alpine"):
- self.assertIn("quay.io/libpod/alpine", r["Name"])
+ for r in self.client.images.search("alpine"):
+ self.assertIn("alpine", r["Name"])
+
+ def test_search_bogus_image(self):
+ """Search for bogus image should throw exception"""
+ try:
+ r = self.client.images.search("bogus/bogus")
+ except:
+ return
+ self.assertTrue(len(r)==0)
def test_remove_image(self):
"""Remove image"""
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 29dc95dc3..dcf1da370 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -589,4 +589,25 @@ json-file | f
is "${lines[1]}" "$rand" "Container runs successfully despite warning"
}
+@test "podman run - check workdir" {
+ # Workdirs specified via the CLI are not created on the root FS.
+ run_podman 126 run --rm --workdir /i/do/not/exist $IMAGE pwd
+ # Note: remote error prepends an attach error.
+ is "$output" "Error: .*workdir \"/i/do/not/exist\" does not exist on container.*"
+
+ testdir=$PODMAN_TMPDIR/volume
+ mkdir -p $testdir
+ randomcontent=$(random_string 10)
+ echo "$randomcontent" > $testdir/content
+
+ # Workdir does not exist on the image but is volume mounted.
+ run_podman run --rm --workdir /IamNotOnTheImage -v $testdir:/IamNotOnTheImage $IMAGE cat content
+ is "$output" "$randomcontent" "cat random content"
+
+ # Workdir does not exist on the image but is created by the runtime as it's
+ # a subdir of a volume.
+ run_podman run --rm --workdir /IamNotOntheImage -v $testdir/content:/IamNotOntheImage/foo $IMAGE cat foo
+ is "$output" "$randomcontent" "cat random content"
+}
+
# vim: filetype=sh
diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats
index 0ae8b0ce0..ae27c479f 100644
--- a/test/system/040-ps.bats
+++ b/test/system/040-ps.bats
@@ -82,11 +82,10 @@ load helpers
run_podman rm -a
}
-@test "podman ps -a --storage" {
- skip_if_remote "ps --storage does not work over remote"
+@test "podman ps -a --external" {
# Setup: ensure that we have no hidden storage containers
- run_podman ps --storage -a
+ run_podman ps --external -a
is "${#lines[@]}" "1" "setup check: no storage containers at start of test"
# Force a buildah timeout; this leaves a buildah container behind
@@ -98,18 +97,18 @@ EOF
run_podman ps -a
is "${#lines[@]}" "1" "podman ps -a does not see buildah container"
- run_podman ps --storage -a
- is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container"
+ run_podman ps --external -a
+ is "${#lines[@]}" "2" "podman ps -a --external sees buildah container"
is "${lines[1]}" \
"[0-9a-f]\{12\} \+$IMAGE *buildah .* seconds ago .* storage .* ${PODMAN_TEST_IMAGE_NAME}-working-container" \
- "podman ps --storage"
+ "podman ps --external"
cid="${lines[1]:0:12}"
# 'rm -a' should be a NOP
run_podman rm -a
- run_podman ps --storage -a
- is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container"
+ run_podman ps --external -a
+ is "${#lines[@]}" "2" "podman ps -a --external sees buildah container"
# We can't rm it without -f, but podman should issue a helpful message
run_podman 2 rm "$cid"
@@ -118,7 +117,7 @@ EOF
# With -f, we can remove it.
run_podman rm -f "$cid"
- run_podman ps --storage -a
+ run_podman ps --external -a
is "${#lines[@]}" "1" "storage container has been removed"
}
diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats
index 548fd56ee..7d9f1fcb3 100644
--- a/test/system/050-stop.bats
+++ b/test/system/050-stop.bats
@@ -30,6 +30,49 @@ load helpers
run_podman rm $cid
}
+# #9051 : podman stop --all was not working with podman-remote
+@test "podman stop --all" {
+ # Start three containers, create (without running) a fourth
+ run_podman run -d --name c1 $IMAGE sleep 20
+ run_podman run -d --name c2 $IMAGE sleep 40
+ run_podman run -d --name c3 $IMAGE sleep 60
+ run_podman create --name c4 $IMAGE sleep 80
+
+ # podman ps (without -a) should show the three running containers
+ run_podman ps --sort names --format '{{.Names}}--{{.Status}}'
+ is "${#lines[*]}" "3" "podman ps shows exactly three containers"
+ is "${lines[0]}" "c1--Up.*" "podman ps shows running container (1)"
+ is "${lines[1]}" "c2--Up.*" "podman ps shows running container (2)"
+ is "${lines[2]}" "c3--Up.*" "podman ps shows running container (3)"
+
+ # Stop -a
+ run_podman stop -a -t 1
+
+ # Now podman ps (without -a) should show nothing.
+ run_podman ps --format '{{.Names}}'
+ is "$output" "" "podman ps, after stop -a, shows no running containers"
+
+ # ...but with -a, containers are shown
+ run_podman ps -a --sort names --format '{{.Names}}--{{.Status}}'
+ is "${#lines[*]}" "4" "podman ps -a shows exactly four containers"
+ is "${lines[0]}" "c1--Exited.*" "ps -a, first stopped container"
+ is "${lines[1]}" "c2--Exited.*" "ps -a, second stopped container"
+ is "${lines[2]}" "c3--Exited.*" "ps -a, third stopped container"
+ is "${lines[3]}" "c4--Created.*" "ps -a, created container (unaffected)"
+}
+
+# #9051 : podman stop --ignore was not working with podman-remote
+@test "podman stop --ignore" {
+ name=thiscontainerdoesnotexist
+ run_podman 125 stop $name
+ is "$output" \
+ "Error: no container with name or ID $name found: no such container" \
+ "podman stop nonexistent container"
+
+ run_podman stop --ignore $name
+ is "$output" "" "podman stop nonexistent container, with --ignore"
+}
+
# Test fallback
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 0e83a184b..6b5bc68fb 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -29,6 +29,29 @@ EOF
run_podman rmi -f build_test
}
+@test "podman build - basic test with --pull" {
+ rand_filename=$(random_string 20)
+ rand_content=$(random_string 50)
+
+ run_podman tag $IMAGE localhost/localonly
+
+ tmpdir=$PODMAN_TMPDIR/build-test
+ mkdir -p $tmpdir
+ dockerfile=$tmpdir/Dockerfile
+ cat >$dockerfile <<EOF
+FROM localhost/localonly
+RUN echo $rand_content > /$rand_filename
+EOF
+ # With --pull, Podman would try to pull a newer image but use the local one
+ # if present. See #9111.
+ run_podman build --pull -t build_test $tmpdir
+
+ run_podman run --rm build_test cat /$rand_filename
+ is "$output" "$rand_content" "reading generated file in image"
+
+ run_podman rmi -f build_test localhost/localonly
+}
+
@test "podman build - global runtime flags test" {
skip_if_remote "--runtime-flag flag not supported for remote"
@@ -126,6 +149,23 @@ EOF
label_name=l$(random_string 8)
label_value=$(random_string 12)
+ # #8679: Create a secrets directory, and mount it in the container
+ # (can only test locally; podman-remote has no --default-mounts-file opt)
+ MOUNTS_CONF=
+ secret_contents="ceci nest pas un secret"
+ CAT_SECRET="echo $secret_contents"
+ if ! is_remote; then
+ mkdir $tmpdir/secrets
+ echo $tmpdir/secrets:/run/secrets > $tmpdir/mounts.conf
+
+ secret_filename=secretfile-$(random_string 20)
+ secret_contents=shhh-$(random_string 30)-shhh
+ echo $secret_contents >$tmpdir/secrets/$secret_filename
+
+ MOUNTS_CONF=--default-mounts-file=$tmpdir/mounts.conf
+ CAT_SECRET="cat /run/secrets/$secret_filename"
+ fi
+
# Command to run on container startup with no args
cat >$tmpdir/mycmd <<EOF
#!/bin/sh
@@ -133,6 +173,7 @@ PATH=/usr/bin:/bin
pwd
echo "\$1"
printenv | grep MYENV | sort | sed -e 's/^MYENV.=//'
+$CAT_SECRET
EOF
# For overriding with --env-file; using multiple files confirms that
@@ -145,10 +186,12 @@ EOF
https_proxy=https-proxy-in-env-file
EOF
+ # NOTE: it's important to not create the workdir.
+ # Podman will make sure to create a missing workdir
+ # if needed. See #9040.
cat >$tmpdir/Containerfile <<EOF
FROM $IMAGE
LABEL $label_name=$label_value
-RUN mkdir $workdir
WORKDIR $workdir
# Test for #7094 - chowning of invalid symlinks
@@ -169,14 +212,20 @@ ENV ftp_proxy ftp-proxy-in-image
ADD mycmd /bin/mydefaultcmd
RUN chmod 755 /bin/mydefaultcmd
RUN chown 2:3 /bin/mydefaultcmd
+
+RUN $CAT_SECRET
+
CMD ["/bin/mydefaultcmd","$s_echo"]
EOF
# cd to the dir, so we test relative paths (important for podman-remote)
cd $PODMAN_TMPDIR
- run_podman build -t build_test -f build-test/Containerfile build-test
+ run_podman ${MOUNTS_CONF} build \
+ -t build_test -f build-test/Containerfile build-test
local iid="${lines[-1]}"
+ # Make sure 'podman build' had the secret mounted
+ is "$output" ".*$secret_contents.*" "podman build has /run/secrets mounted"
if is_remote; then
ENVHOST=""
@@ -187,7 +236,7 @@ EOF
# Run without args - should run the above script. Verify its output.
export MYENV2="$s_env2"
export MYENV3="env-file-should-override-env-host!"
- run_podman run --rm \
+ run_podman ${MOUNTS_CONF} run --rm \
--env-file=$PODMAN_TMPDIR/env-file1 \
--env-file=$PODMAN_TMPDIR/env-file2 \
${ENVHOST} \
@@ -207,6 +256,9 @@ EOF
is "${lines[4]}" "$s_env3" "container default command: env3 (from envfile)"
is "${lines[5]}" "$s_env4" "container default command: env4 (from cmdline)"
+ is "${lines[6]}" "$secret_contents" \
+ "Contents of /run/secrets/$secret_filename in container"
+
# Proxies - environment should override container, but not env-file
http_proxy=http-proxy-from-env ftp_proxy=ftp-proxy-from-env \
run_podman run --rm \
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index c028e16c9..badf44c49 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -6,8 +6,6 @@
load helpers
@test "podman exec - basic test" {
- skip_if_remote "FIXME: pending #7241"
-
rand_filename=$(random_string 20)
rand_content=$(random_string 50)
diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats
index 6a89247e6..f26c97d1e 100644
--- a/test/system/400-unprivileged-access.bats
+++ b/test/system/400-unprivileged-access.bats
@@ -132,7 +132,11 @@ EOF
# Run 'stat' on all the files, plus /dev/null. Get path, file type,
# number of links, major, and minor (see below for why). Do it all
# in one go, to avoid multiple podman-runs
- run_podman run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null ${subset[@]}
+ run_podman '?' run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null ${subset[@]}
+ if [[ $status -gt 1 ]]; then
+ die "Unexpected exit status $status: expected 0 or 1"
+ fi
+
local devnull=
for result in "${lines[@]}"; do
# e.g. /proc/acpi:character special file:1:3:1
@@ -161,6 +165,11 @@ EOF
# If you can think of a better way to do this check,
# please feel free to fix it.
is "$nlinks" "2" "$path: directory link count"
+ elif [[ $result =~ stat:.*No.such.file.or.directory ]]; then
+ # No matter what the path is, this is OK. It has to do with #8949
+ # and RHEL8 and rootless and cgroups v1. Bottom line, what we care
+ # about is that the path not be available inside the container.
+ :
else
die "$path: Unknown file type '$type'"
fi
diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats
index 1e44fe06c..7482d3e55 100644
--- a/test/system/410-selinux.bats
+++ b/test/system/410-selinux.bats
@@ -171,4 +171,15 @@ function check_label() {
run_podman pod rm myselinuxpod
}
+# #8946 - better diagnostics for nonexistent attributes
+@test "podman with nonexistent labels" {
+ skip_if_no_selinux
+
+ # The '.*' in the error below is for dealing with podman-remote, which
+ # includes "error preparing container <sha> for attach" in output.
+ run_podman 126 run --security-opt label=type:foo.bar $IMAGE true
+ is "$output" "Error.*: \`/proc/thread-self/attr/exec\`: OCI runtime error: unable to assign security attribute" "useful diagnostic"
+}
+
+
# vim: filetype=sh
diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go
index c110465db..86160fbd0 100644
--- a/vendor/github.com/google/uuid/version4.go
+++ b/vendor/github.com/google/uuid/version4.go
@@ -14,6 +14,14 @@ func New() UUID {
return Must(NewRandom())
}
+// NewString creates a new random UUID and returns it as a string or panics.
+// NewString is equivalent to the expression
+//
+// uuid.New().String()
+func NewString() string {
+ return Must(NewRandom()).String()
+}
+
// NewRandom returns a Random (Version 4) UUID.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 79aca766d..397ab70be 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -319,7 +319,7 @@ github.com/golang/protobuf/ptypes/timestamp
github.com/google/gofuzz
# github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/google/shlex
-# github.com/google/uuid v1.1.5
+# github.com/google/uuid v1.2.0
github.com/google/uuid
# github.com/gorilla/mux v1.8.0
github.com/gorilla/mux