Add support for external container

External containers are containers created outside of Podman.
For example Buildah and CRI-O Containers.

$ buildah from alpine
alpine-working-container
$ buildah run alpine-working-container touch /test
$ podman container exists --external alpine-working-container

$ podman container diff alpine-working-container
C /etc
A /test

Added --external flag to refer to external containers, rather then --storage.

Added --external for podman container exists and modified podman ps to use
--external rather then --storage.  It was felt that --storage would confuse
the user into thinking about changing the storage driver or options.

--storage is still supported through the use of aliases.

Finally podman contianer diff, does not require the --external flag, since it
there is little change of users making the mistake, and would just be a pain
for the user to remember the flag.

podman container exists --external is required because it could fool scripts
that rely on the existance of a Podman container, and there is a potential
for a partial deletion of a container, which could mess up existing users.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2020-10-07 15:27:54 -04:00
parent 22c8270135
commit db23e12611
15 changed files with 143 additions and 87 deletions

View File

@ -12,10 +12,10 @@ var (
containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.` containerExistsDescription = `If the named container exists in local storage, podman container exists exits with 0, otherwise the exit code will be 1.`
existsCommand = &cobra.Command{ existsCommand = &cobra.Command{
Use: "exists CONTAINER", Use: "exists [flags] CONTAINER",
Short: "Check if a container exists in local storage", Short: "Check if a container exists in local storage",
Long: containerExistsDescription, Long: containerExistsDescription,
Example: `podman container exists containerID Example: `podman container exists --external containerID
podman container exists myctr || podman run --name myctr [etc...]`, podman container exists myctr || podman run --name myctr [etc...]`,
RunE: exists, RunE: exists,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
@ -29,10 +29,19 @@ func init() {
Command: existsCommand, Command: existsCommand,
Parent: containerCmd, Parent: containerCmd,
}) })
flags := existsCommand.Flags()
flags.Bool("external", false, "Check external storage containers as well as Podman containers")
} }
func exists(cmd *cobra.Command, args []string) error { func exists(cmd *cobra.Command, args []string) error {
response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0]) external, err := cmd.Flags().GetBool("external")
if err != nil {
return err
}
options := entities.ContainerExistsOptions{
External: external,
}
response, err := registry.ContainerEngine().ContainerExists(context.Background(), args[0], options)
if err != nil { if err != nil {
return err return err
} }

View File

@ -58,7 +58,7 @@ func init() {
func listFlagSet(flags *pflag.FlagSet) { func listFlagSet(flags *pflag.FlagSet) {
flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers") flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers")
flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given") flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given")
flags.BoolVar(&listOpts.Storage, "storage", false, "Show containers in storage not controlled by Podman") flags.BoolVar(&listOpts.Storage, "external", false, "Show containers in storage not controlled by Podman")
flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template") flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template")
flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)") flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)")
flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information") flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information")

View File

@ -48,7 +48,10 @@ func diff(cmd *cobra.Command, args []string) error {
return containers.Diff(cmd, args, diffOpts) return containers.Diff(cmd, args, diffOpts)
} }
if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0]); err != nil { options := entities.ContainerExistsOptions{
External: true,
}
if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0], options); err != nil {
return err return err
} else if found.Value { } else if found.Value {
return containers.Diff(cmd, args, diffOpts) return containers.Diff(cmd, args, diffOpts)

View File

@ -21,6 +21,8 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
name = "time" name = "time"
case "namespace": case "namespace":
name = "ns" name = "ns"
case "storage":
name = "external"
} }
return pflag.NormalizedName(name) return pflag.NormalizedName(name)
} }

View File

@ -2468,7 +2468,6 @@ _podman_rm() {
-i -i
--latest --latest
-l -l
--storage
--volumes --volumes
-v -v
" "
@ -2696,7 +2695,7 @@ _podman_ps() {
--pod -p --pod -p
--quiet -q --quiet -q
--size -s --size -s
--storage --external
--namespace --ns --namespace --ns
--sync --sync
" "
@ -3168,7 +3167,11 @@ _podman_container_exists() {
" "
local boolean_options=" local boolean_options="
" --external
-h
--help
"
case "$cur" in case "$cur" in
-*) -*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))

View File

@ -4,7 +4,7 @@
podman-container-exists - Check if a container exists in local storage podman-container-exists - Check if a container exists in local storage
## SYNOPSIS ## SYNOPSIS
**podman container exists** *container* **podman container exists** [*options*] *container*
## DESCRIPTION ## DESCRIPTION
**podman container exists** checks if a container exists in local storage. The **ID** or **Name** **podman container exists** checks if a container exists in local storage. The **ID** or **Name**
@ -14,6 +14,9 @@ was an issue accessing the local storage.
## OPTIONS ## OPTIONS
**--external**=*true|false*
Check for external containers as well as Podman containers. These external containers are generally created via other container technology such as Buildah or CRI-O.
**-h**, **--help** **-h**, **--help**
Print usage statement Print usage statement
@ -33,6 +36,13 @@ $ echo $?
1 1
``` ```
Check if an container called `ubi8-working-container` created via Buildah exists in local storage (the container does not actually exist).
```
$ podman container exists --external ubi8-working-container
$ echo $?
1
```
## SEE ALSO ## SEE ALSO
podman(1) podman(1)

View File

@ -34,70 +34,12 @@ all the containers information. By default it lists:
Show all the containers created by Podman, default is only running containers. Show all the containers created by Podman, default is only running containers.
Note: Podman shares containers storage with other tools such as Buildah and CRI-O. In some cases these `external` containers might also exist in the same storage. Use the `--storage` option to see these external containers. External containers show the 'storage' status. Note: Podman shares containers storage with other tools such as Buildah and CRI-O. In some cases these `external` containers might also exist in the same storage. Use the `--external` option to see these external containers. External containers show the 'storage' status.
**--pod**, **-p** **--external**
Display the pods the containers are associated with
**--storage**
Display external containers that are not controlled by Podman but are stored in containers storage. These external containers are generally created via other container technology such as Buildah or CRI-O and may depend on the same container images that Podman is also using. External containers are denoted with either a 'buildah' or 'storage' in the COMMAND and STATUS column of the ps output. Only used with the --all option. Display external containers that are not controlled by Podman but are stored in containers storage. These external containers are generally created via other container technology such as Buildah or CRI-O and may depend on the same container images that Podman is also using. External containers are denoted with either a 'buildah' or 'storage' in the COMMAND and STATUS column of the ps output. Only used with the --all option.
**--no-trunc**
Display the extended information
**--quiet**, **-q**
Print the numeric IDs of the containers only
**--format**=*format*
Pretty-print containers to JSON or using a Go template
Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
| --------------- | ------------------------------------------------ |
| .ID | Container ID |
| .Image | Image Name/ID |
| .ImageID | Image ID |
| .Command | Quoted command used |
| .CreatedAt | Creation time for container |
| .RunningFor | Time elapsed since container was started |
| .Status | Status of container |
| .Pod | Pod the container is associated with |
| .Ports | Exposed ports |
| .Size | Size of container |
| .Names | Name of container |
| .Labels | All the labels assigned to the container |
| .Mounts | Volumes mounted in the container |
**--sort**
Sort by command, created, id, image, names, runningfor, size, or status",
Note: Choosing size will sort by size of rootFs, not alphabetically like the rest of the options
Default: created
**--size**, **-s**
Display the total file size
**--last**, **-n**
Print the n last created containers (all states)
**--latest**, **-l**
Show the latest container created (all states)
The latest option is not supported on the remote client.
**--namespace**, **--ns**
Display namespace information
**--filter**, **-f** **--filter**, **-f**
Filter what containers are shown in the output. Filter what containers are shown in the output.
@ -120,10 +62,68 @@ Valid filters are listed below:
| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | | volume | [VolumeName] or [MountpointDestination] Volume mounted in container |
| health | [Status] healthy or unhealthy | | health | [Status] healthy or unhealthy |
**--format**=*format*
Pretty-print containers to JSON or using a Go template
Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
| --------------- | ------------------------------------------------ |
| .ID | Container ID |
| .Image | Image Name/ID |
| .ImageID | Image ID |
| .Command | Quoted command used |
| .CreatedAt | Creation time for container |
| .RunningFor | Time elapsed since container was started |
| .Status | Status of container |
| .Pod | Pod the container is associated with |
| .Ports | Exposed ports |
| .Size | Size of container |
| .Names | Name of container |
| .Labels | All the labels assigned to the container |
| .Mounts | Volumes mounted in the container |
**--help**, **-h** **--help**, **-h**
Print usage statement Print usage statement
**--last**, **-n**
Print the n last created containers (all states)
**--latest**, **-l**
Show the latest container created (all states)
The latest option is not supported on the remote client.
**--namespace**, **--ns**
Display namespace information
**--no-trunc**
Display the extended information
**--pod**, **-p**
Display the pods the containers are associated with
**--quiet**, **-q**
Print the numeric IDs of the containers only
**--sort**
Sort by command, created, id, image, names, runningfor, size, or status",
Note: Choosing size will sort by size of rootFs, not alphabetically like the rest of the options
Default: created
**--size**, **-s**
Display the total file size
**--sync** **--sync**
Force a sync of container state with the OCI runtime. Force a sync of container state with the OCI runtime.
@ -181,7 +181,7 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
``` ```
``` ```
$ podman ps --storage -a $ podman ps --external -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69ed779d8ef9f redis:alpine "redis-server" 25 hours ago Created 6379/tcp k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1 69ed779d8ef9f redis:alpine "redis-server" 25 hours ago Created 6379/tcp k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1
38a8a78596f9 docker.io/library/busybox:latest buildah 2 hours ago storage busybox-working-container 38a8a78596f9 docker.io/library/busybox:latest buildah 2 hours ago storage busybox-working-container

View File

@ -19,13 +19,30 @@ import (
) )
func ContainerExists(w http.ResponseWriter, r *http.Request) { func ContainerExists(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime) runtime := r.Context().Value("runtime").(*libpod.Runtime)
// Now use the ABI implementation to prevent us from having duplicate // Now use the ABI implementation to prevent us from having duplicate
// code. // code.
containerEngine := abi.ContainerEngine{Libpod: runtime} containerEngine := abi.ContainerEngine{Libpod: runtime}
name := utils.GetName(r) name := utils.GetName(r)
report, err := containerEngine.ContainerExists(r.Context(), name) query := struct {
External bool `schema:"external"`
}{
// override any golang type defaults
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
options := entities.ContainerExistsOptions{
External: query.External,
}
report, err := containerEngine.ContainerExists(r.Context(), name, options)
if err != nil { if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr { if errors.Cause(err) == define.ErrNoSuchCtr {
utils.ContainerNotFound(w, name, err) utils.ContainerNotFound(w, name, err)

View File

@ -322,12 +322,14 @@ func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatu
// Exists is a quick, light-weight way to determine if a given container // Exists is a quick, light-weight way to determine if a given container
// exists in local storage. The nameOrID can be a container name // exists in local storage. The nameOrID can be a container name
// or a partial/full ID. // or a partial/full ID.
func Exists(ctx context.Context, nameOrID string) (bool, error) { func Exists(ctx context.Context, nameOrID string, external bool) (bool, error) {
conn, err := bindings.GetClient(ctx) conn, err := bindings.GetClient(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", nil, nil, nameOrID) params := url.Values{}
params.Set("external", strconv.FormatBool(external))
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", params, nil, nameOrID)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -405,7 +405,7 @@ var _ = Describe("Podman containers ", func() {
It("podman bogus container does not exist in local storage", func() { It("podman bogus container does not exist in local storage", func() {
// Bogus container existence check should fail // Bogus container existence check should fail
containerExists, err := containers.Exists(bt.conn, "foobar") containerExists, err := containers.Exists(bt.conn, "foobar", false)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(containerExists).To(BeFalse()) Expect(containerExists).To(BeFalse())
}) })
@ -415,7 +415,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top" var name = "top"
_, err := bt.RunTopContainer(&name, bindings.PFalse, nil) _, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
containerExists, err := containers.Exists(bt.conn, name) containerExists, err := containers.Exists(bt.conn, name, false)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue()) Expect(containerExists).To(BeTrue())
}) })
@ -425,7 +425,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top" var name = "top"
cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
containerExists, err := containers.Exists(bt.conn, cid) containerExists, err := containers.Exists(bt.conn, cid, false)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue()) Expect(containerExists).To(BeTrue())
}) })
@ -435,7 +435,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top" var name = "top"
cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
containerExists, err := containers.Exists(bt.conn, cid[0:12]) containerExists, err := containers.Exists(bt.conn, cid[0:12], false)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue()) Expect(containerExists).To(BeTrue())
}) })
@ -455,7 +455,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil()) Expect(err).To(BeNil())
err = containers.Kill(bt.conn, name, "SIGINT") err = containers.Kill(bt.conn, name, "SIGINT")
Expect(err).To(BeNil()) Expect(err).To(BeNil())
_, err = containers.Exists(bt.conn, name) _, err = containers.Exists(bt.conn, name, false)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
@ -466,7 +466,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil()) Expect(err).To(BeNil())
err = containers.Kill(bt.conn, cid, "SIGTERM") err = containers.Kill(bt.conn, cid, "SIGTERM")
Expect(err).To(BeNil()) Expect(err).To(BeNil())
_, err = containers.Exists(bt.conn, cid) _, err = containers.Exists(bt.conn, cid, false)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })

View File

@ -246,6 +246,11 @@ type ExecOptions struct {
WorkDir string WorkDir string
} }
// ContainerExistsOptions describes the cli values to check if a container exists
type ContainerExistsOptions struct {
External bool
}
// ContainerStartOptions describes the val from the // ContainerStartOptions describes the val from the
// CLI needed to start a container // CLI needed to start a container
type ContainerStartOptions struct { type ContainerStartOptions struct {

View File

@ -21,7 +21,7 @@ type ContainerEngine interface {
ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error)
ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error) ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error)
ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error) ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error)
ContainerExists(ctx context.Context, nameOrID string) (*BoolReport, error) ContainerExists(ctx context.Context, nameOrID string, options ContainerExistsOptions) (*BoolReport, error)
ContainerExport(ctx context.Context, nameOrID string, options ContainerExportOptions) error ContainerExport(ctx context.Context, nameOrID string, options ContainerExportOptions) error
ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error) ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error)
ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, []error, error) ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, []error, error)

View File

@ -75,15 +75,17 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru
} }
// ContainerExists returns whether the container exists in container storage // ContainerExists returns whether the container exists in container storage
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
_, err := ic.Libpod.LookupContainer(nameOrID) _, err := ic.Libpod.LookupContainer(nameOrID)
if err != nil { if err != nil {
if errors.Cause(err) != define.ErrNoSuchCtr { if errors.Cause(err) != define.ErrNoSuchCtr {
return nil, err return nil, err
} }
// Check if container exists in storage if options.External {
if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil { // Check if container exists in storage
err = nil if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil {
err = nil
}
} }
} }
return &entities.BoolReport{Value: err == nil}, nil return &entities.BoolReport{Value: err == nil}, nil

View File

@ -29,8 +29,8 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string,
return errors.New("not implemented") return errors.New("not implemented")
} }
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
exists, err := containers.Exists(ic.ClientCxt, nameOrID) exists, err := containers.Exists(ic.ClientCxt, nameOrID, options.External)
return &entities.BoolReport{Value: exists}, err return &entities.BoolReport{Value: exists}, err
} }

View File

@ -471,8 +471,11 @@ json-file | f
run buildah from $IMAGE run buildah from $IMAGE
cid="$output" cid="$output"
# exists should fail
run_podman 1 container exists $cid
# exists should succeed # exists should succeed
run_podman container exists $cid run_podman container exists --external $cid
run buildah rm $cid run buildah rm $cid
} }