mirror of
https://github.com/containers/podman.git
synced 2026-03-28 19:43:40 -04:00
When we are activated by systemd the code assumed that we had a valid URL which was not the case so it failed to parse the URL which causes the info call to fail all the time. This fixes two problems first add the schema to the systemd activated listener URL so it can be parsed correctly but second simply do not parse it as url as all we care about in the info call is if it is unix and the file path exists. Fixes #24152 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
355 lines
9.8 KiB
Go
355 lines
9.8 KiB
Go
//go:build !remote
|
|
|
|
package abi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/containers/podman/v5/libpod/define"
|
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
|
"github.com/containers/podman/v5/pkg/domain/entities/reports"
|
|
"github.com/containers/podman/v5/pkg/util"
|
|
"github.com/containers/storage"
|
|
"github.com/containers/storage/pkg/directory"
|
|
"github.com/containers/storage/pkg/fileutils"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
|
|
info, err := ic.Libpod.Info()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info.Host.RemoteSocket = &define.RemoteSocket{Path: ic.Libpod.RemoteURI()}
|
|
|
|
// `podman system connection add` invokes podman via ssh to fill in connection string. Here
|
|
// we are reporting the default systemd activation socket path as we cannot know if a future
|
|
// service may be run with another URI.
|
|
if ic.Libpod.RemoteURI() == "" {
|
|
xdg := defaultRunPath
|
|
if path, err := util.GetRootlessRuntimeDir(); err != nil {
|
|
// Info is as good as we can guess...
|
|
return info, err
|
|
} else if path != "" {
|
|
xdg = path
|
|
}
|
|
|
|
uri := url.URL{
|
|
Scheme: "unix",
|
|
Path: filepath.Join(xdg, "podman", "podman.sock"),
|
|
}
|
|
ic.Libpod.SetRemoteURI(uri.String())
|
|
info.Host.RemoteSocket.Path = uri.Path
|
|
}
|
|
|
|
// check if the unix path exits, if not unix socket we always we assume it exists, i.e. tcp socket
|
|
path, found := strings.CutPrefix(info.Host.RemoteSocket.Path, "unix://")
|
|
if found {
|
|
err := fileutils.Exists(path)
|
|
info.Host.RemoteSocket.Exists = err == nil
|
|
} else {
|
|
info.Host.RemoteSocket.Exists = true
|
|
}
|
|
|
|
return info, nil
|
|
}
|
|
|
|
// SystemPrune removes unused data from the system. Pruning pods, containers, networks, volumes and images.
|
|
func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
|
|
var systemPruneReport = new(entities.SystemPruneReport)
|
|
|
|
if options.External {
|
|
if options.All || options.Volume || len(options.Filters) > 0 {
|
|
return nil, fmt.Errorf("system prune --external cannot be combined with other options")
|
|
}
|
|
err := ic.Libpod.GarbageCollect()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return systemPruneReport, nil
|
|
}
|
|
|
|
filters := []string{}
|
|
for k, v := range options.Filters {
|
|
filters = append(filters, fmt.Sprintf("%s=%s", k, v[0]))
|
|
}
|
|
reclaimedSpace := (uint64)(0)
|
|
found := true
|
|
for found {
|
|
found = false
|
|
|
|
// TODO: Figure out cleaner way to handle all of the different PruneOptions
|
|
// Remove all unused pods.
|
|
podPruneReports, err := ic.prunePodHelper(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(podPruneReports) > 0 {
|
|
found = true
|
|
}
|
|
|
|
systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReports...)
|
|
|
|
// Remove all unused containers.
|
|
containerPruneOptions := entities.ContainerPruneOptions{}
|
|
containerPruneOptions.Filters = (url.Values)(options.Filters)
|
|
|
|
containerPruneReports, err := ic.ContainerPrune(ctx, containerPruneOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
reclaimedSpace += reports.PruneReportsSize(containerPruneReports)
|
|
systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...)
|
|
|
|
// Remove all unused images.
|
|
imagePruneOptions := entities.ImagePruneOptions{
|
|
All: options.All,
|
|
Filter: filters,
|
|
}
|
|
|
|
imageEngine := ImageEngine{Libpod: ic.Libpod}
|
|
imagePruneReports, err := imageEngine.Prune(ctx, imagePruneOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(imagePruneReports) > 0 {
|
|
found = true
|
|
}
|
|
|
|
reclaimedSpace += reports.PruneReportsSize(imagePruneReports)
|
|
systemPruneReport.ImagePruneReports = append(systemPruneReport.ImagePruneReports, imagePruneReports...)
|
|
|
|
// Remove all unused networks.
|
|
networkPruneOptions := entities.NetworkPruneOptions{}
|
|
networkPruneOptions.Filters = options.Filters
|
|
|
|
networkPruneReports, err := ic.NetworkPrune(ctx, networkPruneOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(networkPruneReports) > 0 {
|
|
found = true
|
|
}
|
|
|
|
// Networks reclaimedSpace are always '0'.
|
|
systemPruneReport.NetworkPruneReports = append(systemPruneReport.NetworkPruneReports, networkPruneReports...)
|
|
|
|
// Remove unused volume data.
|
|
if options.Volume {
|
|
volumePruneOptions := entities.VolumePruneOptions{}
|
|
volumePruneOptions.Filters = (url.Values)(options.Filters)
|
|
|
|
volumePruneReports, err := ic.VolumePrune(ctx, volumePruneOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(volumePruneReports) > 0 {
|
|
found = true
|
|
}
|
|
|
|
reclaimedSpace += reports.PruneReportsSize(volumePruneReports)
|
|
systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReports...)
|
|
}
|
|
}
|
|
|
|
systemPruneReport.ReclaimedSpace = reclaimedSpace
|
|
return systemPruneReport, nil
|
|
}
|
|
|
|
func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) {
|
|
var (
|
|
dfImages = []*entities.SystemDfImageReport{}
|
|
)
|
|
|
|
imageStats, totalImageSize, err := ic.Libpod.LibimageRuntime().DiskUsage(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, stat := range imageStats {
|
|
report := entities.SystemDfImageReport{
|
|
Repository: stat.Repository,
|
|
Tag: stat.Tag,
|
|
ImageID: stat.ID,
|
|
Created: stat.Created,
|
|
Size: stat.Size,
|
|
SharedSize: stat.SharedSize,
|
|
UniqueSize: stat.UniqueSize,
|
|
Containers: stat.Containers,
|
|
}
|
|
dfImages = append(dfImages, &report)
|
|
}
|
|
|
|
// Get containers and iterate over them
|
|
cons, err := ic.Libpod.GetAllContainers()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dfContainers := make([]*entities.SystemDfContainerReport, 0, len(cons))
|
|
for _, c := range cons {
|
|
iid, _ := c.Image()
|
|
state, err := c.State()
|
|
if err != nil {
|
|
if errors.Is(err, define.ErrNoSuchCtr) {
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("failed to get state of container %s: %w", c.ID(), err)
|
|
}
|
|
conSize, err := c.RootFsSize()
|
|
if err != nil {
|
|
if errors.Is(err, storage.ErrContainerUnknown) || errors.Is(err, define.ErrNoSuchCtr) {
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("failed to get root file system size of container %s: %w", c.ID(), err)
|
|
}
|
|
rwsize, err := c.RWSize()
|
|
if err != nil {
|
|
if errors.Is(err, storage.ErrContainerUnknown) || errors.Is(err, define.ErrNoSuchCtr) {
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("failed to get read/write size of container %s: %w", c.ID(), err)
|
|
}
|
|
report := entities.SystemDfContainerReport{
|
|
ContainerID: c.ID(),
|
|
Image: iid,
|
|
Command: c.Command(),
|
|
LocalVolumes: len(c.UserVolumes()),
|
|
RWSize: rwsize,
|
|
Size: conSize,
|
|
Created: c.CreatedTime(),
|
|
Status: state.String(),
|
|
Names: c.Name(),
|
|
}
|
|
dfContainers = append(dfContainers, &report)
|
|
}
|
|
|
|
// Get volumes and iterate over them
|
|
vols, err := ic.Libpod.GetAllVolumes()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dfVolumes := make([]*entities.SystemDfVolumeReport, 0, len(vols))
|
|
for _, v := range vols {
|
|
var reclaimableSize int64
|
|
mountPoint, err := v.MountPoint()
|
|
if err != nil {
|
|
if errors.Is(err, define.ErrNoSuchVolume) {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if mountPoint == "" {
|
|
// We can't get any info on this volume, as it's not
|
|
// mounted.
|
|
// TODO: fix this.
|
|
continue
|
|
}
|
|
volSize, err := directory.Size(mountPoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
inUse, err := v.VolumeInUse()
|
|
if err != nil {
|
|
if errors.Is(err, define.ErrNoSuchVolume) {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
if len(inUse) == 0 {
|
|
reclaimableSize = volSize
|
|
}
|
|
report := entities.SystemDfVolumeReport{
|
|
VolumeName: v.Name(),
|
|
Links: len(inUse),
|
|
Size: volSize,
|
|
ReclaimableSize: reclaimableSize,
|
|
}
|
|
dfVolumes = append(dfVolumes, &report)
|
|
}
|
|
|
|
return &entities.SystemDfReport{
|
|
ImagesSize: totalImageSize,
|
|
Images: dfImages,
|
|
Containers: dfContainers,
|
|
Volumes: dfVolumes,
|
|
}, nil
|
|
}
|
|
|
|
func (ic *ContainerEngine) Reset(ctx context.Context) error {
|
|
return ic.Libpod.Reset(ctx)
|
|
}
|
|
|
|
func (ic *ContainerEngine) Renumber(ctx context.Context) error {
|
|
return ic.Libpod.RenumberLocks()
|
|
}
|
|
|
|
func (ic *ContainerEngine) Migrate(ctx context.Context, options entities.SystemMigrateOptions) error {
|
|
return ic.Libpod.Migrate(options.NewRuntime)
|
|
}
|
|
|
|
func (se SystemEngine) Shutdown(ctx context.Context) {
|
|
if err := se.Libpod.Shutdown(false); err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
}
|
|
|
|
func unshareEnv(graphroot, runroot string) []string {
|
|
return append(os.Environ(), "_CONTAINERS_USERNS_CONFIGURED=done",
|
|
fmt.Sprintf("CONTAINERS_GRAPHROOT=%s", graphroot),
|
|
fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot))
|
|
}
|
|
|
|
func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
|
|
unshare := func() error {
|
|
cmd := exec.Command(args[0], args[1:]...)
|
|
cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
if options.RootlessNetNS {
|
|
return ic.Libpod.Network().RunInRootlessNetns(unshare)
|
|
}
|
|
return unshare()
|
|
}
|
|
|
|
func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) {
|
|
var report entities.SystemVersionReport
|
|
v, err := define.GetVersion()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
report.Client = &v
|
|
return &report, err
|
|
}
|
|
|
|
func (ic ContainerEngine) Locks(ctx context.Context) (*entities.LocksReport, error) {
|
|
var report entities.LocksReport
|
|
conflicts, held, err := ic.Libpod.LockConflicts()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
report.LockConflicts = conflicts
|
|
report.LocksHeld = held
|
|
return &report, nil
|
|
}
|
|
|
|
func (ic ContainerEngine) SystemCheck(ctx context.Context, options entities.SystemCheckOptions) (*entities.SystemCheckReport, error) {
|
|
report, err := ic.Libpod.SystemCheck(ctx, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &report, nil
|
|
}
|