diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 2 | ||||
-rw-r--r-- | libpod/container_api.go | 3 | ||||
-rw-r--r-- | libpod/container_copy_linux.go | 12 | ||||
-rw-r--r-- | libpod/container_exec.go | 12 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 105 | ||||
-rw-r--r-- | libpod/container_stat_linux.go | 7 | ||||
-rw-r--r-- | libpod/define/annotations.go | 2 | ||||
-rw-r--r-- | libpod/define/container_inspect.go | 4 | ||||
-rw-r--r-- | libpod/define/info.go | 2 | ||||
-rw-r--r-- | libpod/kube.go | 70 | ||||
-rw-r--r-- | libpod/logs/log.go | 4 | ||||
-rw-r--r-- | libpod/networking_linux.go | 10 | ||||
-rw-r--r-- | libpod/networking_machine.go | 4 | ||||
-rw-r--r-- | libpod/networking_slirp4netns.go | 4 | ||||
-rw-r--r-- | libpod/oci_attach_linux.go | 8 | ||||
-rw-r--r-- | libpod/oci_conmon_exec_linux.go | 12 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 2 | ||||
-rw-r--r-- | libpod/options.go | 15 | ||||
-rw-r--r-- | libpod/pod.go | 36 | ||||
-rw-r--r-- | libpod/pod_api.go | 2 | ||||
-rw-r--r-- | libpod/runtime.go | 4 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 4 | ||||
-rw-r--r-- | libpod/runtime_pod_linux.go | 15 | ||||
-rw-r--r-- | libpod/runtime_volume_linux.go | 39 | ||||
-rw-r--r-- | libpod/util.go | 7 | ||||
-rw-r--r-- | libpod/volume.go | 3 | ||||
-rw-r--r-- | libpod/volume_internal.go | 3 |
27 files changed, 230 insertions, 161 deletions
diff --git a/libpod/container.go b/libpod/container.go index 6e2b7f528..3e7ab7b0a 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1180,7 +1180,7 @@ func (c *Container) Umask() string { return c.config.Umask } -//Secrets return the secrets in the container +// Secrets return the secrets in the container func (c *Container) Secrets() []*ContainerSecret { return c.config.Secrets } diff --git a/libpod/container_api.go b/libpod/container_api.go index fe41998c0..a6fcf709d 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -229,8 +229,7 @@ func (c *Container) Kill(signal uint) error { // This function returns when the attach finishes. It does not hold the lock for // the duration of its runtime, only using it at the beginning to verify state. func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan define.TerminalSize) error { - switch c.LogDriver() { - case define.PassthroughLogging: + if c.LogDriver() == define.PassthroughLogging { return errors.Wrapf(define.ErrNoLogs, "this container is using the 'passthrough' log driver, cannot attach") } if !c.batched { diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go index 91e712c74..7566fbb12 100644 --- a/libpod/container_copy_linux.go +++ b/libpod/container_copy_linux.go @@ -48,7 +48,11 @@ func (c *Container) copyFromArchive(path string, chown bool, rename map[string]s if err != nil { return nil, err } - unmount = func() { c.unmount(false) } + unmount = func() { + if err := c.unmount(false); err != nil { + logrus.Errorf("Failed to unmount container: %v", err) + } + } } if c.state.State == define.ContainerStateRunning { @@ -117,7 +121,11 @@ func (c *Container) copyToArchive(path string, writer io.Writer) (func() error, if err != nil { return nil, err } - unmount = func() { c.unmount(false) } + unmount = func() { + if err := c.unmount(false); err != nil { + logrus.Errorf("Failed to unmount container: %v", err) + } + } } statInfo, resolvedRoot, resolvedPath, err := c.stat(mountPoint, path) diff --git a/libpod/container_exec.go b/libpod/container_exec.go index d782bebf8..c05e7fd94 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -817,16 +817,16 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi // Please be careful when using this function since it might temporarily unlock // the container when os.RemoveAll($bundlePath) fails with ENOTEMPTY or EBUSY // errors. -func (c *Container) cleanupExecBundle(sessionID string) (Err error) { +func (c *Container) cleanupExecBundle(sessionID string) (err error) { path := c.execBundlePath(sessionID) for attempts := 0; attempts < 50; attempts++ { - Err = os.RemoveAll(path) - if Err == nil || os.IsNotExist(Err) { + err = os.RemoveAll(path) + if err == nil || os.IsNotExist(err) { return nil } - if pathErr, ok := Err.(*os.PathError); ok { - Err = pathErr.Err - if errors.Cause(Err) == unix.ENOTEMPTY || errors.Cause(Err) == unix.EBUSY { + if pathErr, ok := err.(*os.PathError); ok { + err = pathErr.Err + if errors.Cause(err) == unix.ENOTEMPTY || errors.Cause(err) == unix.EBUSY { // give other processes a chance to use the container if !c.batched { if err := c.save(); err != nil { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 9f8b7c686..4742b22ab 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -505,8 +505,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } for _, o := range namedVol.Options { - switch o { - case "U": + if o == "U" { if err := c.ChangeHostPathOwnership(mountPoint, true, int(hostUID), int(hostGID)); err != nil { return nil, err } @@ -596,8 +595,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { // Check overlay volume options for _, o := range overlayVol.Options { - switch o { - case "U": + if o == "U" { if err := c.ChangeHostPathOwnership(overlayVol.Source, true, int(hostUID), int(hostGID)); err != nil { return nil, err } @@ -1182,7 +1180,11 @@ func (c *Container) createCheckpointImage(ctx context.Context, options Container return err } // Clean-up buildah working container - defer importBuilder.Delete() + defer func() { + if err := importBuilder.Delete(); err != nil { + logrus.Errorf("Image builder delete failed: %v", err) + } + }() if err := c.prepareCheckpointExport(); err != nil { return err @@ -1203,7 +1205,9 @@ func (c *Container) createCheckpointImage(ctx context.Context, options Container // Copy checkpoint from temporary tar file in the image addAndCopyOptions := buildah.AddAndCopyOptions{} - importBuilder.Add("", true, addAndCopyOptions, options.TargetFile) + if err := importBuilder.Add("", true, addAndCopyOptions, options.TargetFile); err != nil { + return err + } if err := c.addCheckpointImageMetadata(importBuilder); err != nil { return err @@ -1545,7 +1549,11 @@ func (c *Container) importCheckpointImage(ctx context.Context, imageID string) e } mountPoint, err := img.Mount(ctx, nil, "") - defer img.Unmount(true) + defer func() { + if err := c.unmount(true); err != nil { + logrus.Errorf("Failed to unmount container: %v", err) + } + }() if err != nil { return err } @@ -2115,15 +2123,9 @@ func (c *Container) makeBindMounts() error { } } else { if !c.config.UseImageResolvConf { - newResolv, err := c.generateResolvConf() - if err != nil { + if err := c.generateResolvConf(); err != nil { return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID()) } - err = c.mountIntoRootDirs("/etc/resolv.conf", newResolv) - - if err != nil { - return errors.Wrapf(err, "error assigning mounts to container %s", c.ID()) - } } if !c.config.UseImageHosts { @@ -2144,11 +2146,9 @@ func (c *Container) makeBindMounts() error { return err } } - } else { - if !c.config.UseImageHosts && c.state.BindMounts["/etc/hosts"] == "" { - if err := c.createHosts(); err != nil { - return errors.Wrapf(err, "error creating hosts file for container %s", c.ID()) - } + } else if !c.config.UseImageHosts && c.state.BindMounts["/etc/hosts"] == "" { + if err := c.createHosts(); err != nil { + return errors.Wrapf(err, "error creating hosts file for container %s", c.ID()) } } @@ -2267,7 +2267,7 @@ rootless=%d base := "/run/secrets" if secret.Target != "" { secretFileName = secret.Target - //If absolute path for target given remove base. + // If absolute path for target given remove base. if filepath.IsAbs(secretFileName) { base = "" } @@ -2282,23 +2282,25 @@ rootless=%d } // generateResolvConf generates a containers resolv.conf -func (c *Container) generateResolvConf() (string, error) { +func (c *Container) generateResolvConf() error { var ( nameservers []string networkNameServers []string networkSearchDomains []string ) + hostns := true resolvConf := "/etc/resolv.conf" for _, namespace := range c.config.Spec.Linux.Namespaces { if namespace.Type == spec.NetworkNamespace { + hostns = false if namespace.Path != "" && !strings.HasPrefix(namespace.Path, "/proc/") { definedPath := filepath.Join("/etc/netns", filepath.Base(namespace.Path), "resolv.conf") _, err := os.Stat(definedPath) if err == nil { resolvConf = definedPath } else if !os.IsNotExist(err) { - return "", err + return err } } break @@ -2308,17 +2310,17 @@ func (c *Container) generateResolvConf() (string, error) { contents, err := ioutil.ReadFile(resolvConf) // resolv.conf doesn't have to exists if err != nil && !os.IsNotExist(err) { - return "", err + return err } ns := resolvconf.GetNameservers(contents) // check if systemd-resolved is used, assume it is used when 127.0.0.53 is the only nameserver - if len(ns) == 1 && ns[0] == "127.0.0.53" { + if !hostns && len(ns) == 1 && ns[0] == "127.0.0.53" { // read the actual resolv.conf file for systemd-resolved resolvedContents, err := ioutil.ReadFile("/run/systemd/resolve/resolv.conf") if err != nil { if !os.IsNotExist(err) { - return "", errors.Wrapf(err, "detected that systemd-resolved is in use, but could not locate real resolv.conf") + return errors.Wrapf(err, "detected that systemd-resolved is in use, but could not locate real resolv.conf") } } else { contents = resolvedContents @@ -2341,31 +2343,31 @@ func (c *Container) generateResolvConf() (string, error) { ipv6, err := c.checkForIPv6(netStatus) if err != nil { - return "", err + return err } // Ensure that the container's /etc/resolv.conf is compatible with its // network configuration. - resolv, err := resolvconf.FilterResolvDNS(contents, ipv6, c.config.CreateNetNS) + resolv, err := resolvconf.FilterResolvDNS(contents, ipv6, !hostns) if err != nil { - return "", errors.Wrapf(err, "error parsing host resolv.conf") + return errors.Wrapf(err, "error parsing host resolv.conf") } - dns := make([]net.IP, 0, len(c.runtime.config.Containers.DNSServers)) + dns := make([]net.IP, 0, len(c.runtime.config.Containers.DNSServers)+len(c.config.DNSServer)) for _, i := range c.runtime.config.Containers.DNSServers { result := net.ParseIP(i) if result == nil { - return "", errors.Wrapf(define.ErrInvalidArg, "invalid IP address %s", i) + return errors.Wrapf(define.ErrInvalidArg, "invalid IP address %s", i) } dns = append(dns, result) } - dnsServers := append(dns, c.config.DNSServer...) + dns = append(dns, c.config.DNSServer...) // If the user provided dns, it trumps all; then dns masq; then resolv.conf var search []string switch { - case len(dnsServers) > 0: + case len(dns) > 0: // We store DNS servers as net.IP, so need to convert to string - for _, server := range dnsServers { + for _, server := range dns { nameservers = append(nameservers, server.String()) } default: @@ -2406,20 +2408,15 @@ func (c *Container) generateResolvConf() (string, error) { destPath := filepath.Join(c.state.RunDir, "resolv.conf") if err := os.Remove(destPath); err != nil && !os.IsNotExist(err) { - return "", errors.Wrapf(err, "container %s", c.ID()) + return errors.Wrapf(err, "container %s", c.ID()) } // Build resolv.conf if _, err = resolvconf.Build(destPath, nameservers, search, options); err != nil { - return "", errors.Wrapf(err, "error building resolv.conf for container %s", c.ID()) + return errors.Wrapf(err, "error building resolv.conf for container %s", c.ID()) } - // Relabel resolv.conf for the container - if err := c.relabel(destPath, c.config.MountLabel, true); err != nil { - return "", err - } - - return destPath, nil + return c.bindMountRootFile(destPath, "/etc/resolv.conf") } // Check if a container uses IPv6. @@ -2594,17 +2591,21 @@ func (c *Container) createHosts() error { return err } - if err := os.Chown(targetFile, c.RootUID(), c.RootGID()); err != nil { + return c.bindMountRootFile(targetFile, config.DefaultHostsFile) +} + +// bindMountRootFile will chown and relabel the source file to make it usable in the container. +// It will also add the path to the container bind mount map. +// source is the path on the host, dest is the path in the container. +func (c *Container) bindMountRootFile(source, dest string) error { + if err := os.Chown(source, c.RootUID(), c.RootGID()); err != nil { return err } - if err := label.Relabel(targetFile, c.MountLabel(), false); err != nil { + if err := label.Relabel(source, c.MountLabel(), false); err != nil { return err } - if err = c.mountIntoRootDirs(config.DefaultHostsFile, targetFile); err != nil { - return err - } - return nil + return c.mountIntoRootDirs(dest, source) } // generateGroupEntry generates an entry or entries into /etc/group as @@ -2890,11 +2891,11 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, error) { func (c *Container) passwdEntry(username string, uid, gid, name, homeDir string) string { s := c.config.PasswdEntry - s = strings.Replace(s, "$USERNAME", username, -1) - s = strings.Replace(s, "$UID", uid, -1) - s = strings.Replace(s, "$GID", gid, -1) - s = strings.Replace(s, "$NAME", name, -1) - s = strings.Replace(s, "$HOME", homeDir, -1) + s = strings.ReplaceAll(s, "$USERNAME", username) + s = strings.ReplaceAll(s, "$UID", uid) + s = strings.ReplaceAll(s, "$GID", gid) + s = strings.ReplaceAll(s, "$NAME", name) + s = strings.ReplaceAll(s, "$HOME", homeDir) return s + "\n" } diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_linux.go index 9e225de2e..bbe3edbb3 100644 --- a/libpod/container_stat_linux.go +++ b/libpod/container_stat_linux.go @@ -94,15 +94,16 @@ func (c *Container) stat(containerMountPoint string, containerPath string) (*def } } - if statInfo.IsSymlink { + switch { + case statInfo.IsSymlink: // Symlinks are already evaluated and always relative to the // container's mount point. absContainerPath = statInfo.ImmediateTarget - } else if strings.HasPrefix(resolvedPath, containerMountPoint) { + case strings.HasPrefix(resolvedPath, containerMountPoint): // If the path is on the container's mount point, strip it off. absContainerPath = strings.TrimPrefix(resolvedPath, containerMountPoint) absContainerPath = filepath.Join("/", absContainerPath) - } else { + default: // No symlink and not on the container's mount point, so let's // move it back to the original input. It must have evaluated // to a volume or bind mount but we cannot return host paths. diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go index a83fbff0b..8f5279981 100644 --- a/libpod/define/annotations.go +++ b/libpod/define/annotations.go @@ -135,6 +135,8 @@ const ( // creating a checkpoint image to specify the name of host distribution on // which the checkpoint was created. CheckpointAnnotationDistributionName = "io.podman.annotations.checkpoint.distribution.name" + // MaxKubeAnnotation is the max length of annotations allowed by Kubernetes. + MaxKubeAnnotation = 63 ) // IsReservedAnnotation returns true if the specified value corresponds to an diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index ae2ce9724..6cdffb8b7 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -100,7 +100,7 @@ type InspectRestartPolicy struct { // InspectLogConfig holds information about a container's configured log driver type InspectLogConfig struct { Type string `json:"Type"` - Config map[string]string `json:"Config"` //idk type, TODO + Config map[string]string `json:"Config"` // Path specifies a path to the log file Path string `json:"Path"` // Tag specifies a custom log tag for the container @@ -680,7 +680,7 @@ type InspectContainerData struct { SizeRootFs int64 `json:"SizeRootFs,omitempty"` Mounts []InspectMount `json:"Mounts"` Dependencies []string `json:"Dependencies"` - NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO + NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` Namespace string `json:"Namespace"` IsInfra bool `json:"IsInfra"` Config *InspectContainerConfig `json:"Config"` diff --git a/libpod/define/info.go b/libpod/define/info.go index 48ad51c22..713129ada 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -12,7 +12,7 @@ type Info struct { Version Version `json:"version"` } -//HostInfo describes the libpod host +// HostInfo describes the libpod host type SecurityInfo struct { AppArmorEnabled bool `json:"apparmorEnabled"` DefaultCapabilities string `json:"capabilities"` diff --git a/libpod/kube.go b/libpod/kube.go index eb62643fe..5a5fe9d35 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" "time" + "unicode/utf8" "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/config" @@ -220,7 +221,7 @@ func ConvertV1PodToYAMLPod(pod *v1.Pod) *YAMLPod { cs = append(cs, &YAMLContainer{Container: cc, Resources: res}) } mpo := &YAMLPod{Pod: *pod} - mpo.Spec = &YAMLPodSpec{PodSpec: (*pod).Spec, Containers: cs} + mpo.Spec = &YAMLPodSpec{PodSpec: pod.Spec, Containers: cs} for _, ctr := range pod.Spec.Containers { if ctr.SecurityContext == nil || ctr.SecurityContext.SELinuxOptions == nil { continue @@ -288,6 +289,16 @@ func newServicePortState() servicePortState { } } +func TruncateKubeAnnotation(str string) string { + str = strings.TrimSpace(str) + if utf8.RuneCountInString(str) < define.MaxKubeAnnotation { + return str + } + trunc := string([]rune(str)[:define.MaxKubeAnnotation]) + logrus.Warnf("Truncation Annotation: %q to %q: Kubernetes only allows %d characters", str, trunc, define.MaxKubeAnnotation) + return trunc +} + // containerPortsToServicePorts takes a slice of containerports and generates a // slice of service ports func (state *servicePortState) containerPortsToServicePorts(containerPorts []v1.ContainerPort) ([]v1.ServicePort, error) { @@ -348,11 +359,13 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po for _, ctr := range containers { if !ctr.IsInfra() { + for k, v := range ctr.config.Spec.Annotations { + podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = TruncateKubeAnnotation(v) + } // Convert auto-update labels into kube annotations - for k, v := range getAutoUpdateAnnotations(removeUnderscores(ctr.Name()), ctr.Labels()) { - podAnnotations[k] = v + for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) { + podAnnotations[k] = TruncateKubeAnnotation(v) } - isInit := ctr.IsInitCtr() ctr, volumes, _, annotations, err := containerToV1Container(ctx, ctr) @@ -360,7 +373,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po return nil, err } for k, v := range annotations { - podAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) + podAnnotations[define.BindMountPrefix+k] = TruncateKubeAnnotation(v) } // Since port bindings for the pod are handled by the // infra container, wipe them here. @@ -466,10 +479,14 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, kubeAnnotations := make(map[string]string) ctrNames := make([]string, 0, len(ctrs)) for _, ctr := range ctrs { - ctrNames = append(ctrNames, strings.ReplaceAll(ctr.Name(), "_", "")) + ctrNames = append(ctrNames, removeUnderscores(ctr.Name())) + for k, v := range ctr.config.Spec.Annotations { + kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = TruncateKubeAnnotation(v) + } + // Convert auto-update labels into kube annotations - for k, v := range getAutoUpdateAnnotations(removeUnderscores(ctr.Name()), ctr.Labels()) { - kubeAnnotations[k] = v + for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) { + kubeAnnotations[k] = TruncateKubeAnnotation(v) } isInit := ctr.IsInitCtr() @@ -482,7 +499,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, return nil, err } for k, v := range annotations { - kubeAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) + kubeAnnotations[define.BindMountPrefix+k] = TruncateKubeAnnotation(v) } if isInit { kubeInitCtrs = append(kubeInitCtrs, kubeCtr) @@ -523,11 +540,11 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, } } // end if ctrDNS } - podName := strings.ReplaceAll(ctrs[0].Name(), "_", "") + podName := removeUnderscores(ctrs[0].Name()) // Check if the pod name and container name will end up conflicting // Append -pod if so if util.StringInSlice(podName, ctrNames) { - podName = podName + "-pod" + podName += "-pod" } return newPodObject( @@ -633,7 +650,7 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, [] kubeContainer.Ports = ports // This should not be applicable - //container.EnvFromSource = + // container.EnvFromSource = kubeContainer.SecurityContext = kubeSec kubeContainer.StdinOnce = false kubeContainer.TTY = c.config.Spec.Process.Terminal @@ -885,7 +902,7 @@ func convertVolumePathToName(hostSourcePath string) (string, error) { } // First, trim trailing slashes, then replace slashes with dashes. // Thus, /mnt/data/ will become mnt-data - return strings.Replace(strings.Trim(hostSourcePath, "/"), "/", "-", -1), nil + return strings.ReplaceAll(strings.Trim(hostSourcePath, "/"), "/", "-"), nil } func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v1.Capabilities { @@ -927,14 +944,20 @@ func capAddDrop(caps *specs.LinuxCapabilities) (*v1.Capabilities, error) { if err != nil { return nil, err } + + defCaps := g.Config.Process.Capabilities // Combine all the default capabilities into a slice - defaultCaps := append(g.Config.Process.Capabilities.Ambient, g.Config.Process.Capabilities.Bounding...) - defaultCaps = append(defaultCaps, g.Config.Process.Capabilities.Effective...) - defaultCaps = append(defaultCaps, g.Config.Process.Capabilities.Inheritable...) - defaultCaps = append(defaultCaps, g.Config.Process.Capabilities.Permitted...) + defaultCaps := make([]string, 0, len(defCaps.Ambient)+len(defCaps.Bounding)+len(defCaps.Effective)+len(defCaps.Inheritable)+len(defCaps.Permitted)) + defaultCaps = append(defaultCaps, defCaps.Ambient...) + defaultCaps = append(defaultCaps, defCaps.Bounding...) + defaultCaps = append(defaultCaps, defCaps.Effective...) + defaultCaps = append(defaultCaps, defCaps.Inheritable...) + defaultCaps = append(defaultCaps, defCaps.Permitted...) // Combine all the container's capabilities into a slice - containerCaps := append(caps.Ambient, caps.Bounding...) + containerCaps := make([]string, 0, len(caps.Ambient)+len(caps.Bounding)+len(caps.Effective)+len(caps.Inheritable)+len(caps.Permitted)) + containerCaps = append(containerCaps, caps.Ambient...) + containerCaps = append(containerCaps, caps.Bounding...) containerCaps = append(containerCaps, caps.Effective...) containerCaps = append(containerCaps, caps.Inheritable...) containerCaps = append(containerCaps, caps.Permitted...) @@ -1011,7 +1034,11 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { if err != nil { return nil, errors.Wrapf(err, "failed to mount %s mountpoint", c.ID()) } - defer c.unmount(false) + defer func() { + if err := c.unmount(false); err != nil { + logrus.Errorf("Failed to unmount container: %v", err) + } + }() } logrus.Debugf("Looking in container for user: %s", c.User()) @@ -1042,7 +1069,7 @@ func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) []v1.V } func removeUnderscores(s string) string { - return strings.Replace(s, "_", "", -1) + return strings.ReplaceAll(s, "_", "") } // getAutoUpdateAnnotations searches for auto-update container labels @@ -1051,12 +1078,13 @@ func getAutoUpdateAnnotations(ctrName string, ctrLabels map[string]string) map[s autoUpdateLabel := "io.containers.autoupdate" annotations := make(map[string]string) + ctrName = removeUnderscores(ctrName) for k, v := range ctrLabels { if strings.Contains(k, autoUpdateLabel) { // since labels can variate between containers within a pod, they will be // identified with the container name when converted into kube annotations kc := fmt.Sprintf("%s/%s", k, ctrName) - annotations[kc] = v + annotations[kc] = TruncateKubeAnnotation(v) } } diff --git a/libpod/logs/log.go b/libpod/logs/log.go index 0eb3bb922..4d7d5ac58 100644 --- a/libpod/logs/log.go +++ b/libpod/logs/log.go @@ -28,7 +28,7 @@ const ( // FullLogType signifies a log line is full FullLogType = "F" - //ANSIEscapeResetCode is a code that resets all colors and text effects + // ANSIEscapeResetCode is a code that resets all colors and text effects ANSIEscapeResetCode = "\033[0m" ) @@ -167,7 +167,7 @@ func getTailLog(path string, tail int) ([]*LogLine, error) { return tailLog, nil } -//getColor returns a ANSI escape code for color based on the colorID +// getColor returns a ANSI escape code for color based on the colorID func getColor(colorID int64) string { colors := map[int64]string{ 0: "\033[37m", // Light Gray diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index a312f5a0c..0c124cf0b 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -488,7 +488,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { pid := strconv.Itoa(cmd.Process.Pid) err = ioutil.WriteFile(filepath.Join(rootlessNetNsDir, rootlessNetNsSilrp4netnsPidFile), []byte(pid), 0700) if err != nil { - errors.Wrap(err, "unable to write rootless-netns slirp4netns pid file") + return nil, errors.Wrap(err, "unable to write rootless-netns slirp4netns pid file") } defer func() { @@ -579,7 +579,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { // lets add /usr/sbin to $PATH ourselves. path = os.Getenv("PATH") if !strings.Contains(path, "/usr/sbin") { - path = path + ":/usr/sbin" + path += ":/usr/sbin" os.Setenv("PATH", path) } @@ -1148,7 +1148,7 @@ func resultToBasicNetworkConfig(result types.StatusBlock) define.InspectBasicNet for _, netAddress := range netInt.Subnets { size, _ := netAddress.IPNet.Mask.Size() if netAddress.IPNet.IP.To4() != nil { - //ipv4 + // ipv4 if config.IPAddress == "" { config.IPAddress = netAddress.IPNet.IP.String() config.IPPrefixLen = size @@ -1157,7 +1157,7 @@ func resultToBasicNetworkConfig(result types.StatusBlock) define.InspectBasicNet config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, define.Address{Addr: netAddress.IPNet.IP.String(), PrefixLength: size}) } } else { - //ipv6 + // ipv6 if config.GlobalIPv6Address == "" { config.GlobalIPv6Address = netAddress.IPNet.IP.String() config.GlobalIPv6PrefixLen = size @@ -1508,7 +1508,7 @@ func ocicniPortsToNetTypesPorts(ports []types.OCICNIPortMapping) []types.PortMap ports[i].Protocol == currentPort.Protocol && ports[i].HostPort-int32(currentPort.Range) == int32(currentPort.HostPort) && ports[i].ContainerPort-int32(currentPort.Range) == int32(currentPort.ContainerPort) { - currentPort.Range = currentPort.Range + 1 + currentPort.Range++ } else { newPorts = append(newPorts, currentPort) currentPort = types.PortMapping{ diff --git a/libpod/networking_machine.go b/libpod/networking_machine.go index 73089c474..7b8eb94df 100644 --- a/libpod/networking_machine.go +++ b/libpod/networking_machine.go @@ -33,9 +33,9 @@ type machineExpose struct { func requestMachinePorts(expose bool, ports []types.PortMapping) error { url := "http://" + machineGvproxyEndpoint + "/services/forwarder/" if expose { - url = url + "expose" + url += "expose" } else { - url = url + "unexpose" + url += "unexpose" } ctx := context.Background() client := &http.Client{ diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index 4a0ef0b3a..788834435 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -82,7 +82,9 @@ func checkSlirpFlags(path string) (*slirpFeatures, error) { } func parseSlirp4netnsNetworkOptions(r *Runtime, extraOptions []string) (*slirp4netnsNetworkOptions, error) { - slirpOptions := append(r.config.Engine.NetworkCmdOptions, extraOptions...) + slirpOptions := make([]string, 0, len(r.config.Engine.NetworkCmdOptions)+len(extraOptions)) + slirpOptions = append(slirpOptions, r.config.Engine.NetworkCmdOptions...) + slirpOptions = append(slirpOptions, extraOptions...) slirp4netnsOpts := &slirp4netnsNetworkOptions{ // overwrite defaults disableHostLoopback: true, diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index b5eabec1f..c6af294d5 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -274,11 +274,15 @@ func readStdio(conn *net.UnixConn, streams *define.AttachStreams, receiveStdoutE var err error select { case err = <-receiveStdoutError: - conn.CloseWrite() + if err := conn.CloseWrite(); err != nil { + logrus.Errorf("Failed to close stdin: %v", err) + } return err case err = <-stdinDone: if err == define.ErrDetach { - conn.CloseWrite() + if err := conn.CloseWrite(); err != nil { + logrus.Errorf("Failed to close stdin: %v", err) + } return err } if err == nil { diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index b8fd82591..6d2f13525 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -766,13 +766,11 @@ func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessio if execUser.Uid == 0 { pspec.Capabilities.Effective = pspec.Capabilities.Bounding pspec.Capabilities.Permitted = pspec.Capabilities.Bounding - } else { - if user == c.config.User { - pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective - pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective - pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective - pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective - } + } else if user == c.config.User { + pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective + pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective + pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective + pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective } hasHomeSet := false diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index dc1c75cea..c232702e9 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -1371,7 +1371,7 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p case define.JSONLogging: fallthrough //lint:ignore ST1015 the default case has to be here - default: //nolint:stylecheck + default: //nolint:stylecheck,gocritic // No case here should happen except JSONLogging, but keep this here in case the options are extended logrus.Errorf("%s logging specified but not supported. Choosing k8s-file logging instead", ctr.LogDriver()) fallthrough diff --git a/libpod/options.go b/libpod/options.go index 4e597201a..98eb45e76 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1634,6 +1634,19 @@ func WithVolumeNoChown() VolumeCreateOption { } } +// WithVolumeDisableQuota prevents the volume from being assigned a quota. +func WithVolumeDisableQuota() VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.config.DisableQuota = true + + return nil + } +} + // withSetAnon sets a bool notifying libpod that this volume is anonymous and // should be removed when containers using it are removed and volumes are // specified for removal. @@ -1662,7 +1675,7 @@ func WithTimezone(path string) CtrCreateOption { if err != nil { return err } - //We don't want to mount a timezone directory + // We don't want to mount a timezone directory if file.IsDir() { return errors.New("Invalid timezone: is a directory") } diff --git a/libpod/pod.go b/libpod/pod.go index ed2d97b37..237c42901 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -1,7 +1,6 @@ package libpod import ( - "context" "fmt" "sort" "strings" @@ -159,6 +158,15 @@ func (p *Pod) CPUQuota() int64 { return 0 } +// NetworkMode returns the Network mode given by the user ex: pod, private... +func (p *Pod) NetworkMode() string { + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return "" + } + return infra.NetworkMode() +} + // PidMode returns the PID mode given by the user ex: pod, private... func (p *Pod) PidMode() string { infra, err := p.runtime.GetContainer(p.state.InfraContainerID) @@ -296,35 +304,9 @@ func (p *Pod) CgroupPath() (string, error) { if err := p.updatePod(); err != nil { return "", err } - if p.state.CgroupPath != "" { - return p.state.CgroupPath, nil - } if p.state.InfraContainerID == "" { return "", errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") } - - id, err := p.infraContainerID() - if err != nil { - return "", err - } - - if id != "" { - ctr, err := p.infraContainer() - if err != nil { - return "", errors.Wrapf(err, "could not get infra") - } - if ctr != nil { - ctr.Start(context.Background(), true) - cgroupPath, err := ctr.CgroupPath() - fmt.Println(cgroupPath) - if err != nil { - return "", errors.Wrapf(err, "could not get container cgroup") - } - p.state.CgroupPath = cgroupPath - p.save() - return cgroupPath, nil - } - } return p.state.CgroupPath, nil } diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 48049798b..ba30d878e 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -593,7 +593,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { return nil, err } infraConfig = new(define.InspectPodInfraConfig) - infraConfig.HostNetwork = !infra.config.ContainerNetworkConfig.UseImageHosts + infraConfig.HostNetwork = p.NetworkMode() == "host" infraConfig.StaticIP = infra.config.ContainerNetworkConfig.StaticIP infraConfig.NoManageResolvConf = infra.config.UseImageResolvConf infraConfig.NoManageHosts = infra.config.UseImageHosts diff --git a/libpod/runtime.go b/libpod/runtime.go index 877e151a9..6c2323d88 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -550,6 +550,10 @@ func makeRuntime(runtime *Runtime) (retErr error) { // Check if the pause process was created. If it was created, then // move it to its own systemd scope. utils.MovePauseProcessToScope(pausePid) + + // gocritic complains because defer is not run on os.Exit() + // However this is fine because the lock is released anyway when the process exits + //nolint:gocritic os.Exit(ret) } } diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index fd3ffd199..df7174ac6 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -513,7 +513,9 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai case define.NoLogging, define.PassthroughLogging: break case define.JournaldLogging: - ctr.initializeJournal(ctx) + if err := ctr.initializeJournal(ctx); err != nil { + return nil, fmt.Errorf("failed to initialize journal: %w", err) + } default: if ctr.config.LogPath == "" { ctr.config.LogPath = filepath.Join(ctr.config.StaticDir, "ctr.log") diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 2bbccfdf6..62ec7df60 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -199,10 +199,15 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, // Go through and lock all containers so we can operate on them all at // once. // First loop also checks that we are ready to go ahead and remove. + containersLocked := true for _, ctr := range ctrs { ctrLock := ctr.lock ctrLock.Lock() - defer ctrLock.Unlock() + defer func() { + if containersLocked { + ctrLock.Unlock() + } + }() // If we're force-removing, no need to check status. if force { @@ -304,6 +309,12 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, } } + // let's unlock the containers so if there is any cleanup process, it can terminate its execution + for _, ctr := range ctrs { + ctr.lock.Unlock() + } + containersLocked = false + // Remove pod cgroup, if present if p.state.CgroupPath != "" { logrus.Debugf("Removing pod cgroup %s", p.state.CgroupPath) @@ -332,7 +343,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, } } if err == nil { - if err := conmonCgroup.Delete(); err != nil { + if err = conmonCgroup.Delete(); err != nil { if removalErr == nil { removalErr = errors.Wrapf(err, "error removing pod %s conmon cgroup", p.ID()) } else { diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index 241f6e2f2..f8788e183 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -73,7 +73,7 @@ func (r *Runtime) newVolume(options ...VolumeCreateOption) (_ *Volume, deferredE return nil, errors.Wrapf(err, "invalid volume option %s for driver 'local'", key) } } - case "o", "type", "uid", "gid", "size", "inodes": + case "o", "type", "uid", "gid", "size", "inodes", "noquota": // Do nothing, valid keys default: return nil, errors.Wrapf(define.ErrInvalidArg, "invalid mount option %s for driver 'local'", key) @@ -111,23 +111,28 @@ func (r *Runtime) newVolume(options ...VolumeCreateOption) (_ *Volume, deferredE if err := LabelVolumePath(fullVolPath); err != nil { return nil, err } - projectQuotaSupported := false - - q, err := quota.NewControl(r.config.Engine.VolumePath) - if err == nil { - projectQuotaSupported = true - } - quota := quota.Quota{} - if volume.config.Size > 0 || volume.config.Inodes > 0 { - if !projectQuotaSupported { - return nil, errors.New("Volume options size and inodes not supported. Filesystem does not support Project Quota") + if volume.config.DisableQuota { + if volume.config.Size > 0 || volume.config.Inodes > 0 { + return nil, errors.New("volume options size and inodes cannot be used without quota") } - quota.Size = volume.config.Size - quota.Inodes = volume.config.Inodes - } - if projectQuotaSupported { - if err := q.SetQuota(fullVolPath, quota); err != nil { - return nil, errors.Wrapf(err, "failed to set size quota size=%d inodes=%d for volume directory %q", volume.config.Size, volume.config.Inodes, fullVolPath) + } else { + projectQuotaSupported := false + q, err := quota.NewControl(r.config.Engine.VolumePath) + if err == nil { + projectQuotaSupported = true + } + quota := quota.Quota{} + if volume.config.Size > 0 || volume.config.Inodes > 0 { + if !projectQuotaSupported { + return nil, errors.New("volume options size and inodes not supported. Filesystem does not support Project Quota") + } + quota.Size = volume.config.Size + quota.Inodes = volume.config.Inodes + } + if projectQuotaSupported { + if err := q.SetQuota(fullVolPath, quota); err != nil { + return nil, errors.Wrapf(err, "failed to set size quota size=%d inodes=%d for volume directory %q", volume.config.Size, volume.config.Inodes, fullVolPath) + } } } diff --git a/libpod/util.go b/libpod/util.go index 51fe60427..1753b4f34 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -55,8 +55,11 @@ func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, e if err := watcher.Add(filepath.Dir(path)); err == nil { inotifyEvents = watcher.Events } - defer watcher.Close() - defer watcher.Remove(filepath.Dir(path)) + defer func() { + if err := watcher.Close(); err != nil { + logrus.Errorf("Failed to close fsnotify watcher: %v", err) + } + }() } var timeoutChan <-chan time.Time diff --git a/libpod/volume.go b/libpod/volume.go index bffafdc15..ab461a37f 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -52,6 +52,9 @@ type VolumeConfig struct { Size uint64 `json:"size"` // Inodes maximum of the volume. Inodes uint64 `json:"inodes"` + // DisableQuota indicates that the volume should completely disable using any + // quota tracking. + DisableQuota bool `json:"disableQuota,omitempty"` } // VolumeState holds the volume's mutable state. diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 9850c2ea1..e0ebb729d 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -52,6 +52,9 @@ func (v *Volume) needsMount() bool { if _, ok := v.config.Options["SIZE"]; ok { index++ } + if _, ok := v.config.Options["NOQUOTA"]; ok { + index++ + } // when uid or gid is set there is also the "o" option // set so we have to ignore this one as well if index > 0 { |