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.go8
-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
-rwxr-xr-xcontrib/cirrus/runner.sh54
-rwxr-xr-xcontrib/cirrus/setup_environment.sh2
-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-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--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/networking_linux.go8
-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/server/register_containers.go5
-rw-r--r--pkg/api/server/register_images.go15
-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/create_test.go12
-rw-r--r--test/e2e/history_test.go17
-rw-r--r--test/e2e/inspect_test.go23
-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/070-build.bats10
61 files changed, 757 insertions, 541 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 6071a6fa7..5a4815e1c 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..4219e325b 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -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/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index d9f91c7af..50bc1102f 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/_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-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/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/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/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/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/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/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/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/070-build.bats b/test/system/070-build.bats
index 9e5e3ee1d..7a42a4c18 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -163,10 +163,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
@@ -188,8 +190,7 @@ ADD mycmd /bin/mydefaultcmd
RUN chmod 755 /bin/mydefaultcmd
RUN chown 2:3 /bin/mydefaultcmd
-#FIXME FIXME FIXME: enable if/when 'podman build' passes mounts.conf to buildah
-#RUN $CAT_SECRET
+RUN $CAT_SECRET
CMD ["/bin/mydefaultcmd","$s_echo"]
EOF
@@ -201,8 +202,7 @@ EOF
local iid="${lines[-1]}"
# Make sure 'podman build' had the secret mounted
- #FIXME FIXME: enable if/when 'podman build' passes mounts.conf to buildah
- #is "$output" ".*$secret_contents.*" "podman build has /run/secrets mounted"
+ is "$output" ".*$secret_contents.*" "podman build has /run/secrets mounted"
if is_remote; then
ENVHOST=""