summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md22
-rw-r--r--README.md2
-rw-r--r--cmd/podman/cliconfig/config.go2
-rw-r--r--cmd/podman/main_remote.go30
-rw-r--r--cmd/podman/network_create.go6
-rw-r--r--cmd/podman/remoteclientconfig/config.go10
-rw-r--r--cmd/podman/remoteclientconfig/configfile_test.go6
-rw-r--r--cmd/podman/rm.go7
-rw-r--r--cmd/podman/varlink/io.podman.varlink18
-rw-r--r--docs/podman-remote.conf.5.md6
-rw-r--r--docs/podman-rm.1.md7
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--libpod/boltdb_state.go147
-rw-r--r--libpod/boltdb_state_internal.go83
-rw-r--r--libpod/define/errors.go4
-rw-r--r--libpod/in_memory_state.go108
-rw-r--r--libpod/options.go20
-rw-r--r--libpod/runtime_cstorage.go6
-rw-r--r--libpod/runtime_ctr.go116
-rw-r--r--libpod/state.go6
-rw-r--r--libpod/state_test.go70
-rw-r--r--libpod/util_linux.go13
-rw-r--r--libpod/util_unsupported.go4
-rw-r--r--pkg/adapter/client.go2
-rw-r--r--pkg/adapter/client_unix.go11
-rw-r--r--pkg/adapter/client_windows.go15
-rw-r--r--pkg/adapter/containers.go17
-rw-r--r--pkg/adapter/containers_remote.go25
-rw-r--r--pkg/varlinkapi/containers.go9
-rw-r--r--test/e2e/network_create_test.go6
-rw-r--r--test/system/005-info.bats2
-rw-r--r--test/system/400-unprivileged-access.bats2
-rw-r--r--vendor/gopkg.in/yaml.v2/decode.go13
-rw-r--r--vendor/gopkg.in/yaml.v2/resolve.go2
-rw-r--r--vendor/modules.txt2
36 files changed, 647 insertions, 156 deletions
diff --git a/API.md b/API.md
index 3d2a6b01f..a9905c940 100755
--- a/API.md
+++ b/API.md
@@ -41,6 +41,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func Diff(name: string) DiffInfo](#Diff)
+[func EvictContainer(name: string, removeVolumes: bool) string](#EvictContainer)
+
[func ExecContainer(opts: ExecOpts) ](#ExecContainer)
[func ExportContainer(name: string, path: string) string](#ExportContainer)
@@ -480,6 +482,22 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.DeleteUnusedImages
method Diff(name: [string](https://godoc.org/builtin#string)) [DiffInfo](#DiffInfo)</div>
Diff returns a diff between libpod objects
+### <a name="EvictContainer"></a>func EvictContainer
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method EvictContainer(name: [string](https://godoc.org/builtin#string), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
+EvictContainer requires the name or ID of a container as well as a boolean that
+indicates to remove builtin volumes. Upon successful eviction of the container,
+its ID is returned. If the container cannot be found by name or ID,
+a [ContainerNotFound](#ContainerNotFound) error will be returned.
+See also [RemoveContainer](RemoveContainer).
+#### Example
+~~~
+$ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}'
+{
+ "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
+}
+~~~
### <a name="ExecContainer"></a>func ExecContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -988,10 +1006,12 @@ ReceiveFile allows the host to send a remote client a file
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method RemoveContainer(name: [string](https://godoc.org/builtin#string), force: [bool](https://godoc.org/builtin#bool), removeVolumes: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
-RemoveContainer requires the name or ID of container as well a boolean representing whether a running container can be stopped and removed, and a boolean
+RemoveContainer requires the name or ID of a container as well as a boolean that
+indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean
indicating whether to remove builtin volumes. Upon successful removal of the
container, its ID is returned. If the
container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
+See also [EvictContainer](EvictContainer).
#### Example
~~~
$ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
diff --git a/README.md b/README.md
index da8df1c15..9bb7403dc 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Libpod provides a library for applications looking to use the Container Pod concept,
popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes.
-* [Latest Version: 1.5.1](https://github.com/containers/libpod/releases/latest)
+* [Latest Version: 1.6.0](https://github.com/containers/libpod/releases/latest)
* [Continuous Integration:](contrib/cirrus/README.md) [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod/master)
* [GoDoc: ![GoDoc](https://godoc.org/github.com/containers/libpod/libpod?status.svg)](https://godoc.org/github.com/containers/libpod/libpod)
* Automated continuous release downloads (including remote-client):
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index b8796f9b3..5b5225f02 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -42,6 +42,8 @@ type MainFlags struct {
ConnectionName string
RemoteConfigFilePath string
Port int
+ IdentityFile string
+ IgnoreHosts bool
}
type AttachValues struct {
diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go
index f617422e6..623f4098e 100644
--- a/cmd/podman/main_remote.go
+++ b/cmd/podman/main_remote.go
@@ -3,9 +3,11 @@
package main
import (
- "github.com/pkg/errors"
+ "os"
"os/user"
+ "strconv"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -13,14 +15,32 @@ const remote = true
func init() {
var username string
- if curruser, err := user.Current(); err == nil {
- username = curruser.Username
+ if username = os.Getenv("PODMAN_USER"); username == "" {
+ if curruser, err := user.Current(); err == nil {
+ username = curruser.Username
+ }
+ }
+ host := os.Getenv("PODMAN_HOST")
+ port := 22
+ if portstr := os.Getenv("PODMAN_PORT"); portstr != "" {
+ if p, err := strconv.Atoi(portstr); err == nil {
+ port = p
+ }
+ }
+ key := os.Getenv("PODMAN_IDENTITY_FILE")
+ ignore := false
+ if ignorestr := os.Getenv("PODMAN_IGNORE_HOSTS"); ignorestr != "" {
+ if b, err := strconv.ParseBool(ignorestr); err == nil {
+ ignore = b
+ }
}
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConnectionName, "connection", "", "remote connection name")
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteConfigFilePath, "remote-config-path", "", "alternate path for configuration file")
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", username, "username on the remote host")
- rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.Port, "port", 22, "port on remote host")
- rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", "", "remote host")
+ rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.Port, "port", port, "port on remote host")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", host, "remote host")
+ rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.IdentityFile, "identity-file", key, "identity-file")
+ rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.IgnoreHosts, "ignore-hosts", ignore, "ignore hosts")
// TODO maybe we allow the altering of this for bridge connections?
// rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket")
rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.LogLevel, "log-level", "error", "Log messages above specified level: debug, info, warn, error, fatal or panic. Logged to ~/.config/containers/podman.log")
diff --git a/cmd/podman/network_create.go b/cmd/podman/network_create.go
index 378a92568..11f13faad 100644
--- a/cmd/podman/network_create.go
+++ b/cmd/podman/network_create.go
@@ -4,11 +4,12 @@ package main
import (
"fmt"
- "github.com/containers/libpod/pkg/network"
"net"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/adapter"
+ "github.com/containers/libpod/pkg/network"
"github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -58,6 +59,9 @@ func networkcreateCmd(c *cliconfig.NetworkCreateValues) error {
if len(c.InputArgs) > 1 {
return errors.Errorf("only one network can be created at a time")
}
+ if len(c.InputArgs) > 0 && !libpod.NameRegex.MatchString(c.InputArgs[0]) {
+ return libpod.RegexError
+ }
runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand)
if err != nil {
return err
diff --git a/cmd/podman/remoteclientconfig/config.go b/cmd/podman/remoteclientconfig/config.go
index 13880a868..3faa7954a 100644
--- a/cmd/podman/remoteclientconfig/config.go
+++ b/cmd/podman/remoteclientconfig/config.go
@@ -9,10 +9,12 @@ type RemoteConfig struct {
// RemoteConnection describes the attributes of a podman-remote endpoint
type RemoteConnection struct {
- Destination string `toml:"destination"`
- Username string `toml:"username"`
- IsDefault bool `toml:"default"`
- Port int `toml:"port"`
+ Destination string `toml:"destination"`
+ Username string `toml:"username"`
+ IsDefault bool `toml:"default"`
+ Port int `toml:"port"`
+ IdentityFile string `toml:"identity_file"`
+ IgnoreHosts bool `toml:"ignore_hosts"`
}
// GetConfigFilePath is a simple helper to export the configuration file's
diff --git a/cmd/podman/remoteclientconfig/configfile_test.go b/cmd/podman/remoteclientconfig/configfile_test.go
index ea2224ea7..0bcac29a8 100644
--- a/cmd/podman/remoteclientconfig/configfile_test.go
+++ b/cmd/podman/remoteclientconfig/configfile_test.go
@@ -143,7 +143,7 @@ func TestRemoteConfig_GetDefault(t *testing.T) {
wantErr bool
}{
// A good toml should return the connection that is marked isDefault
- {"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true, 22}, false},
+ {"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true, 22, "", false}, false},
// If nothing is marked as isDefault and there is more than one connection, error should occur
{"nodefault", fields{Connections: noDefault}, nil, true},
// if nothing is marked as isDefault but there is only one connection, the one connection is considered the default
@@ -183,9 +183,9 @@ func TestRemoteConfig_GetRemoteConnection(t *testing.T) {
wantErr bool
}{
// Good connection
- {"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true, 22}, false},
+ {"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true, 22, "", false}, false},
// Good connection
- {"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false, 22}, false},
+ {"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false, 22, "", false}, false},
// Getting an unknown connection should result in error
{"noexist", fields{Connections: makeGoodResult().Connections}, args{name: "foobar"}, nil, true},
// Getting a connection when there are none should result in an error
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index 9e3ce4d0b..89062f524 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -13,7 +13,7 @@ var (
rmCommand cliconfig.RmValues
rmDescription = fmt.Sprintf(`Removes one or more containers from the host. The container name or ID can be used.
- Command does not remove images. Running containers will not be removed without the -f option.`)
+ Command does not remove images. Running or unusable containers will not be removed without the -f option.`)
_rmCommand = &cobra.Command{
Use: "rm [flags] CONTAINER [CONTAINER...]",
Short: "Remove one or more containers",
@@ -29,7 +29,8 @@ var (
},
Example: `podman rm imageID
podman rm mywebserver myflaskserver 860a4b23
- podman rm --force --all`,
+ podman rm --force --all
+ podman rm -f c684f0d469f2`,
}
)
@@ -39,7 +40,7 @@ func init() {
rmCommand.SetUsageTemplate(UsageTemplate())
flags := rmCommand.Flags()
flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers")
- flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running container. The default is false")
+ flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false")
flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.BoolVar(&rmCommand.Storage, "storage", false, "Remove container from storage library")
flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container")
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 7239f5d2e..2408dc80c 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -727,10 +727,12 @@ method GetAttachSockets(name: string) -> (sockets: Sockets)
# or name, a [ContainerNotFound](#ContainerNotFound) error is returned.
method WaitContainer(name: string, interval: int) -> (exitcode: int)
-# RemoveContainer requires the name or ID of container as well a boolean representing whether a running container can be stopped and removed, and a boolean
+# RemoveContainer requires the name or ID of a container as well as a boolean that
+# indicates whether a container should be forcefully removed (e.g., by stopping it), and a boolean
# indicating whether to remove builtin volumes. Upon successful removal of the
# container, its ID is returned. If the
# container cannot be found by name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned.
+# See also [EvictContainer](EvictContainer).
# #### Example
# ~~~
# $ varlink call -m unix:/run/podman/io.podman/io.podman.RemoveContainer '{"name": "62f4fd98cb57"}'
@@ -740,6 +742,20 @@ method WaitContainer(name: string, interval: int) -> (exitcode: int)
# ~~~
method RemoveContainer(name: string, force: bool, removeVolumes: bool) -> (container: string)
+# EvictContainer requires the name or ID of a container as well as a boolean that
+# indicates to remove builtin volumes. Upon successful eviction of the container,
+# its ID is returned. If the container cannot be found by name or ID,
+# a [ContainerNotFound](#ContainerNotFound) error will be returned.
+# See also [RemoveContainer](RemoveContainer).
+# #### Example
+# ~~~
+# $ varlink call -m unix:/run/podman/io.podman/io.podman.EvictContainer '{"name": "62f4fd98cb57"}'
+# {
+# "container": "62f4fd98cb57f529831e8f90610e54bba74bd6f02920ffb485e15376ed365c20"
+# }
+# ~~~
+method EvictContainer(name: string, removeVolumes: bool) -> (container: string)
+
# DeleteStoppedContainers will delete all containers that are not running. It will return a list the deleted
# container IDs. See also [RemoveContainer](RemoveContainer).
# #### Example
diff --git a/docs/podman-remote.conf.5.md b/docs/podman-remote.conf.5.md
index 3c8a1a801..e9cc05989 100644
--- a/docs/podman-remote.conf.5.md
+++ b/docs/podman-remote.conf.5.md
@@ -25,6 +25,12 @@ of the user's remote connections.
**port** = int
Use an alternative port for the ssh connections. The default port is 22.
+**identity_file** = ""
+ Use an alternative location for the ssh private key
+
+**ignore_hosts** = bool
+ Don't match the remote ssh host key with known hosts
+
## EXAMPLE
diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md
index 88339af16..207d9d61d 100644
--- a/docs/podman-rm.1.md
+++ b/docs/podman-rm.1.md
@@ -9,7 +9,8 @@ podman\-rm - Remove one or more containers
**podman container rm** [*options*] *container*
## DESCRIPTION
-**podman rm** will remove one or more containers from the host. The container name or ID can be used. This does not remove images. Running containers will not be removed without the `-f` option
+**podman rm** will remove one or more containers from the host. The container name or ID can be used. This does not remove images.
+Running or unusable containers will not be removed without the `-f` option.
## OPTIONS
@@ -19,9 +20,11 @@ Remove all containers. Can be used in conjunction with -f as well.
**--force**, **-f**
-Force the removal of running and paused containers. Forcing a containers removal also
+Force the removal of running and paused containers. Forcing a container removal also
removes containers from container storage even if the container is not known to podman.
Containers could have been created by a different container engine.
+In addition, forcing can be used to remove unusable containers, e.g. containers
+whose OCI runtime has become unavailable.
**--latest**, **-l**
diff --git a/go.mod b/go.mod
index bb39f503d..7dd0a8845 100644
--- a/go.mod
+++ b/go.mod
@@ -104,7 +104,7 @@ require (
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 // indirect
google.golang.org/grpc v1.21.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
- gopkg.in/yaml.v2 v2.2.2
+ gopkg.in/yaml.v2 v2.2.3
k8s.io/api v0.0.0-20190813020757-36bff7324fb7
k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab
diff --git a/go.sum b/go.sum
index b5b6e7eed..c82030a32 100644
--- a/go.sum
+++ b/go.sum
@@ -758,6 +758,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v0.0.0-20190624233834-05ebafbffc79/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index a6fd9a7d8..e43d54eee 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -407,6 +407,60 @@ func (s *BoltState) Container(id string) (*Container, error) {
return ctr, nil
}
+// LookupContainerID retrieves a container ID from the state by full or unique
+// partial ID or name
+func (s *BoltState) LookupContainerID(idOrName string) (string, error) {
+ if idOrName == "" {
+ return "", define.ErrEmptyID
+ }
+
+ if !s.valid {
+ return "", define.ErrDBClosed
+ }
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return "", err
+ }
+ defer s.deferredCloseDBCon(db)
+
+ var id []byte
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrBucket, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ namesBucket, err := getNamesBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ nsBucket, err := getNSBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ fullID, err := s.lookupContainerID(idOrName, ctrBucket, namesBucket, nsBucket)
+ // Check if it is in our namespace
+ if s.namespaceBytes != nil {
+ ns := nsBucket.Get(fullID)
+ if !bytes.Equal(ns, s.namespaceBytes) {
+ return errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
+ }
+ }
+ id = fullID
+ return err
+ })
+
+ if err != nil {
+ return "", err
+ }
+
+ retID := string(id)
+ return retID, nil
+}
+
// LookupContainer retrieves a container from the state by full or unique
// partial ID or name
func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
@@ -444,67 +498,9 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
return err
}
- // First, check if the ID given was the actual container ID
- var id []byte
- ctrExists := ctrBucket.Bucket([]byte(idOrName))
- if ctrExists != nil {
- // A full container ID was given.
- // It might not be in our namespace, but
- // getContainerFromDB() will handle that case.
- id = []byte(idOrName)
- return s.getContainerFromDB(id, ctr, ctrBucket)
- }
-
- // Next, check if the full name was given
- isPod := false
- fullID := namesBucket.Get([]byte(idOrName))
- if fullID != nil {
- // The name exists and maps to an ID.
- // However, we are not yet certain the ID is a
- // container.
- ctrExists = ctrBucket.Bucket(fullID)
- if ctrExists != nil {
- // A container bucket matching the full ID was
- // found.
- return s.getContainerFromDB(fullID, ctr, ctrBucket)
- }
- // Don't error if we have a name match but it's not a
- // container - there's a chance we have a container with
- // an ID starting with those characters.
- // However, so we can return a good error, note whether
- // this is a pod.
- isPod = true
- }
-
- // We were not given a full container ID or name.
- // Search for partial ID matches.
- exists := false
- err = ctrBucket.ForEach(func(checkID, checkName []byte) error {
- // If the container isn't in our namespace, we
- // can't match it
- if s.namespaceBytes != nil {
- ns := nsBucket.Get(checkID)
- if !bytes.Equal(ns, s.namespaceBytes) {
- return nil
- }
- }
- if strings.HasPrefix(string(checkID), idOrName) {
- if exists {
- return errors.Wrapf(define.ErrCtrExists, "more than one result for container ID %s", idOrName)
- }
- id = checkID
- exists = true
- }
-
- return nil
- })
+ id, err := s.lookupContainerID(idOrName, ctrBucket, namesBucket, nsBucket)
if err != nil {
return err
- } else if !exists {
- if isPod {
- return errors.Wrapf(define.ErrNoSuchCtr, "%s is a pod, not a container", idOrName)
- }
- return errors.Wrapf(define.ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
}
return s.getContainerFromDB(id, ctr, ctrBucket)
@@ -860,6 +856,39 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// GetContainerConfig returns a container config from the database by full ID
+func (s *BoltState) GetContainerConfig(id string) (*ContainerConfig, error) {
+ if len(id) == 0 {
+ return nil, define.ErrEmptyID
+ }
+
+ if !s.valid {
+ return nil, define.ErrDBClosed
+ }
+
+ config := new(ContainerConfig)
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return nil, err
+ }
+ defer s.deferredCloseDBCon(db)
+
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrBucket, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ return s.getContainerConfigFromDB([]byte(id), config, ctrBucket)
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return config, nil
+}
+
// RewriteContainerConfig rewrites a container's configuration.
// WARNING: This function is DANGEROUS. Do not use without reading the full
// comment on this function in state.go.
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index a50fce31e..ed87373e9 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -347,7 +347,7 @@ func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
return bkt, nil
}
-func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error {
+func (s *BoltState) getContainerConfigFromDB(id []byte, config *ContainerConfig, ctrsBkt *bolt.Bucket) error {
ctrBkt := ctrsBkt.Bucket(id)
if ctrBkt == nil {
return errors.Wrapf(define.ErrNoSuchCtr, "container %s not found in DB", string(id))
@@ -365,10 +365,18 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.
return errors.Wrapf(define.ErrInternal, "container %s missing config key in DB", string(id))
}
- if err := json.Unmarshal(configBytes, ctr.config); err != nil {
+ if err := json.Unmarshal(configBytes, config); err != nil {
return errors.Wrapf(err, "error unmarshalling container %s config", string(id))
}
+ return nil
+}
+
+func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error {
+ if err := s.getContainerConfigFromDB(id, ctr.config, ctrsBkt); err != nil {
+ return err
+ }
+
// Get the lock
lock, err := s.runtime.lockManager.RetrieveLock(ctr.config.LockID)
if err != nil {
@@ -388,7 +396,7 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.
ociRuntime, ok := s.runtime.ociRuntimes[runtimeName]
if !ok {
- return errors.Wrapf(define.ErrInternal, "container %s was created with OCI runtime %s, but that runtime is not available in the current configuration", ctr.ID(), ctr.config.OCIRuntime)
+ return errors.Wrapf(define.ErrOCIRuntimeUnavailable, "cannot find OCI runtime %q for container %s", ctr.config.OCIRuntime, ctr.ID())
}
ctr.ociRuntime = ociRuntime
}
@@ -862,3 +870,72 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
return nil
}
+
+// lookupContainerID retrieves a container ID from the state by full or unique
+// partial ID or name.
+// NOTE: the retrieved container ID namespace may not match the state namespace.
+func (s *BoltState) lookupContainerID(idOrName string, ctrBucket, namesBucket, nsBucket *bolt.Bucket) ([]byte, error) {
+ // First, check if the ID given was the actual container ID
+ ctrExists := ctrBucket.Bucket([]byte(idOrName))
+ if ctrExists != nil {
+ // A full container ID was given.
+ // It might not be in our namespace, but this will be handled
+ // the callers.
+ return []byte(idOrName), nil
+ }
+
+ // Next, check if the full name was given
+ isPod := false
+ fullID := namesBucket.Get([]byte(idOrName))
+ if fullID != nil {
+ // The name exists and maps to an ID.
+ // However, we are not yet certain the ID is a
+ // container.
+ ctrExists = ctrBucket.Bucket(fullID)
+ if ctrExists != nil {
+ // A container bucket matching the full ID was
+ // found.
+ return fullID, nil
+ }
+ // Don't error if we have a name match but it's not a
+ // container - there's a chance we have a container with
+ // an ID starting with those characters.
+ // However, so we can return a good error, note whether
+ // this is a pod.
+ isPod = true
+ }
+
+ var id []byte
+ // We were not given a full container ID or name.
+ // Search for partial ID matches.
+ exists := false
+ err := ctrBucket.ForEach(func(checkID, checkName []byte) error {
+ // If the container isn't in our namespace, we
+ // can't match it
+ if s.namespaceBytes != nil {
+ ns := nsBucket.Get(checkID)
+ if !bytes.Equal(ns, s.namespaceBytes) {
+ return nil
+ }
+ }
+ if strings.HasPrefix(string(checkID), idOrName) {
+ if exists {
+ return errors.Wrapf(define.ErrCtrExists, "more than one result for container ID %s", idOrName)
+ }
+ id = checkID
+ exists = true
+ }
+
+ return nil
+ })
+
+ if err != nil {
+ return nil, err
+ } else if !exists {
+ if isPod {
+ return nil, errors.Wrapf(define.ErrNoSuchCtr, "%s is a pod, not a container", idOrName)
+ }
+ return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container with name or ID %s found", idOrName)
+ }
+ return id, nil
+}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 004acd58f..5392fbc62 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -112,6 +112,10 @@ var (
// that was not found
ErrOCIRuntimeNotFound = errors.New("OCI runtime command not found error")
+ // ErrOCIRuntimeUnavailable indicates that the OCI runtime associated to a container
+ // could not be found in the configuration
+ ErrOCIRuntimeUnavailable = errors.New("OCI runtime not available in the current configuration")
+
// ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH)
// is out of date for the current podman version
ErrConmonOutdated = errors.New("outdated conmon version")
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index a008fcb39..5ab258772 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -115,15 +115,16 @@ func (s *InMemoryState) Container(id string) (*Container, error) {
return ctr, nil
}
-// LookupContainer retrieves a container by full ID, unique partial ID, or name
-func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) {
+// lookupID retrieves a container or pod ID by full ID, unique partial ID, or
+// name
+func (s *InMemoryState) lookupID(idOrName string) (string, error) {
var (
nameIndex *registrar.Registrar
idIndex *truncindex.TruncIndex
)
if idOrName == "" {
- return nil, define.ErrEmptyID
+ return "", define.ErrEmptyID
}
if s.namespace != "" {
@@ -131,7 +132,7 @@ func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) {
if !ok {
// We have no containers in the namespace
// Return false
- return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
+ return "", define.ErrNoSuchCtr
}
nameIndex = nsIndex.nameIndex
idIndex = nsIndex.idIndex
@@ -147,15 +148,55 @@ func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) {
fullID, err = idIndex.Get(idOrName)
if err != nil {
if err == truncindex.ErrNotExist {
- return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
+ return "", define.ErrNoSuchCtr
}
- return nil, errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
+ return "", errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
}
} else {
- return nil, errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
+ return "", errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
}
}
+ return fullID, nil
+}
+
+// LookupContainerID retrieves a container ID by full ID, unique partial ID, or
+// name
+func (s *InMemoryState) LookupContainerID(idOrName string) (string, error) {
+ fullID, err := s.lookupID(idOrName)
+
+ switch err {
+ case nil:
+ _, ok := s.containers[fullID]
+ if !ok {
+ // It's a pod, not a container
+ return "", errors.Wrapf(define.ErrNoSuchCtr, "name or ID %s is a pod, not a container", idOrName)
+ }
+
+ case define.ErrNoSuchCtr:
+ return "", errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
+
+ default:
+ return "", err
+ }
+
+ return fullID, nil
+}
+
+// LookupContainer retrieves a container by full ID, unique partial ID, or name
+func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) {
+ fullID, err := s.lookupID(idOrName)
+
+ switch err {
+ case nil:
+
+ case define.ErrNoSuchCtr:
+ return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
+
+ default:
+ return nil, err
+ }
+
ctr, ok := s.containers[fullID]
if !ok {
// It's a pod, not a container
@@ -385,6 +426,16 @@ func (s *InMemoryState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// GetContainerConfig returns a container config from the database by full ID
+func (s *InMemoryState) GetContainerConfig(id string) (*ContainerConfig, error) {
+ ctr, err := s.LookupContainer(id)
+ if err != nil {
+ return nil, err
+ }
+
+ return ctr.Config(), nil
+}
+
// RewriteContainerConfig rewrites a container's configuration.
// This function is DANGEROUS, even with an in-memory state.
// Please read the full comment on it in state.go before using it.
@@ -623,49 +674,22 @@ func (s *InMemoryState) Pod(id string) (*Pod, error) {
// LookupPod retrieves a pod from the state from a full or unique partial ID or
// a full name
func (s *InMemoryState) LookupPod(idOrName string) (*Pod, error) {
- var (
- nameIndex *registrar.Registrar
- idIndex *truncindex.TruncIndex
- )
+ fullID, err := s.lookupID(idOrName)
- if idOrName == "" {
- return nil, define.ErrEmptyID
- }
+ switch err {
+ case nil:
- if s.namespace != "" {
- nsIndex, ok := s.namespaceIndexes[s.namespace]
- if !ok {
- // We have no containers in the namespace
- // Return false
- return nil, errors.Wrapf(define.ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
- }
- nameIndex = nsIndex.nameIndex
- idIndex = nsIndex.idIndex
- } else {
- nameIndex = s.nameIndex
- idIndex = s.idIndex
- }
+ case define.ErrNoSuchCtr, define.ErrNoSuchPod:
+ return nil, errors.Wrapf(define.ErrNoSuchPod, "no pod found with name or ID %s", idOrName)
- fullID, err := nameIndex.Get(idOrName)
- if err != nil {
- if err == registrar.ErrNameNotReserved {
- // What was passed is not a name, assume it's an ID
- fullID, err = idIndex.Get(idOrName)
- if err != nil {
- if err == truncindex.ErrNotExist {
- return nil, errors.Wrapf(define.ErrNoSuchPod, "no pod found with name or ID %s", idOrName)
- }
- return nil, errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
- }
- } else {
- return nil, errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
- }
+ default:
+ return nil, err
}
pod, ok := s.pods[fullID]
if !ok {
// It's a container not a pod
- return nil, errors.Wrapf(define.ErrNoSuchPod, "id or name %s is a container not a pod", idOrName)
+ return nil, errors.Wrapf(define.ErrNoSuchPod, "id or name %s is a container, not a pod", idOrName)
}
return pod, nil
diff --git a/libpod/options.go b/libpod/options.go
index d28cb3d8c..22ab22a95 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -20,8 +20,8 @@ import (
)
var (
- nameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
- regexError = errors.Wrapf(define.ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*")
+ NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
+ RegexError = errors.Wrapf(define.ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*")
)
// Runtime Creation Options
@@ -648,8 +648,8 @@ func WithName(name string) CtrCreateOption {
}
// Check the name against a regex
- if !nameRegex.MatchString(name) {
- return regexError
+ if !NameRegex.MatchString(name) {
+ return RegexError
}
ctr.config.Name = name
@@ -1426,8 +1426,8 @@ func WithVolumeName(name string) VolumeCreateOption {
}
// Check the name against a regex
- if !nameRegex.MatchString(name) {
- return regexError
+ if !NameRegex.MatchString(name) {
+ return RegexError
}
volume.config.Name = name
@@ -1532,8 +1532,8 @@ func WithPodName(name string) PodCreateOption {
}
// Check the name against a regex
- if !nameRegex.MatchString(name) {
- return regexError
+ if !NameRegex.MatchString(name) {
+ return RegexError
}
pod.config.Name = name
@@ -1550,8 +1550,8 @@ func WithPodHostname(hostname string) PodCreateOption {
}
// Check the hostname against a regex
- if !nameRegex.MatchString(hostname) {
- return regexError
+ if !NameRegex.MatchString(hostname) {
+ return RegexError
}
pod.config.Hostname = hostname
diff --git a/libpod/runtime_cstorage.go b/libpod/runtime_cstorage.go
index 1e84aef4b..47a91c881 100644
--- a/libpod/runtime_cstorage.go
+++ b/libpod/runtime_cstorage.go
@@ -60,6 +60,12 @@ func (r *Runtime) RemoveStorageContainer(idOrName string, force bool) error {
r.lock.Lock()
defer r.lock.Unlock()
+ return r.removeStorageContainer(idOrName, force)
+}
+
+// Internal function to remove the container storage without
+// locking the runtime.
+func (r *Runtime) removeStorageContainer(idOrName string, force bool) error {
targetID, err := r.store.Lookup(idOrName)
if err != nil {
if err == storage.ErrLayerUnknown {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index bffce7bca..1a2987244 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -550,6 +550,122 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
return cleanupErr
}
+// EvictContainer removes the given container partial or full ID or name, and
+// returns the full ID of the evicted container and any error encountered.
+// It should be used to remove a container when obtaining a Container struct
+// pointer has failed.
+// Running container will not be stopped.
+// If removeVolume is specified, named volumes used by the container will
+// be removed also if and only if the container is the sole user.
+func (r *Runtime) EvictContainer(ctx context.Context, idOrName string, removeVolume bool) (string, error) {
+ r.lock.RLock()
+ defer r.lock.RUnlock()
+ return r.evictContainer(ctx, idOrName, removeVolume)
+}
+
+// evictContainer is the internal function to handle container eviction based
+// on its partial or full ID or name.
+// It returns the full ID of the evicted container and any error encountered.
+// This does not lock the runtime nor the container.
+// removePod is used only when removing pods. It instructs Podman to ignore
+// infra container protections, and *not* remove from the database (as pod
+// remove will handle that).
+func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVolume bool) (string, error) {
+ var err error
+
+ if !r.valid {
+ return "", define.ErrRuntimeStopped
+ }
+ id, err := r.state.LookupContainerID(idOrName)
+ if err != nil {
+ return "", errors.Wrapf(err, "Failed to find container %q in state", idOrName)
+ }
+
+ // Error out if the container does not exist in libpod
+ exists, err := r.state.HasContainer(id)
+ if err != nil {
+ return id, err
+ }
+ if !exists {
+ return id, errors.Wrapf(err, "Failed to find container ID %q for eviction", id)
+ }
+
+ // Re-create a container struct for removal purposes
+ c := new(Container)
+ c.config, err = r.state.GetContainerConfig(id)
+ if err != nil {
+ return id, errors.Wrapf(err, "failed to retrieve config for ctr ID %q", id)
+ }
+ c.state = new(ContainerState)
+
+ // We need to lock the pod before we lock the container.
+ // To avoid races around removing a container and the pod it is in.
+ // Don't need to do this in pod removal case - we're evicting the entire
+ // pod.
+ var pod *Pod
+ if c.config.Pod != "" {
+ pod, err = r.state.Pod(c.config.Pod)
+ if err != nil {
+ return id, errors.Wrapf(err, "container %s is in pod %s, but pod cannot be retrieved", c.ID(), pod.ID())
+ }
+
+ // Lock the pod while we're removing container
+ pod.lock.Lock()
+ defer pod.lock.Unlock()
+ if err := pod.updatePod(); err != nil {
+ return id, err
+ }
+
+ infraID := pod.state.InfraContainerID
+ if c.ID() == infraID {
+ return id, errors.Errorf("container %s is the infra container of pod %s and cannot be removed without removing the pod", c.ID(), pod.ID())
+ }
+ }
+
+ var cleanupErr error
+ // Remove the container from the state
+ if c.config.Pod != "" {
+ // If we're removing the pod, the container will be evicted
+ // from the state elsewhere
+ if err := r.state.RemoveContainerFromPod(pod, c); err != nil {
+ cleanupErr = err
+ }
+ } else {
+ if err := r.state.RemoveContainer(c); err != nil {
+ cleanupErr = err
+ }
+ }
+
+ // Unmount container mount points
+ for _, mount := range c.config.Mounts {
+ Unmount(mount)
+ }
+
+ // Remove container from c/storage
+ if err := r.removeStorageContainer(id, true); err != nil {
+ if cleanupErr == nil {
+ cleanupErr = err
+ }
+ }
+
+ if !removeVolume {
+ return id, cleanupErr
+ }
+
+ for _, v := range c.config.NamedVolumes {
+ if volume, err := r.state.Volume(v.Name); err == nil {
+ if !volume.IsCtrSpecific() {
+ continue
+ }
+ if err := r.removeVolume(ctx, volume, false); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed {
+ logrus.Errorf("cleanup volume (%s): %v", v, err)
+ }
+ }
+ }
+
+ return id, cleanupErr
+}
+
// GetContainer retrieves a container by its ID
func (r *Runtime) GetContainer(id string) (*Container, error) {
r.lock.RLock()
diff --git a/libpod/state.go b/libpod/state.go
index 40080d2cc..e38f820b5 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -58,6 +58,9 @@ type State interface {
// If the container is not in the set namespace, an error will be
// returned.
Container(id string) (*Container, error)
+ // Return a container ID from the database by full or partial ID or full
+ // name.
+ LookupContainerID(idOrName string) (string, error)
// Return a container from the database by full or partial ID or full
// name.
// Containers not in the set namespace will be ignored.
@@ -98,6 +101,9 @@ type State interface {
// returned.
AllContainers() ([]*Container, error)
+ // Return a container config from the database by full ID
+ GetContainerConfig(id string) (*ContainerConfig, error)
+
// PLEASE READ FULL DESCRIPTION BEFORE USING.
// Rewrite a container's configuration.
// This function breaks libpod's normal prohibition on a read-only
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 26a1dee7d..5db1f301c 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -452,6 +452,9 @@ func TestLookupContainerWithEmptyIDFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
_, err := state.LookupContainer("")
assert.Error(t, err)
+
+ _, err = state.LookupContainerID("")
+ assert.Error(t, err)
})
}
@@ -459,6 +462,9 @@ func TestLookupNonexistentContainerFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
_, err := state.LookupContainer("does not exist")
assert.Error(t, err)
+
+ _, err = state.LookupContainerID("does not exist")
+ assert.Error(t, err)
})
}
@@ -472,8 +478,11 @@ func TestLookupContainerByFullID(t *testing.T) {
retrievedCtr, err := state.LookupContainer(testCtr.ID())
assert.NoError(t, err)
-
testContainersEqual(t, retrievedCtr, testCtr, true)
+
+ retrievedID, err := state.LookupContainerID(testCtr.ID())
+ assert.NoError(t, err)
+ assert.Equal(t, retrievedID, testCtr.ID())
})
}
@@ -487,8 +496,11 @@ func TestLookupContainerByUniquePartialID(t *testing.T) {
retrievedCtr, err := state.LookupContainer(testCtr.ID()[0:8])
assert.NoError(t, err)
-
testContainersEqual(t, retrievedCtr, testCtr, true)
+
+ retrievedID, err := state.LookupContainerID(testCtr.ID()[0:8])
+ assert.NoError(t, err)
+ assert.Equal(t, retrievedID, testCtr.ID())
})
}
@@ -507,6 +519,9 @@ func TestLookupContainerByNonUniquePartialIDFails(t *testing.T) {
_, err = state.LookupContainer(testCtr1.ID()[0:8])
assert.Error(t, err)
+
+ _, err = state.LookupContainerID(testCtr1.ID()[0:8])
+ assert.Error(t, err)
})
}
@@ -520,8 +535,11 @@ func TestLookupContainerByName(t *testing.T) {
retrievedCtr, err := state.LookupContainer(testCtr.Name())
assert.NoError(t, err)
-
testContainersEqual(t, retrievedCtr, testCtr, true)
+
+ retrievedID, err := state.LookupContainerID(testCtr.Name())
+ assert.NoError(t, err)
+ assert.Equal(t, retrievedID, testCtr.ID())
})
}
@@ -535,6 +553,9 @@ func TestLookupCtrByPodNameFails(t *testing.T) {
_, err = state.LookupContainer(testPod.Name())
assert.Error(t, err)
+
+ _, err = state.LookupContainerID(testPod.Name())
+ assert.Error(t, err)
})
}
@@ -548,6 +569,9 @@ func TestLookupCtrByPodIDFails(t *testing.T) {
_, err = state.LookupContainer(testPod.ID())
assert.Error(t, err)
+
+ _, err = state.LookupContainerID(testPod.ID())
+ assert.Error(t, err)
})
}
@@ -565,8 +589,11 @@ func TestLookupCtrInSameNamespaceSucceeds(t *testing.T) {
ctr, err := state.LookupContainer(testCtr.ID())
assert.NoError(t, err)
-
testContainersEqual(t, ctr, testCtr, true)
+
+ ctrID, err := state.LookupContainerID(testCtr.ID())
+ assert.NoError(t, err)
+ assert.Equal(t, ctrID, testCtr.ID())
})
}
@@ -584,6 +611,9 @@ func TestLookupCtrInDifferentNamespaceFails(t *testing.T) {
_, err = state.LookupContainer(testCtr.ID())
assert.Error(t, err)
+
+ _, err = state.LookupContainerID(testCtr.ID())
+ assert.Error(t, err)
})
}
@@ -606,8 +636,11 @@ func TestLookupContainerMatchInDifferentNamespaceSucceeds(t *testing.T) {
ctr, err := state.LookupContainer("000")
assert.NoError(t, err)
-
testContainersEqual(t, ctr, testCtr2, true)
+
+ ctrID, err := state.LookupContainerID("000")
+ assert.NoError(t, err)
+ assert.Equal(t, ctrID, testCtr2.ID())
})
}
@@ -3599,3 +3632,30 @@ func TestSaveAndUpdatePodSameNamespace(t *testing.T) {
testPodsEqual(t, testPod, statePod, false)
})
}
+
+func TestGetContainerConfigSucceeds(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testCtr, err := getTestCtr1(manager)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ ctrCfg, err := state.GetContainerConfig(testCtr.ID())
+ assert.NoError(t, err)
+ assert.Equal(t, ctrCfg, testCtr.Config())
+ })
+}
+
+func TestGetContainerConfigEmptyIDFails(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ _, err := state.GetContainerConfig("")
+ assert.Error(t, err)
+ })
+}
+func TestGetContainerConfigNonExistentIDFails(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ _, err := state.GetContainerConfig("does not exist")
+ assert.Error(t, err)
+ })
+}
diff --git a/libpod/util_linux.go b/libpod/util_linux.go
index d5c113daf..631f6836c 100644
--- a/libpod/util_linux.go
+++ b/libpod/util_linux.go
@@ -5,6 +5,7 @@ package libpod
import (
"fmt"
"strings"
+ "syscall"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups"
@@ -12,6 +13,7 @@ import (
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
)
// systemdSliceFromPath makes a new systemd slice under the given parent with
@@ -107,3 +109,14 @@ func LabelVolumePath(path string, shared bool) error {
}
return nil
}
+
+// Unmount umounts a target directory
+func Unmount(mount string) {
+ if err := unix.Unmount(mount, unix.MNT_DETACH); err != nil {
+ if err != syscall.EINVAL {
+ logrus.Warnf("failed to unmount %s : %v", mount, err)
+ } else {
+ logrus.Debugf("failed to unmount %s : %v", mount, err)
+ }
+ }
+}
diff --git a/libpod/util_unsupported.go b/libpod/util_unsupported.go
index 58b0dfbcd..9a9a6eeb6 100644
--- a/libpod/util_unsupported.go
+++ b/libpod/util_unsupported.go
@@ -28,3 +28,7 @@ func assembleSystemdCgroupName(baseSlice, newSlice string) (string, error) {
func LabelVolumePath(path string, shared bool) error {
return define.ErrNotImplemented
}
+
+func Unmount(mount string) error {
+ return define.ErrNotImplemented
+}
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go
index 1805c758d..da4670892 100644
--- a/pkg/adapter/client.go
+++ b/pkg/adapter/client.go
@@ -35,7 +35,7 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) {
if len(r.cmd.RemoteUserName) < 1 {
return nil, errors.New("you must provide a username when providing a remote host name")
}
- rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port}
+ rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port, r.cmd.IdentityFile, r.cmd.IgnoreHosts}
remoteEndpoint, err = newBridgeConnection("", &rc, r.cmd.LogLevel)
// if the user has a config file with connections in it
} else if len(remoteConfigConnections.Connections) > 0 {
diff --git a/pkg/adapter/client_unix.go b/pkg/adapter/client_unix.go
index a7bc7c1c0..7af8b24c6 100644
--- a/pkg/adapter/client_unix.go
+++ b/pkg/adapter/client_unix.go
@@ -14,7 +14,14 @@ func formatDefaultBridge(remoteConn *remoteclientconfig.RemoteConnection, logLev
if port == 0 {
port = 22
}
+ options := ""
+ if remoteConn.IdentityFile != "" {
+ options += " -i " + remoteConn.IdentityFile
+ }
+ if remoteConn.IgnoreHosts {
+ options += " -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
+ }
return fmt.Sprintf(
- `ssh -p %d -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`,
- port, remoteConn.Username, remoteConn.Destination, logLevel)
+ `ssh -p %d -T%s %s@%s -- varlink -A \'podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`,
+ port, options, remoteConn.Username, remoteConn.Destination, logLevel)
}
diff --git a/pkg/adapter/client_windows.go b/pkg/adapter/client_windows.go
index 31e5d9830..32302a600 100644
--- a/pkg/adapter/client_windows.go
+++ b/pkg/adapter/client_windows.go
@@ -9,7 +9,18 @@ import (
)
func formatDefaultBridge(remoteConn *remoteclientconfig.RemoteConnection, logLevel string) string {
+ port := remoteConn.Port
+ if port == 0 {
+ port = 22
+ }
+ options := ""
+ if remoteConn.IdentityFile != "" {
+ options += " -i " + remoteConn.IdentityFile
+ }
+ if remoteConn.IgnoreHosts {
+ options += " -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
+ }
return fmt.Sprintf(
- `ssh -T %s@%s -- /usr/bin/varlink -A '/usr/bin/podman --log-level=%s varlink $VARLINK_ADDRESS' bridge`,
- remoteConn.Username, remoteConn.Destination, logLevel)
+ `ssh -p %d -T%s %s@%s -- varlink -A 'podman --log-level=%s varlink $VARLINK_ADDRESS' bridge`,
+ port, options, remoteConn.Username, remoteConn.Destination, logLevel)
}
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 47db5c0dc..afca4c948 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -205,7 +205,22 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
if err != nil {
- return ok, failures, err
+ // Failed to get containers. If force is specified, get the containers ID
+ // and evict them
+ if !cli.Force {
+ return ok, failures, err
+ }
+
+ for _, ctr := range cli.InputArgs {
+ logrus.Debugf("Evicting container %q", ctr)
+ id, err := r.EvictContainer(ctx, ctr, cli.Volumes)
+ if err != nil {
+ failures[ctr] = errors.Wrapf(err, "Failed to evict container: %q", id)
+ continue
+ }
+ ok = append(ok, id)
+ }
+ return ok, failures, nil
}
pool := shared.NewPool("rm", maxWorkers, len(ctrs))
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 6cecb92da..f7cb28b0c 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -321,16 +321,31 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa
// RemoveContainer removes container(s) based on varlink inputs.
func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) {
- ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
- if err != nil {
- return nil, nil, TranslateError(err)
- }
-
var (
ok = []string{}
failures = map[string]error{}
)
+ ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
+ if err != nil {
+ // Failed to get containers. If force is specified, get the containers ID
+ // and evict them
+ if !cli.Force {
+ return nil, nil, TranslateError(err)
+ }
+
+ for _, ctr := range cli.InputArgs {
+ logrus.Debugf("Evicting container %q", ctr)
+ id, err := iopodman.EvictContainer().Call(r.Conn, ctr, cli.Volumes)
+ if err != nil {
+ failures[ctr] = errors.Wrapf(err, "Failed to evict container: %q", id)
+ continue
+ }
+ ok = append(ok, string(id))
+ }
+ return ok, failures, nil
+ }
+
for _, id := range ids {
_, err := iopodman.RemoveContainer().Call(r.Conn, id, cli.Force, cli.Volumes)
if err != nil {
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 93f9d4fe3..79fcef11a 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -508,7 +508,16 @@ func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, forc
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyRemoveContainer(ctr.ID())
+}
+// EvictContainer ...
+func (i *LibpodAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error {
+ ctx := getContext()
+ id, err := i.Runtime.EvictContainer(ctx, name, removeVolumes)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyEvictContainer(id)
}
// DeleteStoppedContainers ...
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index 410d0b97c..264219178 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -208,4 +208,10 @@ var _ = Describe("Podman network create", func() {
Expect(ncFail.ExitCode()).ToNot(BeZero())
})
+ It("podman network create with invalid network name", func() {
+ nc := podmanTest.Podman([]string{"network", "create", "foo "})
+ nc.WaitWithDefaultTimeout()
+ Expect(nc.ExitCode()).ToNot(BeZero())
+ })
+
})
diff --git a/test/system/005-info.bats b/test/system/005-info.bats
index 7fccc75af..5df6033fc 100644
--- a/test/system/005-info.bats
+++ b/test/system/005-info.bats
@@ -33,7 +33,7 @@ RunRoot:
run_podman info --format=json
expr_nvr="[a-z0-9-]\\\+-[a-z0-9.]\\\+-[a-z0-9]\\\+\."
- expr_path="/[a-z0-9\\\-\\\/.]\\\+\\\$"
+ expr_path="/[a-z0-9\\\/.-]\\\+\\\$"
tests="
host.BuildahVersion | [0-9.]
diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats
index 738d8d87b..56c40e9c8 100644
--- a/test/system/400-unprivileged-access.bats
+++ b/test/system/400-unprivileged-access.bats
@@ -22,7 +22,7 @@ load helpers
# as a user, the parent directory must be world-readable.
test_script=$PODMAN_TMPDIR/fail-if-writable
cat >$test_script <<"EOF"
-#!/bin/sh
+#!/bin/bash
path="$1"
diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go
index e4e56e28e..91679b5b4 100644
--- a/vendor/gopkg.in/yaml.v2/decode.go
+++ b/vendor/gopkg.in/yaml.v2/decode.go
@@ -229,6 +229,10 @@ type decoder struct {
mapType reflect.Type
terrors []string
strict bool
+
+ decodeCount int
+ aliasCount int
+ aliasDepth int
}
var (
@@ -315,6 +319,13 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm
}
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
+ d.decodeCount++
+ if d.aliasDepth > 0 {
+ d.aliasCount++
+ }
+ if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > 0.99 {
+ failf("document contains excessive aliasing")
+ }
switch n.kind {
case documentNode:
return d.document(n, out)
@@ -353,7 +364,9 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
failf("anchor '%s' value contains itself", n.value)
}
d.aliases[n] = true
+ d.aliasDepth++
good = d.unmarshal(n.alias, out)
+ d.aliasDepth--
delete(d.aliases, n)
return good
}
diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go
index 6c151db6f..4120e0c91 100644
--- a/vendor/gopkg.in/yaml.v2/resolve.go
+++ b/vendor/gopkg.in/yaml.v2/resolve.go
@@ -81,7 +81,7 @@ func resolvableTag(tag string) bool {
return false
}
-var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`)
+var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
func resolve(tag string, in string) (rtag string, out interface{}) {
if !resolvableTag(tag) {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index dc113b619..1d92a249d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -549,7 +549,7 @@ gopkg.in/fsnotify.v1
gopkg.in/inf.v0
# gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
gopkg.in/tomb.v1
-# gopkg.in/yaml.v2 v2.2.2
+# gopkg.in/yaml.v2 v2.2.3
gopkg.in/yaml.v2
# k8s.io/api v0.0.0-20190813020757-36bff7324fb7
k8s.io/api/core/v1