summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_internal_linux.go3
-rw-r--r--libpod/define/errors.go3
-rw-r--r--libpod/define/pod_inspect.go2
-rw-r--r--libpod/healthcheck_linux.go5
-rw-r--r--libpod/image/image.go72
-rw-r--r--libpod/image/prune.go50
-rw-r--r--libpod/options.go20
-rw-r--r--libpod/pod.go2
-rw-r--r--libpod/pod_api.go2
-rw-r--r--libpod/rootless_cni_linux.go2
-rw-r--r--libpod/runtime_pod_infra_linux.go5
-rw-r--r--libpod/util_linux.go12
-rw-r--r--libpod/util_linux_test.go39
13 files changed, 172 insertions, 45 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index dde7cafb1..eba732d2a 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -415,8 +415,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
// Look up and add groups the user belongs to, if a group wasn't directly specified
- if !rootless.IsRootless() && !strings.Contains(c.config.User, ":") {
+ if !strings.Contains(c.config.User, ":") {
for _, gid := range execUser.Sgids {
+ // FIXME: We need to add a flag to containers.conf to not add these for HPC Users.
g.AddProcessAdditionalGid(uint32(gid))
}
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index b3f6483d1..627928ef7 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -162,6 +162,9 @@ var (
// in a pod. This cannot be done as the infra container has all the network information
ErrNetworkOnPodContainer = errors.New("network cannot be configured when it is shared with a pod")
+ // ErrNetworkInUse indicates the requested operation failed because the network was in use
+ ErrNetworkInUse = errors.New("network is being used")
+
// ErrStoreNotInitialized indicates that the container storage was never
// initialized.
ErrStoreNotInitialized = errors.New("the container storage was never initialized")
diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go
index 60e19fe05..a4115eb92 100644
--- a/libpod/define/pod_inspect.go
+++ b/libpod/define/pod_inspect.go
@@ -89,6 +89,8 @@ type InspectPodInfraConfig struct {
HostAdd []string
// Networks is a list of CNI networks the pod will join.
Networks []string
+ // NetworkOptions are additional options for each network
+ NetworkOptions map[string][]string
}
// InspectPodContainerInfo contains information on a container in a pod.
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 08f37d412..b0f1ff35d 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -35,9 +35,8 @@ func (c *Container) createTimer() error {
conn.Close()
logrus.Debugf("creating systemd-transient files: %s %s", "systemd-run", cmd)
systemdRun := exec.Command("systemd-run", cmd...)
- _, err = systemdRun.CombinedOutput()
- if err != nil {
- return err
+ if output, err := systemdRun.CombinedOutput(); err != nil {
+ return errors.Errorf("%s", output)
}
return nil
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5dfb33afb..f5bf47694 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -410,48 +410,92 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro
if inputName == "" {
return "", nil, errors.Errorf("input name is blank")
}
+
// Check if the input name has a transport and if so strip it
dest, err := alltransports.ParseImageName(inputName)
if err == nil && dest.DockerReference() != nil {
inputName = dest.DockerReference().String()
}
- img, err := ir.getImage(stripSha256(inputName))
+ // Early check for fully-qualified images and (short) IDs.
+ img, err := ir.store.Image(stripSha256(inputName))
if err == nil {
- return inputName, img, err
+ return inputName, img, nil
}
- // container-storage wasn't able to find it in its current form
- // check if the input name has a tag, and if not, run it through
- // again
+ // Note that it's crucial to first decompose the image and check if
+ // it's a fully-qualified one or a "short name". The latter requires
+ // some normalization with search registries and the
+ // "localhost/prefix".
decomposedImage, err := decompose(inputName)
if err != nil {
+ // We may have a storage reference. We can't parse it to a
+ // reference before. Otherwise, we'd normalize "alpine" to
+ // "docker.io/library/alpine:latest" which would break the
+ // order in which we should query local images below.
+ if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil {
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
+ }
return "", nil, err
}
- // The image has a registry name in it and we made sure we looked for it locally
- // with a tag. It cannot be local.
+ // The specified image is fully qualified, so it doesn't exist in the
+ // storage.
if decomposedImage.hasRegistry {
+ // However ... we may still need to normalize to docker.io:
+ // `docker.io/foo` -> `docker.io/library/foo`
+ if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil {
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
+ }
return "", nil, errors.Wrapf(ErrNoSuchImage, imageError)
}
- // if the image is saved with the repository localhost, searching with localhost prepended is necessary
- // We don't need to strip the sha because we have already determined it is not an ID
- ref, err := decomposedImage.referenceWithRegistry(DefaultLocalRegistry)
+
+ // "Short-name image", so let's try out certain prefixes:
+ // 1) DefaultLocalRegistry (i.e., "localhost/)
+ // 2) Unqualified-search registries from registries.conf
+ unqualifiedSearchRegistries, err := registries.GetRegistries()
if err != nil {
return "", nil, err
}
- img, err = ir.getImage(ref.String())
+
+ for _, candidate := range append([]string{DefaultLocalRegistry}, unqualifiedSearchRegistries...) {
+ ref, err := decomposedImage.referenceWithRegistry(candidate)
+ if err != nil {
+ return "", nil, err
+ }
+ img, err := ir.store.Image(ref.String())
+ if err == nil {
+ return ref.String(), img, nil
+ }
+ }
+
+ // Backwards compat: normalize to docker.io as some users may very well
+ // rely on that.
+ ref, err := is.Transport.ParseStoreReference(ir.store, inputName)
if err == nil {
- return inputName, img, err
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
}
- // grab all the local images
+ // Last resort: look at the repotags of all images and try to find a
+ // match.
images, err := ir.GetImages()
if err != nil {
return "", nil, err
}
- // check the repotags of all images for a match
+ decomposedImage, err = decompose(inputName)
+ if err != nil {
+ return "", nil, err
+ }
repoImage, err := findImageInRepotags(decomposedImage, images)
if err == nil {
return inputName, repoImage, nil
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index fcc65fb03..b38265a7e 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -125,29 +125,39 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) (
filterFuncs = append(filterFuncs, generatedFunc)
}
- pruneImages, err := ir.GetPruneImages(ctx, all, filterFuncs)
- if err != nil {
- return nil, errors.Wrap(err, "unable to get images to prune")
- }
- prunedCids := make([]string, 0, len(pruneImages))
- for _, p := range pruneImages {
- repotags, err := p.RepoTags()
+ pruned := []string{}
+ prev := 0
+ for {
+ toPrune, err := ir.GetPruneImages(ctx, all, filterFuncs)
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, "unable to get images to prune")
}
- if err := p.Remove(ctx, true); err != nil {
- if errors.Cause(err) == storage.ErrImageUsedByContainer {
- logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage i.e. Buildah, CRI-O, etc., maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", p.ID(), err)
- continue
- }
- return nil, errors.Wrap(err, "failed to prune image")
+ numImages := len(toPrune)
+ if numImages == 0 || numImages == prev {
+ // If there's nothing left to do, return.
+ break
}
- defer p.newImageEvent(events.Prune)
- nameOrID := p.ID()
- if len(repotags) > 0 {
- nameOrID = repotags[0]
+ prev = numImages
+ for _, img := range toPrune {
+ repotags, err := img.RepoTags()
+ if err != nil {
+ return nil, err
+ }
+ if err := img.Remove(ctx, false); err != nil {
+ if errors.Cause(err) == storage.ErrImageUsedByContainer {
+ logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage (e.g., Buildah, CRI-O, etc.) maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", img.ID(), err)
+ continue
+ }
+ return nil, errors.Wrap(err, "failed to prune image")
+ }
+ defer img.newImageEvent(events.Prune)
+ nameOrID := img.ID()
+ if len(repotags) > 0 {
+ nameOrID = repotags[0]
+ }
+ pruned = append(pruned, nameOrID)
}
- prunedCids = append(prunedCids, nameOrID)
+
}
- return prunedCids, nil
+ return pruned, nil
}
diff --git a/libpod/options.go b/libpod/options.go
index f7b3419e5..f7190d0e3 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -2203,3 +2203,23 @@ func WithPodInfraExitCommand(exitCmd []string) PodCreateOption {
return nil
}
}
+
+// WithPodSlirp4netns tells the pod to use slirp4netns.
+func WithPodSlirp4netns(networkOptions map[string][]string) PodCreateOption {
+ return func(pod *Pod) error {
+ if pod.valid {
+ return define.ErrPodFinalized
+ }
+
+ if !pod.config.InfraContainer.HasInfraContainer {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod networking as no infra container is being created")
+ }
+ if pod.config.InfraContainer.HostNetwork {
+ return errors.Wrapf(define.ErrInvalidArg, "cannot set both HostNetwork and Slirp4netns")
+ }
+ pod.config.InfraContainer.Slirp4netns = true
+ pod.config.InfraContainer.NetworkOptions = networkOptions
+
+ return nil
+ }
+}
diff --git a/libpod/pod.go b/libpod/pod.go
index 709184008..a5a0532be 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -107,6 +107,8 @@ type InfraContainerConfig struct {
ExitCommand []string `json:"exitCommand,omitempty"`
InfraImage string `json:"infraImage,omitempty"`
InfraCommand []string `json:"infraCommand,omitempty"`
+ Slirp4netns bool `json:"slirp4netns,omitempty"`
+ NetworkOptions map[string][]string `json:"network_options,omitempty"`
}
// ID retrieves the pod's ID
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index ec4cc08f7..0ae180356 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -584,7 +584,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.Networks = make([]string, 0, len(p.config.InfraContainer.Networks))
infraConfig.Networks = append(infraConfig.Networks, p.config.InfraContainer.Networks...)
}
-
+ infraConfig.NetworkOptions = p.config.InfraContainer.NetworkOptions
infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings)
}
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
index 2877191e5..21e43ebd0 100644
--- a/libpod/rootless_cni_linux.go
+++ b/libpod/rootless_cni_linux.go
@@ -25,7 +25,7 @@ import (
// Built from ../contrib/rootless-cni-infra.
var rootlessCNIInfraImage = map[string]string{
- "amd64": "quay.io/libpod/rootless-cni-infra@sha256:e92c3a6367f8e554121b96d39af1f19f0f9ac5a32922b290112e13bc661d3a29", // 2-amd64
+ "amd64": "quay.io/libpod/rootless-cni-infra@sha256:304742d5d221211df4ec672807a5842ff11e3729c50bc424ea0cea858f69d7b7", // 3-amd64
}
const (
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index 570cdd38f..7f58e86d8 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -81,8 +81,11 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
// Since user namespace sharing is not implemented, we only need to check if it's rootless
if !p.config.InfraContainer.HostNetwork {
netmode := "bridge"
- if isRootless {
+ if isRootless || p.config.InfraContainer.Slirp4netns {
netmode = "slirp4netns"
+ if len(p.config.InfraContainer.NetworkOptions) != 0 {
+ options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions))
+ }
}
// PostConfigureNetNS should not be set since user namespace sharing is not implemented
// and rootless networking no longer supports post configuration setup
diff --git a/libpod/util_linux.go b/libpod/util_linux.go
index 03c3ab061..5184ed393 100644
--- a/libpod/util_linux.go
+++ b/libpod/util_linux.go
@@ -90,19 +90,23 @@ func assembleSystemdCgroupName(baseSlice, newSlice string) (string, error) {
return final, nil
}
+var lvpRelabel = label.Relabel
+var lvpInitLabels = label.InitLabels
+var lvpReleaseLabel = label.ReleaseLabel
+
// LabelVolumePath takes a mount path for a volume and gives it an
// selinux label of either shared or not
func LabelVolumePath(path string) error {
- _, mountLabel, err := label.InitLabels([]string{})
+ _, mountLabel, err := lvpInitLabels([]string{})
if err != nil {
return errors.Wrapf(err, "error getting default mountlabels")
}
- if err := label.ReleaseLabel(mountLabel); err != nil {
+ if err := lvpReleaseLabel(mountLabel); err != nil {
return errors.Wrapf(err, "error releasing label %q", mountLabel)
}
- if err := label.Relabel(path, mountLabel, true); err != nil {
- if err != syscall.ENOTSUP {
+ if err := lvpRelabel(path, mountLabel, true); err != nil {
+ if err == syscall.ENOTSUP {
logrus.Debugf("Labeling not supported on %q", path)
} else {
return errors.Wrapf(err, "error setting selinux label for %s to %q as shared", path, mountLabel)
diff --git a/libpod/util_linux_test.go b/libpod/util_linux_test.go
new file mode 100644
index 000000000..5fcb04beb
--- /dev/null
+++ b/libpod/util_linux_test.go
@@ -0,0 +1,39 @@
+package libpod
+
+import (
+ "syscall"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLabelVolumePath(t *testing.T) {
+ // Set up mocked SELinux functions for testing.
+ oldRelabel := lvpRelabel
+ oldInitLabels := lvpInitLabels
+ oldReleaseLabel := lvpReleaseLabel
+ defer func() {
+ lvpRelabel = oldRelabel
+ lvpInitLabels = oldInitLabels
+ lvpReleaseLabel = oldReleaseLabel
+ }()
+
+ // Relabel returns ENOTSUP unconditionally.
+ lvpRelabel = func(path string, fileLabel string, shared bool) error {
+ return syscall.ENOTSUP
+ }
+
+ // InitLabels and ReleaseLabel both return dummy values and nil errors.
+ lvpInitLabels = func(options []string) (string, string, error) {
+ pLabel := "system_u:system_r:container_t:s0:c1,c2"
+ mLabel := "system_u:object_r:container_file_t:s0:c1,c2"
+ return pLabel, mLabel, nil
+ }
+ lvpReleaseLabel = func(label string) error {
+ return nil
+ }
+
+ // LabelVolumePath should not return an error if the operation is unsupported.
+ err := LabelVolumePath("/foo/bar")
+ assert.NoError(t, err)
+}