diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal_linux.go | 3 | ||||
-rw-r--r-- | libpod/define/errors.go | 3 | ||||
-rw-r--r-- | libpod/define/pod_inspect.go | 2 | ||||
-rw-r--r-- | libpod/healthcheck_linux.go | 5 | ||||
-rw-r--r-- | libpod/image/image.go | 72 | ||||
-rw-r--r-- | libpod/image/prune.go | 50 | ||||
-rw-r--r-- | libpod/options.go | 20 | ||||
-rw-r--r-- | libpod/pod.go | 2 | ||||
-rw-r--r-- | libpod/pod_api.go | 2 | ||||
-rw-r--r-- | libpod/rootless_cni_linux.go | 2 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 5 | ||||
-rw-r--r-- | libpod/util_linux.go | 12 | ||||
-rw-r--r-- | libpod/util_linux_test.go | 39 |
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) +} |