summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/config/config.go19
-rw-r--r--libpod/container_internal_linux.go11
-rw-r--r--libpod/image/image.go20
-rw-r--r--libpod/image/prune.go79
-rw-r--r--libpod/networking_linux.go34
-rw-r--r--libpod/oci_util.go19
-rw-r--r--libpod/options.go8
-rw-r--r--libpod/reset.go107
-rw-r--r--libpod/runtime_migrate_unsupported.go4
9 files changed, 271 insertions, 30 deletions
diff --git a/libpod/config/config.go b/libpod/config/config.go
index 5b4b57f3a..f1fa70fbc 100644
--- a/libpod/config/config.go
+++ b/libpod/config/config.go
@@ -469,6 +469,9 @@ func NewConfig(userConfigPath string) (*Config, error) {
if defaultConfig, err := defaultConfigFromMemory(); err != nil {
return nil, errors.Wrapf(err, "error generating default config from memory")
} else {
+ // Check if we need to switch to cgroupfs and logger=file on rootless.
+ defaultConfig.checkCgroupsAndLogger()
+
if err := config.mergeConfig(defaultConfig); err != nil {
return nil, errors.Wrapf(err, "error merging default config from memory")
}
@@ -487,9 +490,6 @@ func NewConfig(userConfigPath string) (*Config, error) {
return nil, errors.Wrapf(define.ErrInvalidArg, "volume path must be an absolute path - instead got %q", config.VolumePath)
}
- // Check if we need to switch to cgroupfs on rootless.
- config.checkCgroupsAndAdjustConfig()
-
return config, nil
}
@@ -524,11 +524,13 @@ func systemConfigs() ([]string, error) {
return configs, nil
}
-// checkCgroupsAndAdjustConfig checks if we're running rootless with the systemd
+// checkCgroupsAndLogger checks if we're running rootless with the systemd
// cgroup manager. In case the user session isn't available, we're switching the
-// cgroup manager to cgroupfs. Note, this only applies to rootless.
-func (c *Config) checkCgroupsAndAdjustConfig() {
- if !rootless.IsRootless() || c.CgroupManager != define.SystemdCgroupsManager {
+// cgroup manager to cgroupfs and the events logger backend to 'file'.
+// Note, this only applies to rootless.
+func (c *Config) checkCgroupsAndLogger() {
+ if !rootless.IsRootless() || (c.CgroupManager !=
+ define.SystemdCgroupsManager && c.EventsLogger == "file") {
return
}
@@ -543,7 +545,8 @@ func (c *Config) checkCgroupsAndAdjustConfig() {
logrus.Warningf("The cgroups manager is set to systemd but there is no systemd user session available")
logrus.Warningf("For using systemd, you may need to login using an user session")
logrus.Warningf("Alternatively, you can enable lingering with: `loginctl enable-linger %d` (possibly as root)", rootless.GetRootlessUID())
- logrus.Warningf("Falling back to --cgroup-manager=cgroupfs")
+ logrus.Warningf("Falling back to --cgroup-manager=cgroupfs and --events-backend=file")
c.CgroupManager = define.CgroupfsCgroupsManager
+ c.EventsLogger = "file"
}
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index aca7bdc67..586de0776 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -677,6 +677,10 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
return errors.Wrapf(define.ErrCtrStateInvalid, "%q is not running, cannot checkpoint", c.state.State)
}
+ if c.AutoRemove() && options.TargetFile == "" {
+ return errors.Errorf("Cannot checkpoint containers that have been started with '--rm' unless '--export' is used")
+ }
+
if err := c.checkpointRestoreLabelLog("dump.log"); err != nil {
return err
}
@@ -887,7 +891,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// We want to have the same network namespace as before.
if c.config.CreateNetNS {
- if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), c.state.NetNS.Path()); err != nil {
+ netNSPath := ""
+ if !c.config.PostConfigureNetNS {
+ netNSPath = c.state.NetNS.Path()
+ }
+
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), netNSPath); err != nil {
return err
}
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 75ac85311..129ccd376 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -74,6 +74,11 @@ type InfoImage struct {
Layers []LayerInfo
}
+// ImageFilter is a function to determine whether a image is included
+// in command output. Images to be outputted are tested using the function.
+// A true return will include the image, a false return will exclude it.
+type ImageFilter func(*Image) bool //nolint
+
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
var ErrRepoTagNotFound = stderrors.New("unable to match user input to any specific repotag")
@@ -330,6 +335,21 @@ func (i *Image) Names() []string {
return i.image.Names
}
+// NamesHistory returns a string array of names previously associated with the
+// image, which may be a mixture of tags and digests
+func (i *Image) NamesHistory() []string {
+ if len(i.image.Names) > 0 && len(i.image.NamesHistory) > 0 &&
+ // We compare the latest (time-referenced) tags for equality and skip
+ // it in the history if they match to not display them twice. We have
+ // to compare like this, because `i.image.Names` (latest last) gets
+ // appended on retag, whereas `i.image.NamesHistory` gets prepended
+ // (latest first)
+ i.image.Names[len(i.image.Names)-1] == i.image.NamesHistory[0] {
+ return i.image.NamesHistory[1:]
+ }
+ return i.image.NamesHistory
+}
+
// RepoTags returns a string array of repotags associated with the image
func (i *Image) RepoTags() ([]string, error) {
var repoTags []string
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index 006cbdf22..f5be8ed50 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -2,23 +2,78 @@ package image
import (
"context"
+ "strings"
+ "time"
"github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/pkg/timetype"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
+ switch filter {
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(i *Image) bool {
+ labels, err := i.Labels(context.Background())
+ if err != nil {
+ return false
+ }
+ for labelKey, labelValue := range labels {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
+
+ case "until":
+ ts, err := timetype.GetTimestamp(filterValue, time.Now())
+ if err != nil {
+ return nil, err
+ }
+ seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ if err != nil {
+ return nil, err
+ }
+ until := time.Unix(seconds, nanoseconds)
+ return func(i *Image) bool {
+ if !until.IsZero() && i.Created().After((until)) {
+ return true
+ }
+ return false
+ }, nil
+
+ }
+ return nil, nil
+}
+
// GetPruneImages returns a slice of images that have no names/unused
-func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
+func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image, error) {
var (
pruneImages []*Image
)
+
allImages, err := ir.GetRWImages()
if err != nil {
return nil, err
}
for _, i := range allImages {
+ // filter the images based on this.
+ for _, filterFunc := range filterFuncs {
+ if !filterFunc(i) {
+ continue
+ }
+ }
+
if len(i.Names()) == 0 {
pruneImages = append(pruneImages, i)
continue
@@ -38,9 +93,25 @@ func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
// PruneImages prunes dangling and optionally all unused images from the local
// image store
-func (ir *Runtime) PruneImages(ctx context.Context, all bool) ([]string, error) {
- var prunedCids []string
- pruneImages, err := ir.GetPruneImages(all)
+func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
+ var (
+ prunedCids []string
+ filterFuncs []ImageFilter
+ )
+ for _, f := range filter {
+ filterSplit := strings.SplitN(f, "=", 2)
+ if len(filterSplit) < 2 {
+ return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+
+ generatedFunc, err := generatePruneFilterFuncs(filterSplit[0], filterSplit[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+
+ pruneImages, err := ir.GetPruneImages(all, filterFuncs)
if err != nil {
return nil, errors.Wrap(err, "unable to get images to prune")
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index cba7b636a..a68338dbb 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -29,19 +29,40 @@ import (
// Get an OCICNI network config
func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP, staticMAC net.HardwareAddr) ocicni.PodNetwork {
- defaultNetwork := r.netPlugin.GetDefaultNetworkName()
+ var networkKey string
+ if len(networks) > 0 {
+ // This is inconsistent for >1 network, but it's probably the
+ // best we can do.
+ networkKey = networks[0]
+ } else {
+ networkKey = r.netPlugin.GetDefaultNetworkName()
+ }
network := ocicni.PodNetwork{
Name: name,
Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces
ID: id,
NetNS: nsPath,
RuntimeConfig: map[string]ocicni.RuntimeConfig{
- defaultNetwork: {PortMappings: ports},
+ networkKey: {PortMappings: ports},
},
}
+ // If we have extra networks, add them
+ if len(networks) > 0 {
+ network.Networks = make([]ocicni.NetAttachment, len(networks))
+ for i, netName := range networks {
+ network.Networks[i].Name = netName
+ }
+ }
+
if staticIP != nil || staticMAC != nil {
- network.Networks = []ocicni.NetAttachment{{Name: defaultNetwork}}
+ // For static IP or MAC, we need to populate networks even if
+ // it's just the default.
+ if len(networks) == 0 {
+ // If len(networks) == 0 this is guaranteed to be the
+ // default network.
+ network.Networks = []ocicni.NetAttachment{{Name: networkKey}}
+ }
var rt ocicni.RuntimeConfig = ocicni.RuntimeConfig{PortMappings: ports}
if staticIP != nil {
rt.IP = staticIP.String()
@@ -50,12 +71,7 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port
rt.MAC = staticMAC.String()
}
network.RuntimeConfig = map[string]ocicni.RuntimeConfig{
- defaultNetwork: rt,
- }
- } else {
- network.Networks = make([]ocicni.NetAttachment, len(networks))
- for i, netName := range networks {
- network.Networks[i].Name = netName
+ networkKey: rt,
}
}
diff --git a/libpod/oci_util.go b/libpod/oci_util.go
index c1a7f1c9a..3345220ac 100644
--- a/libpod/oci_util.go
+++ b/libpod/oci_util.go
@@ -83,11 +83,22 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
func getOCIRuntimeError(runtimeMsg string) error {
r := strings.ToLower(runtimeMsg)
- if match, _ := regexp.MatchString(".*permission denied.*|.*operation not permitted.*", r); match {
- return errors.Wrapf(define.ErrOCIRuntimePermissionDenied, "%s", strings.Trim(runtimeMsg, "\n"))
+
+ includeFullOutput := logrus.GetLevel() == logrus.DebugLevel
+
+ if match := regexp.MustCompile(".*permission denied.*|.*operation not permitted.*").FindString(r); match != "" {
+ errStr := match
+ if includeFullOutput {
+ errStr = runtimeMsg
+ }
+ return errors.Wrapf(define.ErrOCIRuntimePermissionDenied, "%s", strings.Trim(errStr, "\n"))
}
- if match, _ := regexp.MatchString(".*executable file not found in.*|.*no such file or directory.*", r); match {
- return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(runtimeMsg, "\n"))
+ if match := regexp.MustCompile(".*executable file not found in.*|.*no such file or directory.*").FindString(r); match != "" {
+ errStr := match
+ if includeFullOutput {
+ errStr = runtimeMsg
+ }
+ return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(errStr, "\n"))
}
return errors.Wrapf(define.ErrOCIRuntime, "%s", strings.Trim(runtimeMsg, "\n"))
}
diff --git a/libpod/options.go b/libpod/options.go
index 19c776cf0..a9b775dc3 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -985,8 +985,8 @@ func WithStaticIP(ip net.IP) CtrCreateOption {
return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if the container is not creating a network namespace")
}
- if len(ctr.config.Networks) != 0 {
- return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if joining additional CNI networks")
+ if len(ctr.config.Networks) > 1 {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if joining more than 1 CNI network")
}
ctr.config.StaticIP = ip
@@ -1010,8 +1010,8 @@ func WithStaticMAC(mac net.HardwareAddr) CtrCreateOption {
return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if the container is not creating a network namespace")
}
- if len(ctr.config.Networks) != 0 {
- return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining additional CNI networks")
+ if len(ctr.config.Networks) > 1 {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining more than 1 CNI network")
}
ctr.config.StaticMAC = mac
diff --git a/libpod/reset.go b/libpod/reset.go
new file mode 100644
index 000000000..a35b476a4
--- /dev/null
+++ b/libpod/reset.go
@@ -0,0 +1,107 @@
+package libpod
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// Reset removes all storage
+func (r *Runtime) Reset(ctx context.Context) error {
+
+ pods, err := r.GetAllPods()
+ if err != nil {
+ return err
+ }
+ for _, p := range pods {
+ if err := r.RemovePod(ctx, p, true, true); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchPod {
+ continue
+ }
+ logrus.Errorf("Error removing Pod %s: %v", p.ID(), err)
+ }
+ }
+
+ ctrs, err := r.GetAllContainers()
+ if err != nil {
+ return err
+ }
+
+ for _, c := range ctrs {
+ if err := r.RemoveContainer(ctx, c, true, true); err != nil {
+ if err := r.RemoveStorageContainer(c.ID(), true); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ continue
+ }
+ logrus.Errorf("Error removing container %s: %v", c.ID(), err)
+ }
+ }
+ }
+
+ if err := stopPauseProcess(); err != nil {
+ logrus.Errorf("Error stopping pause process: %v", err)
+ }
+
+ ir := r.ImageRuntime()
+ images, err := ir.GetImages()
+ if err != nil {
+ return err
+ }
+
+ for _, i := range images {
+ if err := i.Remove(ctx, true); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchImage {
+ continue
+ }
+ logrus.Errorf("Error removing image %s: %v", i.ID(), err)
+ }
+ }
+ volumes, err := r.state.AllVolumes()
+ if err != nil {
+ return err
+ }
+ for _, v := range volumes {
+ if err := r.RemoveVolume(ctx, v, true); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchVolume {
+ continue
+ }
+ logrus.Errorf("Error removing volume %s: %v", v.config.Name, err)
+ }
+ }
+
+ _, prevError := r.store.Shutdown(true)
+ if err := os.RemoveAll(r.store.GraphRoot()); err != nil {
+ if prevError != nil {
+ logrus.Error(prevError)
+ }
+ prevError = err
+ }
+ if err := os.RemoveAll(r.store.RunRoot()); err != nil {
+ if prevError != nil {
+ logrus.Error(prevError)
+ }
+ prevError = err
+ }
+ if err := os.RemoveAll(r.config.TmpDir); err != nil {
+ if prevError != nil {
+ logrus.Error(prevError)
+ }
+ prevError = err
+ }
+ if rootless.IsRootless() {
+ configPath := filepath.Join(os.Getenv("HOME"), ".config/containers")
+ if err := os.RemoveAll(configPath); err != nil {
+ if prevError != nil {
+ logrus.Error(prevError)
+ }
+ prevError = err
+ }
+ }
+
+ return prevError
+}
diff --git a/libpod/runtime_migrate_unsupported.go b/libpod/runtime_migrate_unsupported.go
index 1a9e46fdc..e362cca63 100644
--- a/libpod/runtime_migrate_unsupported.go
+++ b/libpod/runtime_migrate_unsupported.go
@@ -9,3 +9,7 @@ import (
func (r *Runtime) migrate(ctx context.Context) error {
return nil
}
+
+func stopPauseProcess() error {
+ return nil
+}