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.`
existsCommand = &cobra.Command{
Use: "exists CONTAINER",
Use: "exists [flags] CONTAINER",
Short: "Check if a container exists in local storage",
Long: containerExistsDescription,
Example: `podman container exists containerID
Example: `podman container exists --external containerID
podman container exists myctr || podman run --name myctr [etc...]`,
RunE: exists,
Args: cobra.ExactArgs(1),
@ -29,10 +29,19 @@ func init() {
Command: existsCommand,
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 {
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 {
return err
}

View File

@ -58,7 +58,7 @@ func init() {
func listFlagSet(flags *pflag.FlagSet) {
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.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.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)")
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)
}
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
} else if found.Value {
return containers.Diff(cmd, args, diffOpts)

View File

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

View File

@ -2468,7 +2468,6 @@ _podman_rm() {
-i
--latest
-l
--storage
--volumes
-v
"
@ -2696,7 +2695,7 @@ _podman_ps() {
--pod -p
--quiet -q
--size -s
--storage
--external
--namespace --ns
--sync
"
@ -3168,7 +3167,11 @@ _podman_container_exists() {
"
local boolean_options="
"
--external
-h
--help
"
case "$cur" in
-*)
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
## SYNOPSIS
**podman container exists** *container*
**podman container exists** [*options*] *container*
## DESCRIPTION
**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
**--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**
Print usage statement
@ -33,6 +36,13 @@ $ echo $?
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
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.
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**
Display the pods the containers are associated with
**--storage**
**--external**
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 what containers are shown in the output.
@ -120,10 +62,68 @@ Valid filters are listed below:
| volume | [VolumeName] or [MountpointDestination] Volume mounted in container |
| 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**
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**
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
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

View File

@ -19,13 +19,30 @@ import (
)
func ContainerExists(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
// Now use the ABI implementation to prevent us from having duplicate
// code.
containerEngine := abi.ContainerEngine{Libpod: runtime}
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 errors.Cause(err) == define.ErrNoSuchCtr {
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 in local storage. The nameOrID can be a container name
// 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)
if err != nil {
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 {
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() {
// 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(containerExists).To(BeFalse())
})
@ -415,7 +415,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top"
_, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil())
containerExists, err := containers.Exists(bt.conn, name)
containerExists, err := containers.Exists(bt.conn, name, false)
Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue())
})
@ -425,7 +425,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top"
cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
Expect(err).To(BeNil())
containerExists, err := containers.Exists(bt.conn, cid)
containerExists, err := containers.Exists(bt.conn, cid, false)
Expect(err).To(BeNil())
Expect(containerExists).To(BeTrue())
})
@ -435,7 +435,7 @@ var _ = Describe("Podman containers ", func() {
var name = "top"
cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
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(containerExists).To(BeTrue())
})
@ -455,7 +455,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil())
err = containers.Kill(bt.conn, name, "SIGINT")
Expect(err).To(BeNil())
_, err = containers.Exists(bt.conn, name)
_, err = containers.Exists(bt.conn, name, false)
Expect(err).To(BeNil())
})
@ -466,7 +466,7 @@ var _ = Describe("Podman containers ", func() {
Expect(err).To(BeNil())
err = containers.Kill(bt.conn, cid, "SIGTERM")
Expect(err).To(BeNil())
_, err = containers.Exists(bt.conn, cid)
_, err = containers.Exists(bt.conn, cid, false)
Expect(err).To(BeNil())
})

View File

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

View File

@ -21,7 +21,7 @@ type ContainerEngine interface {
ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error)
ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, 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
ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, 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
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)
if err != nil {
if errors.Cause(err) != define.ErrNoSuchCtr {
return nil, err
}
// Check if container exists in storage
if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil {
err = nil
if options.External {
// Check if container exists in storage
if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil {
err = 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")
}
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) {
exists, err := containers.Exists(ic.ClientCxt, nameOrID)
func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) {
exists, err := containers.Exists(ic.ClientCxt, nameOrID, options.External)
return &entities.BoolReport{Value: exists}, err
}

View File

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