aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_api.go6
-rw-r--r--libpod/container_config.go4
-rw-r--r--libpod/container_copy_linux.go13
-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
8 files changed, 120 insertions, 83 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 0fab36bdc..d87deb71a 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -446,7 +446,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
@@ -877,7 +877,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()
@@ -887,7 +887,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 ae3bc5865..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
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/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