aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go3
-rw-r--r--libpod/container_api.go9
-rw-r--r--libpod/container_config.go5
-rw-r--r--libpod/container_copy_linux.go13
-rw-r--r--libpod/container_exec.go2
-rw-r--r--libpod/container_internal_linux.go7
-rw-r--r--libpod/define/info.go12
-rw-r--r--libpod/define/pod_inspect.go1
-rw-r--r--libpod/healthcheck_linux.go70
-rw-r--r--libpod/info.go63
-rw-r--r--libpod/networking_linux.go34
-rw-r--r--libpod/plugin/volume_api.go7
-rw-r--r--libpod/pod_api.go9
-rw-r--r--libpod/runtime.go3
14 files changed, 130 insertions, 108 deletions
diff --git a/libpod/container.go b/libpod/container.go
index 64b4453fb..04a4ae64a 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -1331,8 +1331,7 @@ func (c *Container) getNetworkStatus() map[string]types.StatusBlock {
}
c.state.NetworkStatus = result
_ = c.save()
- // TODO remove debug for final version
- logrus.Debugf("converted old network result to new result %v", result)
+
return result
}
return nil
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 930b3e17b..b064d3528 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -213,9 +213,8 @@ func (c *Container) Kill(signal uint) error {
}
}
- // TODO: Is killing a paused container OK?
switch c.state.State {
- case define.ContainerStateRunning, define.ContainerStateStopping:
+ case define.ContainerStateRunning, define.ContainerStateStopping, define.ContainerStatePaused:
// Note that killing containers in "stopping" state is okay.
// In that state, the Podman is waiting for the runtime to
// stop the container and if that is taking too long, a user
@@ -468,7 +467,7 @@ func (c *Container) AddArtifact(name string, data []byte) error {
return define.ErrCtrRemoved
}
- return ioutil.WriteFile(c.getArtifactPath(name), data, 0740)
+ return ioutil.WriteFile(c.getArtifactPath(name), data, 0o740)
}
// GetArtifact reads the specified artifact file from the container
@@ -899,7 +898,7 @@ func (c *Container) ShouldRestart(ctx context.Context) bool {
// CopyFromArchive copies the contents from the specified tarStream to path
// *inside* the container.
-func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, chown bool, rename map[string]string, tarStream io.Reader) (func() error, error) {
+func (c *Container) CopyFromArchive(_ context.Context, containerPath string, chown, noOverwriteDirNonDir bool, rename map[string]string, tarStream io.Reader) (func() error, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -909,7 +908,7 @@ func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, c
}
}
- return c.copyFromArchive(containerPath, chown, rename, tarStream)
+ return c.copyFromArchive(containerPath, chown, noOverwriteDirNonDir, rename, tarStream)
}
// CopyToArchive copies the contents from the specified path *inside* the
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 3e85ad4d5..30b84adcf 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -243,12 +243,12 @@ type ContainerNetworkConfig struct {
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned an IP by CNI.
// Deprecated: Do no use this anymore, this is only for DB backwards compat.
- StaticIP net.IP `json:"staticIP"`
+ StaticIP net.IP `json:"staticIP,omitempty"`
// StaticMAC is a static MAC to request for the container.
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned a MAC by CNI.
// Deprecated: Do no use this anymore, this is only for DB backwards compat.
- StaticMAC types.HardwareAddr `json:"staticMAC"`
+ StaticMAC types.HardwareAddr `json:"staticMAC,omitempty"`
// PortMappings are the ports forwarded to the container's network
// namespace
// These are not used unless CreateNetNS is true
@@ -372,7 +372,6 @@ type ContainerMiscConfig struct {
// restart the container. Used only if RestartPolicy is set to
// "on-failure".
RestartRetries uint `json:"restart_retries,omitempty"`
- // TODO log options for log drivers
// PostConfigureNetNS needed when a user namespace is created by an OCI runtime
// if the network namespace is created before the user namespace it will be
// owned by the wrong user namespace.
diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go
index 7566fbb12..9528cd06b 100644
--- a/libpod/container_copy_linux.go
+++ b/libpod/container_copy_linux.go
@@ -23,7 +23,7 @@ import (
"golang.org/x/sys/unix"
)
-func (c *Container) copyFromArchive(path string, chown bool, rename map[string]string, reader io.Reader) (func() error, error) {
+func (c *Container) copyFromArchive(path string, chown, noOverwriteDirNonDir bool, rename map[string]string, reader io.Reader) (func() error, error) {
var (
mountPoint string
resolvedRoot string
@@ -89,11 +89,12 @@ func (c *Container) copyFromArchive(path string, chown bool, rename map[string]s
defer unmount()
defer decompressed.Close()
putOptions := buildahCopiah.PutOptions{
- UIDMap: c.config.IDMappings.UIDMap,
- GIDMap: c.config.IDMappings.GIDMap,
- ChownDirs: idPair,
- ChownFiles: idPair,
- Rename: rename,
+ UIDMap: c.config.IDMappings.UIDMap,
+ GIDMap: c.config.IDMappings.GIDMap,
+ ChownDirs: idPair,
+ ChownFiles: idPair,
+ NoOverwriteDirNonDir: noOverwriteDirNonDir,
+ Rename: rename,
}
return c.joinMountAndExec(
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index c05e7fd94..1e8fce4da 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -279,8 +279,6 @@ func (c *Container) ExecStart(sessionID string) error {
// ExecStartAndAttach starts and attaches to an exec session in a container.
// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty
-// TODO: Should we include detach keys in the signature to allow override?
-// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *define.TerminalSize) error {
if !c.batched {
c.lock.Lock()
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 298eb1947..e19d75deb 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -1091,7 +1091,6 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
g.AddProcessEnv("HOSTNAME", hostname)
}
- // TODO need unlocked version of this for use in pods
nsPath, err := nsCtr.NamespacePath(ns)
if err != nil {
return err
@@ -3230,10 +3229,8 @@ func (c *Container) fixVolumePermissions(v *ContainerNamedVolume) error {
return err
}
- // TODO: For now, I've disabled chowning volumes owned by non-Podman
- // drivers. This may be safe, but it's really going to be a case-by-case
- // thing, I think - safest to leave disabled now and re-enable later if
- // there is a demand.
+ // Volumes owned by a volume driver are not chowned - we don't want to
+ // mess with a mount not managed by us.
if vol.state.NeedsChown && !vol.UsesVolumeDriver() {
vol.state.NeedsChown = false
diff --git a/libpod/define/info.go b/libpod/define/info.go
index 911fa5c03..c716bec7b 100644
--- a/libpod/define/info.go
+++ b/libpod/define/info.go
@@ -14,7 +14,7 @@ type Info struct {
Version Version `json:"version"`
}
-// HostInfo describes the libpod host
+// SecurityInfo describes the libpod host
type SecurityInfo struct {
AppArmorEnabled bool `json:"apparmorEnabled"`
DefaultCapabilities string `json:"capabilities"`
@@ -64,8 +64,7 @@ type RemoteSocket struct {
Exists bool `json:"exists,omitempty"`
}
-// SlirpInfo describes the slirp executable that
-// is being being used.
+// SlirpInfo describes the slirp executable that is being used
type SlirpInfo struct {
Executable string `json:"executable"`
Package string `json:"package"`
@@ -78,8 +77,7 @@ type IDMappings struct {
UIDMap []idtools.IDMap `json:"uidmap"`
}
-// DistributionInfo describes the host distribution
-// for libpod
+// DistributionInfo describes the host distribution for libpod
type DistributionInfo struct {
Distribution string `json:"distribution"`
Variant string `json:"variant,omitempty"`
@@ -141,8 +139,8 @@ type Plugins struct {
Volume []string `json:"volume"`
Network []string `json:"network"`
Log []string `json:"log"`
- // FIXME what should we do with Authorization, docker seems to return nothing by default
- // Authorization []string `json:"authorization"`
+ // Authorization is provided for compatibility, will always be nil as Podman has no daemon
+ Authorization []string `json:"authorization"`
}
type CPUUsage struct {
diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go
index 219ffade2..c387856e5 100644
--- a/libpod/define/pod_inspect.go
+++ b/libpod/define/pod_inspect.go
@@ -82,6 +82,7 @@ type InspectPodInfraConfig struct {
HostNetwork bool
// StaticIP is a static IPv4 that will be assigned to the infra
// container and then used by the pod.
+ // swagger:strfmt ipv4
StaticIP net.IP
// StaticMAC is a static MAC address that will be assigned to the infra
// container and then used by the pod.
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 45b3a0e41..1e03db542 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -7,6 +7,7 @@ import (
"os/exec"
"strings"
+ "github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/systemd"
"github.com/pkg/errors"
@@ -46,6 +47,17 @@ func (c *Container) createTimer() error {
return nil
}
+// Wait for a message on the channel. Throw an error if the message is not "done".
+func systemdOpSuccessful(c chan string) error {
+ msg := <-c
+ switch msg {
+ case "done":
+ return nil
+ default:
+ return fmt.Errorf("expected %q but received %q", "done", msg)
+ }
+}
+
// startTimer starts a systemd timer for the healthchecks
func (c *Container) startTimer() error {
if c.disableHealthCheckSystemd() {
@@ -56,8 +68,17 @@ func (c *Container) startTimer() error {
return errors.Wrapf(err, "unable to get systemd connection to start healthchecks")
}
defer conn.Close()
- _, err = conn.StartUnitContext(context.Background(), fmt.Sprintf("%s.service", c.ID()), "fail", nil)
- return err
+
+ startFile := fmt.Sprintf("%s.service", c.ID())
+ startChan := make(chan string)
+ if _, err := conn.StartUnitContext(context.Background(), startFile, "fail", startChan); err != nil {
+ return err
+ }
+ if err := systemdOpSuccessful(startChan); err != nil {
+ return fmt.Errorf("starting systemd health-check timer %q: %w", startFile, err)
+ }
+
+ return nil
}
// removeTransientFiles removes the systemd timer and unit files
@@ -71,30 +92,37 @@ func (c *Container) removeTransientFiles(ctx context.Context) error {
return errors.Wrapf(err, "unable to get systemd connection to remove healthchecks")
}
defer conn.Close()
+
+ // Errors are returned at the very end. Let's make sure to stop and
+ // clean up as much as possible.
+ stopErrors := []error{}
+
+ // Stop the timer before the service to make sure the timer does not
+ // fire after the service is stopped.
+ timerChan := make(chan string)
timerFile := fmt.Sprintf("%s.timer", c.ID())
- serviceFile := fmt.Sprintf("%s.service", c.ID())
+ if _, err := conn.StopUnitContext(ctx, timerFile, "fail", timerChan); err != nil {
+ if !strings.HasSuffix(err.Error(), ".timer not loaded.") {
+ stopErrors = append(stopErrors, fmt.Errorf("removing health-check timer %q: %w", timerFile, err))
+ }
+ } else if err := systemdOpSuccessful(timerChan); err != nil {
+ stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check timer %q: %w", timerFile, err))
+ }
- // If the service has failed (the healthcheck has failed), then
- // the .service file is not removed on stopping the unit file. If
- // we check the properties of the service, it will automatically
- // reset the state. But checking the state takes msecs vs usecs to
- // blindly call reset.
+ // Reset the service before stopping it to make sure it's being removed
+ // on stop.
+ serviceChan := make(chan string)
+ serviceFile := fmt.Sprintf("%s.service", c.ID())
if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil {
- logrus.Debugf("failed to reset unit file: %q", err)
+ logrus.Debugf("Failed to reset unit file: %q", err)
}
-
- // We want to ignore errors where the timer unit and/or service unit has already
- // been removed. The error return is generic so we have to check against the
- // string in the error
- if _, err = conn.StopUnitContext(ctx, serviceFile, "fail", nil); err != nil {
+ if _, err := conn.StopUnitContext(ctx, serviceFile, "fail", serviceChan); err != nil {
if !strings.HasSuffix(err.Error(), ".service not loaded.") {
- return errors.Wrapf(err, "unable to remove service file")
- }
- }
- if _, err = conn.StopUnitContext(ctx, timerFile, "fail", nil); err != nil {
- if strings.HasSuffix(err.Error(), ".timer not loaded.") {
- return nil
+ stopErrors = append(stopErrors, fmt.Errorf("removing health-check service %q: %w", serviceFile, err))
}
+ } else if err := systemdOpSuccessful(serviceChan); err != nil {
+ stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check service %q: %w", serviceFile, err))
}
- return err
+
+ return errorhandling.JoinErrors(stopErrors)
}
diff --git a/libpod/info.go b/libpod/info.go
index bc49a6cc9..561d11524 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -199,50 +199,38 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
info.OCIRuntime = ociruntimeInfo
}
- up, err := readUptime()
+ duration, err := procUptime()
if err != nil {
return nil, errors.Wrapf(err, "error reading up time")
}
- // Convert uptime in seconds to a human-readable format
- upSeconds := up + "s"
- upDuration, err := time.ParseDuration(upSeconds)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing system uptime")
- }
-
- // TODO Isn't there a simple lib for this, something like humantime?
- hoursFound := false
- var timeBuffer bytes.Buffer
- var hoursBuffer bytes.Buffer
- for _, elem := range upDuration.String() {
- timeBuffer.WriteRune(elem)
- if elem == 'h' || elem == 'm' {
- timeBuffer.WriteRune(' ')
- if elem == 'h' {
- hoursFound = true
- }
- }
- if !hoursFound {
- hoursBuffer.WriteRune(elem)
- }
+
+ uptime := struct {
+ hours float64
+ minutes float64
+ seconds float64
+ }{
+ hours: duration.Truncate(time.Hour).Hours(),
+ minutes: duration.Truncate(time.Minute).Minutes(),
+ seconds: duration.Truncate(time.Second).Seconds(),
}
- info.Uptime = timeBuffer.String()
- if hoursFound {
- hours, err := strconv.ParseFloat(hoursBuffer.String(), 64)
- if err == nil {
- days := hours / 24
- info.Uptime = fmt.Sprintf("%s (Approximately %.2f days)", info.Uptime, days)
- }
+ // Could not find a humanize-formatter for time.Duration
+ var buffer bytes.Buffer
+ buffer.WriteString(fmt.Sprintf("%.0fh %.0fm %.2fs",
+ uptime.hours,
+ math.Mod(uptime.seconds, 3600)/60,
+ math.Mod(uptime.seconds, 60),
+ ))
+ if int64(uptime.hours) > 0 {
+ buffer.WriteString(fmt.Sprintf(" (Approximately %.2f days)", uptime.hours/24))
}
+ info.Uptime = buffer.String()
return &info, nil
}
func (r *Runtime) getContainerStoreInfo() (define.ContainerStore, error) {
- var (
- paused, running, stopped int
- )
+ var paused, running, stopped int
cs := define.ContainerStore{}
cons, err := r.GetAllContainers()
if err != nil {
@@ -353,16 +341,17 @@ func readKernelVersion() (string, error) {
return string(f[2]), nil
}
-func readUptime() (string, error) {
+func procUptime() (time.Duration, error) {
+ var zero time.Duration
buf, err := ioutil.ReadFile("/proc/uptime")
if err != nil {
- return "", err
+ return zero, err
}
f := bytes.Fields(buf)
if len(f) < 1 {
- return "", fmt.Errorf("invalid uptime")
+ return zero, errors.New("unable to parse uptime from /proc/uptime")
}
- return string(f[0]), nil
+ return time.ParseDuration(string(f[0]) + "s")
}
// GetHostDistributionInfo returns a map containing the host's distribution and version
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 73e64530e..37fa9b5f5 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -930,6 +930,8 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu
return r.configureNetNS(ctr, ctr.state.NetNS)
}
+// TODO (5.0): return the statistics per network interface
+// This would allow better compat with docker.
func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
var netStats *netlink.LinkStatistics
@@ -943,21 +945,39 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
return nil, nil
}
- // FIXME get the interface from the container netstatus
- dev := "eth0"
netMode := ctr.config.NetMode
+ netStatus := ctr.getNetworkStatus()
if otherCtr != nil {
netMode = otherCtr.config.NetMode
+ netStatus = otherCtr.getNetworkStatus()
}
if netMode.IsSlirp4netns() {
- dev = "tap0"
+ // create a fake status with correct interface name for the logic below
+ netStatus = map[string]types.StatusBlock{
+ "slirp4netns": {
+ Interfaces: map[string]types.NetInterface{"tap0": {}},
+ },
+ }
}
err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error {
- link, err := netlink.LinkByName(dev)
- if err != nil {
- return err
+ for _, status := range netStatus {
+ for dev := range status.Interfaces {
+ link, err := netlink.LinkByName(dev)
+ if err != nil {
+ return err
+ }
+ if netStats == nil {
+ netStats = link.Attrs().Statistics
+ continue
+ }
+ // Currently only Tx/RxBytes are used.
+ // In the future we should return all stats per interface so that
+ // api users have a better options.
+ stats := link.Attrs().Statistics
+ netStats.TxBytes += stats.TxBytes
+ netStats.RxBytes += stats.RxBytes
+ }
}
- netStats = link.Attrs().Statistics
return nil
})
return netStats, err
diff --git a/libpod/plugin/volume_api.go b/libpod/plugin/volume_api.go
index a6d66a034..2818e70c1 100644
--- a/libpod/plugin/volume_api.go
+++ b/libpod/plugin/volume_api.go
@@ -22,9 +22,6 @@ import (
var json = jsoniter.ConfigCompatibleWithStandardLibrary
-// TODO: We should add syntax for specifying plugins to containers.conf, and
-// support for loading based on that.
-
// Copied from docker/go-plugins-helpers/volume/api.go - not exported, so we
// need to do this to get at them.
// These are well-established paths that should not change unless the plugin API
@@ -185,8 +182,7 @@ func (p *VolumePlugin) getURI() string {
}
// Verify the plugin is still available.
-// TODO: Do we want to ping with an HTTP request? There's no ping endpoint so
-// we'd need to hit Activate or Capabilities?
+// Does not actually ping the API, just verifies that the socket still exists.
func (p *VolumePlugin) verifyReachable() error {
if _, err := os.Stat(p.SocketPath); err != nil {
if os.IsNotExist(err) {
@@ -307,7 +303,6 @@ func (p *VolumePlugin) ListVolumes() ([]*volume.Volume, error) {
return nil, err
}
- // TODO: Can probably unify response reading under a helper
volumeRespBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrapf(err, "error reading response body from volume plugin %s", p.Name)
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index eede896a9..1c1e15984 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -152,8 +152,8 @@ func (p *Pod) stopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m
return nil, err
}
- // TODO: There may be cases where it makes sense to order stops based on
- // dependencies. Should we bother with this?
+ // Stopping pods is not ordered by dependency. We haven't seen any case
+ // where this would actually matter.
ctrErrChan := make(map[string]<-chan error)
@@ -162,8 +162,9 @@ func (p *Pod) stopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m
c := ctr
logrus.Debugf("Adding parallel job to stop container %s", c.ID())
retChan := parallel.Enqueue(ctx, func() error {
- // TODO: Might be better to batch stop and cleanup
- // together?
+ // Can't batch these without forcing Stop() to hold the
+ // lock for the full duration of the timeout.
+ // We probably don't want to do that.
if timeout > -1 {
if err := c.StopWithTimeout(uint(timeout)); err != nil {
return err
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 00fa2fe88..e268c2d17 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -412,7 +412,6 @@ func makeRuntime(runtime *Runtime) (retErr error) {
return err
}
runtime.eventer = eventer
- // TODO: events for libimage
// Set up containers/image
if runtime.imageContext == nil {
@@ -517,8 +516,6 @@ func makeRuntime(runtime *Runtime) (retErr error) {
}
// Acquire the lock and hold it until we return
// This ensures that no two processes will be in runtime.refresh at once
- // TODO: we can't close the FD in this lock, so we should keep it around
- // and use it to lock important operations
aliveLock.Lock()
doRefresh := false
defer func() {